美文网首页技术专栏
双轨迹旋转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