zoukankan      html  css  js  c++  java
  • canvas绘制飞线效果

    在我们做的可视化大屏项目中,经常会遇到飞线的效果。 在我们的大屏编辑器中,可以通过拖拽+配置参数的方式很快就能够实现。下面是我们使用大屏编辑器实现的一个项目效果:

    效果

    中间地图就有飞线的效果。

    抛开编辑器的快速实现不说,我们大致来说下canvas绘制飞线的大致原理。

    贝塞尔曲线

    飞线的路径主要是一个贝塞尔曲线,canvas绘制贝塞尔曲线比较容易。canvas支持绘制二次和三次,在本次示例中,主要还是绘制二次贝塞尔曲线为主。canvas中指定二次贝塞尔曲线路径的函数如下:

    ctx.quadraticCurveTo(cpx, cpy, x, y);
    

    有关贝塞尔曲线的基础知识,读者可以自行学习,此处不再赘述。

    渐变实现

    从图中,可以看出飞线的效果是淡入的效果,颜色并不是一致的,起点处颜色很淡,终点处颜色就比较浓厚。
    怎么样能够实现这种效果呢? 答案就是渐变,我们知道,canvas支持线性渐变和放射渐变。但是这两种渐变似乎都不太适合曲线的路径。

    事实上,我们会考虑使用线性渐变。因为飞线效果中,曲线的弯曲程度都不太大,所以使用线性渐变,曲线造成的差异,人眼是感觉不出来的。

    嗯嗯,图形学就是欺骗的艺术。

    只要在线的起点和终点创建一个线性渐变,起点的颜色非透明度是0,终点的非透明度是1即可达到目标。

    示例代码如下:

      function createGradient(ctx,p0,p1){
               var grd = ctx.createLinearGradient(p0.x,p0.y,p1.x,p1.y);
               grd.addColorStop(0,'rgba(255,0,255,0)');
               grd.addColorStop(1,'rgba(255,0,255,1)');
               return grd;
       }
      ctx.beginPath();
      ctx.moveTo(P0.x,P0.y);
      ctx.quadraticCurveTo(Q01.x,Q01.y,B1.x,B1.y);
      ctx.lineCap = 'round';
      ctx.lineWidth =3;
      ctx.strokeStyle = createGradient(ctx,P0,P2);
      ctx.shadowColor = 'rgba(255,0,255,1)';
      ctx.shadowBlur = 5;
      ctx.stroke();
    

    渐变效果

    流动效果

    流动效果就是线条从起点开始,慢慢飞到终点的效果。 技术角度来说,就是绘制二次曲线百分之几的一部分,百分比的数值从0增加到1,然后又回到0,周而复始。
    代码如下:

            let percent = 0.0;       
            function render(){
                ctx.save();
                //按百分比绘制
                ctx.restore();
                percent += 0.005;
                if(percent > 1){
                    percent = 0.;
                }
                requestAnimationFrame(render);
            }
    

    问题的关键在于如何绘制贝塞尔曲线的一部分。 一种思路是使用二次贝塞尔曲线的公式,把曲线分成很多片段来进行模拟,然而这种方式的效率并不高。 其实可以使用插值的方式来获取一段贝塞尔曲线。代码如下:

        // 参考https://xiaozhuanlan.com/topic/9506147283#section0t
               let P0 = startPoint, P1 = controlPoint,P2 = endPoint;
               let Q01 = interpolation(P0,P1,percent),
                   Q11 = interpolation(P1,P2,percent),
                   B1 = interpolation(Q01,Q11,percent);
    function interpolation(P0,P1,t) {
               var Q = {
                   x: P0.x * (1 - t) + P1.x * (t),
                   y: P0.y * (1 - t) + P1.y * (t),
               };
               return Q;
           } 
    

    有关上面插值的原理,可以参考下面的说明,摘取字文章:
    https://xiaozhuanlan.com/topic/9506147283#section-5

    二次贝塞尔曲线

    我们知道二次贝塞尔曲线有三个点P0、P1、P2。二次贝塞尔曲线的表达方程如下:
    B(t) = (1-t)2 * P0 + 2t(1-t) * P1 + t2 * P2
    其中: $t in $[0,1]

    借助上面一次贝塞尔曲线的计算方法,可以通过以下步骤来确定二次贝塞尔曲线的B(t)点:

    • 选定 $t in $[0,1]
    • 通过插值运算法则,在P0和P1所组成的线段上,计算出P0和P1点之间的插值点Q0,其中插值的比例值是t。根据插值规则有:length( P0, Q0 ) = length( P0, P1 ) * t
    • 通过插值运算法则,在P1和P2所组成的线段上,计算出P1和P2点之间的插值点Q1,其中插值的比例是t。
    • 通过插值运算法则,在Q1和Q2所组成的线段上,计算出P1和P2点之间的插值点B,其中插值的比例是t。
      上述过程中计算出来的点B就是在曲线上面点。上述过程如下图所示:

    二次贝塞尔曲线的计算方法

    从图中可以得出结论:

    • 直线(Q0,Q1)和曲线相切于B点。

    另外还有隐藏的结论:

    • 曲线(P0,B)也是贝塞尔曲线,P0是曲线的起始点,B是曲线的终止点,而Q0是控制点
    • 曲线(B,P2)也是贝塞尔曲线,B是曲线的起始点,P2是曲线的终止点,而Q1是控制点

    上面两个结论会很有用,有了这个两个结论,前面“迭代(分片)”绘制部分贝塞尔的方法,可以用更加简单的方法替代,这在稍后详细说明。

    如果将t的值从0过渡到1,不断计算点B,这些点的集合就可以组成一条二次贝塞尔曲线。下面图形动画复现了这个效果:
    二次贝塞尔曲线的计算方法过程

    通过上面的方式,就可以绘制流动的飞线效果了,如下图所示:

    流动效果

    加上阴影

    默认线条的样式并不是很好看,如果加上阴影,可以让效果更加丰满。 加上阴影也很简单,代码如下:

     ctx.shadowColor = 'rgba(255,0,255,1)';
     ctx.shadowBlur = 5;
    

    最终的飞线效果参考下图:
    效果

    结语

    如果对可视化感兴趣,可以和我交流,微信541002349. 如果你对我们的大屏编辑器产品或者3d组态编辑器感兴趣,可以加我微信交流,或者发邮件也可以,terry.tan@servasoft.com。 另外关注公众号“ITMan彪叔” 可以及时收到更多有价值的文章。

  • 相关阅读:
    C++ mutex&&RWlock
    一个资源池模型
    C++安全编码摘录
    C++点滴
    git常用命令(转)
    ASN.1
    TDOA泰勒级数法
    teamviewer解除最大通话限制-特别破解版(转)
    剑指offer——旋转数组的最小数字
    常见的相关分析方法——结合实例
  • 原文地址:https://www.cnblogs.com/flyfox1982/p/13954719.html
Copyright © 2011-2022 走看看