• canvas实现"雷达扫描"效果


    今天来讲解“雷达扫描”效果demo,来源于QQ群里边有群友说想要个雷达效果,就尝试写了一下。

    效果图

    demo链接: https://win7killer.github.io/can_demo/demo/radar.html

    ********************************************************************

    这个东西,背景圆,坐标、圆圈都很简单实现,arc结合moveTo、lineTo就可以解决,背景色也不是问题,一句带过。

    那么,有挑战的地方,就是这个扫描的东西

    特点: 

    1、旋转

    2、渐变

    开始实现:

    1、误入歧途

    首先考虑了过渡色,实现过渡色之后,只需要旋转canvas,恩,完美~(头脑简单的例子,后边发现这思路行不通)

    step1. *过渡色*

    过渡色只有“线性过渡”、“辐射过渡(环形过渡)”,而这个效果需要的是一种类似于“扇形侧面过渡”(木有这种过度,我瞎叫的)。环形过渡并不满足需求,只能考虑线性过渡。

    考虑到canvas路径的填充(fillStyle)可以使用过渡色对象,先实现第一帧的过渡,开搞。

    代码如下:

    1 var grd  = ctx.createLinearGradient(175,100,can.width,150);
    2 
    3 grd.addColorStop(0,"rgba(0,255,0,0)");
    4 grd.addColorStop(1,"rgba(0,255,0,1)");

    然后绘制一个扇形,去填充

    1 ctx.fillStyle = grd;
    2 ctx.beginPath();
    3 ctx.moveTo(150,150);
    4 ctx.arc(150, 150, 150, -90/180*Math.PI, 0/180*Math.PI);
    5 ctx.fill();

    加上背景色

    1 ctx.fillStyle = 'rgba(0,0,0,1)';
    2 ctx.strokeStyle = 'rgba(0,255,0,1)';
    3 
    4 ctx.arc(150,150,150,0,2*Math.PI);
    5 ctx.fill();

    效果图如下:

    还算有那么点样子哦~,接下来就是让它动起来

    step2. *旋转*

    旋转思路:旋转点在canvas的中心点,围绕中心点旋转,然后不停的绘制扫描区的扇形。

    用了之前的旋转函数

    复制代码
    1 function drawRotate(deg, fn) {
    2     ctx.save();
    3     ctx.translate(can.width/2, can.height/2);
    4     ctx.rotate(deg);
    5     fn && fn(ctx);
    6     ctx.restore();
    7 }
    复制代码

    但是!!!!真的转起来的时候,问题来了。

    扇形的旋转完美,没问题,说明这个旋转函数也没问题。

    问题出在过渡色身上。。。  

    过渡色创建的时候,走向是固定的,在渲染到扇形后,依旧是一样的走向(扇形每次都要重绘),导致出现错误的结果。

    由于原来的错误代码不全了,所以就没图给大家看了。大家可以自己试一下。

    有考虑到在旋转的过程中去改变过渡色走向,但是涉及到比较繁琐的计算,还是放弃了(比较懒,如果真的去算位置,应该是可以达到效果的)。

    于是放弃,去吃午饭了,大脑肯定是去能量了。

    2、迷途折返

    午饭过后,继续思考,换思路。

    经过考虑,想起以前做“字幕雨”(类似黑客帝国)的思路来。 

    附: 字幕雨链接: https://win7killer.github.io/can_demo/demo/text_rain.html

    思路如下:

    整体思路变化,先处理旋转,再处理过渡。

    step1.  旋转

    以小角度(1°-5°)绘制纯色的扇形,没错,就是纯色的,不要过渡色,然后旋转,以保证扫描区前边亮色。这样,旋转一周,会r让整个雷达高亮。

    注意,这里的旋转不再是旋转canvas,而是不断改变绘制扇形的角度。

    复制代码
    1 function drawRadar(iDeg) {
    2     ctx.fillStyle = 'rgba(0,200,0,.7)';
    3     ctx.beginPath();
    4     ctx.moveTo(150, 150);
    5     ctx.arc(150, 150, 150, (-2 * CFG.perDeg + iDeg) / 180 * Math.PI, (0 + iDeg) / 180 * Math.PI);
    6     ctx.closePath();
    7     ctx.fill();
    8 }
    复制代码

    step2.  *扇形*

    然后,处理过渡。仔细考虑, 这个并不是“过渡色”效果,真的不是,而是“渐进消隐”效果,就是出现后高亮,慢慢消失的效果。

    此类“渐进消隐”效果的做法,很简单,用rgba半透明色(饱和度1的时候与背景色相同)填充整个canvas,一层一层覆盖上去,就会得到慢慢消失的效果。

    loop以下代码:

    复制代码
    1 function cover() {
    2     ctx.save();
    3     ctx.fillStyle = 'rgba(0,0,0,0.02)';
    4     ctx.arc(150, 150, 150, 0, 2 * Math.PI);
    5     ctx.fill();
    6     ctx.restore();
    7 }
    复制代码

    在整个loop中先去覆盖之前的,然后去重绘坐标、圆环等,重绘该改变角度的扇形,就达到了效果,完美。

    最终整体代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    <!DOCTYPE html>
    <html lang="zh">
     
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>radar</title>
        <style>
            canvas {
                margin: 20px auto;
                display: block;
            }
        </style>
    </head>
     
    <body>
        <canvas id="can" width=300 height=300></canvas>
     
        <script type="text/javascript">
            var CFG = {
                perDeg: 1,
            };<br>
            var can = document.getElementById('can');
            var ctx = can.getContext('2d');
            var deg = 0;
            ctx.strokeStyle = 'rgba(0,255,0,1)';
     
            function init() {
                ctx.fillStyle = 'rgba(0,50,0,1)';
                ctx.arc(150, 150, 150, 0, 2 * Math.PI);
                ctx.fill();
                var raf = window.requestAnimationFrame(loop);
            }
     
            function loop() {
                deg = (deg + CFG.perDeg);
                cover();
                drawPosLine();
                drawRadar(deg);
                raf = window.requestAnimationFrame(loop);
            }
     
            function cover() {
                ctx.save();
                ctx.fillStyle = 'rgba(0,0,0,0.02)';
                ctx.arc(150, 150, 150, 0, 2 * Math.PI);
                ctx.fill();
                ctx.restore();
            }
     
            function drawPosLine() {
                ctx.beginPath();
                ctx.moveTo(150, 0);
                ctx.lineTo(150, 300);
                ctx.closePath();
                ctx.stroke();
     
                ctx.beginPath();
                ctx.moveTo(0, 150);
                ctx.lineTo(300, 150);
                ctx.closePath();
                ctx.stroke();
     
                ctx.moveTo(150, 150);
                ctx.beginPath();
                ctx.arc(150, 150, 100, 0 * Math.PI, 2 * Math.PI);
                ctx.closePath();
                ctx.stroke();
     
                ctx.moveTo(150, 150);
                ctx.beginPath();
                ctx.arc(150, 150, 50, 0 * Math.PI, 2 * Math.PI);
                ctx.closePath();
                ctx.stroke();
            }
     
            function drawRadar(iDeg) {
                ctx.fillStyle = 'rgba(0,200,0,.7)';
                ctx.beginPath();
                ctx.moveTo(150, 150);
                ctx.arc(150, 150, 150, (-2 * CFG.perDeg + iDeg) / 180 * Math.PI, (0 + iDeg) / 180 * Math.PI);
                ctx.closePath();
                ctx.fill();
            }
     
            init();
        </script>
    </body>
     
    </html>

    至此,完成效果,符合预期,完美~

    ****************************************************

    <!DOCTYPE html>
    <html lang="zh">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>radar</title>
        <style>
            canvas {
                margin: 20px auto;
                display: block;
            }
        </style>
    </head>
    
    <body>
        <canvas id="can" width=300 height=300></canvas>
    
        <script type="text/javascript">
            var CFG = {
                perDeg: 1,
            };
    
            var aTarget = [];
    
            var can = document.getElementById('can');
            var ctx = can.getContext('2d');
            var deg = 0;
            ctx.strokeStyle = 'rgba(0,255,0,1)';
    
            function init() {
                ctx.fillStyle = 'rgba(0,50,0,1)';
                ctx.arc(150, 150, 150, 0, 2 * Math.PI);
                ctx.fill();
                var raf = window.requestAnimationFrame(loop);
            }
    
            function loop() {
                deg = (deg + CFG.perDeg);
                cover();
                drawPosLine();
                drawRadar(deg);
                raf = window.requestAnimationFrame(loop);
            }
    
            function cover() {
                ctx.save();
                ctx.fillStyle = 'rgba(0,0,0,0.02)';
                ctx.arc(150, 150, 150, 0, 2 * Math.PI);
                ctx.fill();
                ctx.restore();
            }
    
            function drawPosLine() {
                ctx.beginPath();
                ctx.moveTo(150, 0);
                ctx.lineTo(150, 300);
                ctx.closePath();
                ctx.stroke();
    
                ctx.beginPath();
                ctx.moveTo(0, 150);
                ctx.lineTo(300, 150);
                ctx.closePath();
                ctx.stroke();
    
                ctx.moveTo(150, 150);
                ctx.beginPath();
                ctx.arc(150, 150, 100, 0 * Math.PI, 2 * Math.PI);
                ctx.closePath();
                ctx.stroke();
    
                ctx.moveTo(150, 150);
                ctx.beginPath();
                ctx.arc(150, 150, 50, 0 * Math.PI, 2 * Math.PI);
                ctx.closePath();
                ctx.stroke();
            }
    
            function drawRadar(iDeg) {
                ctx.fillStyle = 'rgba(0,200,0,.7)';
                ctx.beginPath();
                ctx.moveTo(150, 150);
                ctx.arc(150, 150, 150, (-2 * CFG.perDeg + iDeg) / 180 * Math.PI, (0 + iDeg) / 180 * Math.PI);
                ctx.closePath();
                ctx.fill();
            }
    
            function bornTarget() {
                aTarget.push({
                    deg: Math.round(Math.random() * 360),
                    r: Math.round(Math.random() * can.width / 2)
                });
            }
    
            init();
        </script>
    </body>
    
    </html>
    

      

  • 相关阅读:
    jQuery火箭图标返回顶部代码
    jQuery火箭图标返回顶部代码
    All strings must be XML compatible: Unicode or ASCII, no NULL bytes or control characters
    记一次odoo创建新的模块时,但是在odoo web界面找不到应用的案例
    python实现格式化输出9*9乘法表
    format和urlencode的使用对比
    python字典小知识
    01
    深浅拷贝再回顾
    DRF的路由生成类的使用
  • 原文地址:https://www.cnblogs.com/libin-1/p/6655511.html
Copyright © 2020-2023  润新知