美文网首页技术专栏
双轨迹旋转Chart

双轨迹旋转Chart

作者: Cesium4Unreal | 来源:发表于2018-08-25 08:58 被阅读0次

    效果

    双轨迹旋转Chart

    实现思路

    因工作关系,首先想到使用TWaver的矢量图定制,这样可以减少很多工作量,当然使用Canvas也可以实现同样的效果。
    记得以前写过一个流动线条攻击效果,用来表示网络安全态势攻击,echart也有相应实现,效果如图:

    流动攻击效果

    记得当时的思路是,用一次绘制很多个小圈圈,通过渐变,透明度等设置,来模拟轨迹的效果。然而这种方法的致命缺点就是效率问题。

    function FlowLink() {
        FlowLink.superClass.constructor.apply(this, arguments);
      }
    
      twaver.Util.ext(FlowLink, twaver.Link, {
        getVectorUIClass: function() {
          return FlowLinkUI;
        },
        playAnimate: function(dur) {
          this.animate.play();
        },
        setAnimate: function(dur) {
          var dur = dur || 1200;
          var self = this;
          var fNode, tNode;
    
          fNode = self.getFromNode();
          tNode = self.getToNode();
    
          this.animate = new twaver.Animate({
            from: 0,
            to: 1,
            repeat: Number.POSITIVE_INFINITY,
            // repeat: 2,
            reverse: false,
            dur: dur,
            onPlay: function() {
    
            },
            onUpdate: function(value) {
              self.setClient('percent', value);
              if (value >= 1) self.setClient('over', true);
              else self.setClient('over', false);
            },
            onDone: function() {
    
            }
          });
        }
      });
    
      function FlowLinkUI() {
        FlowLinkUI.superClass.constructor.apply(this, arguments);
      }
    
      twaver.Util.ext(FlowLinkUI, twaver.vector.LinkUI, {
        paintBody: function(ctx) {
          var link = this.getElement();
          if (link.getClient('over')) return;
          var fillColor = link.getClient('fillColor');
          var shadowColor = link.getClient('shadowColor');
          var tail = link.getClient('tail');
          var percent = link.getClient('percent');
          var paths = this.getLinkPoints();
          var offset = this.getLineLength() * percent;
          var tailFactor = link.getClient('tailFactor') || 1.5;
          var tailRadius = link.getClient('tailRadius') || 2;
          var point;
          var fromPoint = this._element.getFromAgent().getCenterLocation();
          var toPoint = this._element.getToAgent().getCenterLocation();
          var point = _twaver.math.calculatePointInfoAlongLine(paths, true, offset, 0).point;
          var points = new twaver.List();
    
          points.add(fromPoint);
          points.add(point);
    
          if (link.getClient('controlPointX')) {
            var controlPoint = {
              x: link.getClient('controlPointX'),
              y: link.getClient('controlPointY')
            };
            var c1 = {
              x: (fromPoint.x + controlPoint.x) / 2,
              y: (fromPoint.y + controlPoint.y) / 2
            };
            var c2 = {
              x: (toPoint.x + controlPoint.x) / 2,
              y: (toPoint.y + controlPoint.y) / 2
            };
            var c3 = {
              x: (c1.x + c2.x) / 2,
              y: (c1.y + c2.y) / 2
            };
            points.add(c1);
            points.add(c2);
            points.add(c3);
            // points.add(controlPoint);
          }
          var rect = _twaver.math.getRect(points);
    
          if (fromPoint.x < toPoint.x) {
            ctx.rect(rect.x, rect.y, Math.abs(point.x - fromPoint.x) + 1.5, rect.height);
          } else {
            ctx.rect(point.x - 1.5, rect.y, Math.abs(point.x - fromPoint.x), rect.height);
          }
          ctx.clip();
          FlowLinkUI.superClass.paintBody.call(this, ctx);
    
          for (var i = 0, count = tail; i < count; i++) {
            var v = i / count;
            point = _twaver.math.calculatePointInfoAlongLine(paths, true, offset - (count - i) *
              tailFactor, 0).point;
            ctx.globalAlpha = v * v;
            ctx.shadowBlur = 10;
            ctx.shadowColor = shadowColor;
            ctx.beginPath();
            ctx.fillStyle = fillColor;
            ctx.arc(point.x, point.y, tailRadius, 0, Math.PI * 2, false);
            ctx.fill();
          }
          for (var i = 0, count = tail; i < count; i++) {
            var v = i / count;
            point = _twaver.math.calculatePointInfoAlongLine(paths, true, offset - (count - i) * 1,
              0).point;
            ctx.globalAlpha = v * v;
            ctx.shadowBlur = 1;
            ctx.shadowColor = shadowColor;
            ctx.beginPath();
            ctx.fillStyle = 'white';
            ctx.arc(point.x, point.y, 1, 0, Math.PI * 2, false);
            ctx.fill();
          }
        }
      });
    

    点击查看效果
    使用globalCompositeOperation方法

    var canvas = this.canvas,
                g = this.g,
                gradient = this.gradient,
                gradient2 = this.gradient2,
                diameter = Math.floor(data.getWidth() * view.getZoom()),
                radius = diameter / 2,
                hueStart = 120,
                hueEnd = 170,
                hueDiff = Math.abs(hueEnd - hueStart),
                width = diameter,
                height = diameter;
              if (!canvas) {
                this.canvas = canvas = document.createElement('canvas');
              }
    
              if (width !== canvas.width || height !== canvas.height) {
                canvas.width = diameter;
                canvas.height = diameter;
                this.g = g = canvas.getContext('2d');
                this.gradient = gradient = g.createLinearGradient(radius, 0, 0, 0);
                gradient.addColorStop(0, 'hsla(' + hueStart + ', ' + 50 + '%, ' + 40 + '%, 1.0)');
                gradient.addColorStop(1, 'hsla(' + hueEnd + ', ' + 50 + '%, ' + 40 + '%, 0.1)');
    
                this.gradient2 = gradient2 = g.createLinearGradient(-radius, 0, 0, 0);
                gradient2.addColorStop(0, 'hsla(' + hueStart + ', ' + 50 + '%, ' + 40 + '%, 1.0)');
                gradient2.addColorStop(1, 'hsla(' + hueEnd + ', ' + 50 + '%, ' + 40 + '%, 0.1)');
              }
    
              g.globalCompositeOperation = 'destination-out'; //'destination-out';
              g.fillStyle = 'hsla(0, 0%, 0%, 0.1)';
              g.fillRect(0, 0, width, height);
    
              g.globalCompositeOperation = 'lighter';
              var i;
              for (i = 2; i < 3; i++) {
                g.beginPath();
                g.arc(radius, radius, ((radius - 1) / 4) * (i + 1), 0, Math.PI * 2, false);
                g.strokeStyle = 'hsla(' + (hueEnd - (i * (hueDiff / 4))) + ', ' + 50 + '%, ' + 40 + '%, 0.1)';
                g.lineWidth = 2;
                g.stroke();
              };
    
              g.beginPath();
              g.save();
              g.translate(radius, radius);
              g.rotate(data.getClient('angle'));
              g.arc(((radius - 1) / 4) * (2.5 + 1), 0, 4, 0, Math.PI * 2, false);
              g.fillStyle = gradient;
              g.fill();
              g.restore();
    
              g.beginPath();
              g.save();
              g.translate(radius, radius);
              g.rotate(data.getClient('angle'));
              g.arc(-((radius - 1) / 4) * (2.5 + 1), 0, 4, 0, Math.PI * 2, false);
              g.fillStyle = gradient2;
              g.fill();
              g.restore();
    
              ctx.scale(1 / view.getZoom(), 1 / view.getZoom());
              ctx.drawImage(canvas, -width / 2, -height / 2);
              stats.end();
    

    点击查看效果

    性能对比

    FlowLink


    image.png

    双轨迹


    image.png
    明显后者性能好于前者。

    参考文献

    1. http://doc.servasoft.com/twaver-document-center/recommended/twaver-html5-guide/animation/
    2. https://devdocs.io/dom/canvasrenderingcontext2d/globalcompositeoperation
    3. https://devdocs.io/dom/canvas_api/tutorial/compositing
    4. https://github.com/slothbrain/stats.js

    相关文章

      网友评论

        本文标题:双轨迹旋转Chart

        本文链接:https://www.haomeiwen.com/subject/wgewiftx.html