Drawing a wavy line in FabricJS

后端 未结 2 746
囚心锁ツ
囚心锁ツ 2021-01-01 02:35

I\'m using FabricJS to create a canvas for drawing specific lines and shapes. One of the lines is a wavy line with an arrow similar to this:

I\'ve successfu

2条回答
  •  耶瑟儿~
    2021-01-01 03:09

    As we are drawing from both corners for a line, you can draw wavy line in _render method of custom class. From the end I draw a line to mid , to show its connected with arrow.

    DEMO

    var line, isDown, evented;
    var canvas = new fabric.Canvas('canvas', {
      perPixelTargetFind: true
    });
    draw();
    
    function selection() {
      changeObjSelection(true);
      canvas.off('mouse:down');
      canvas.off('mouse:move');
      canvas.off('mouse:up');
      evented = false;
    }
    
    function draw() {
      changeObjSelection(false);
      if (!evented) {
        canvas.on('mouse:down', onMouseDown);
        canvas.on('mouse:move', onMouseMove);
        canvas.on('mouse:up', onMouseUp);
        evented = true;
      }
    }
    
    function clearCanvas() {
     canvas.clear();
    }
    
    function changeObjSelection(value) {
      canvas.selection = value;
      canvas.forEachObject(function(obj) {
        obj.selectable = value;
      })
      canvas.requestRenderAll();
    }
    
    function onMouseDown(options) {
      isDown = true;
      var pointer = canvas.getPointer(options.e);
      var points = [pointer.x, pointer.y, pointer.x, pointer.y];
      line = selectLine(points);
      canvas.add(line);
    }
    
    function onMouseMove(options) {
      if (!isDown) return;
      var pointer = canvas.getPointer(options.e);
      line.set({
        x2: pointer.x,
        y2: pointer.y
      });
      canvas.renderAll();
    
    }
    
    function onMouseUp(options) {
      isDown = false;
      line.setCoords();
      canvas.requestRenderAll();
    }
    
    function drawLineWithArrow(points, color) {
      return new fabric.LineWithArrow(points, {
        strokeWidth: 2,
        stroke: color,
        objectCaching: false,
        selectable: false
      })
    }
    
    function selectLine(points) {
      return drawLineWithArrow(points, 'black');
    }
    
    //Wavy line
    
    (function(global) {
      'use strict';
      if (fabric.LineWithArrow) {
        fabric.warn('fabric.LineWithArrow is already defined.');
        return;
      }
      var clone = fabric.util.object.clone;
      fabric.LineWithArrow = fabric.util.createClass(fabric.Line, {
        type: 'lineWithArrow',
    
        initialize: function(element, options) {
          options || (options = {});
          this.callSuper('initialize', element, options);
    
          // Set default options
          this.set({
            hasBorders: false,
            hasControls: false,
          });
        },
    
        _render: function(ctx) {
          // this.callSuper('_render', ctx);
          ctx.save();
          const xDiff = this.x2 - this.x1;
          const yDiff = this.y2 - this.y1;
          const angle = Math.atan2(yDiff, xDiff);
          ctx.translate(xDiff / 2, yDiff / 2);
          ctx.rotate(angle);
          ctx.beginPath();
          // Move 5px in front of line to start the arrow so it does not have the square line end showing in front (0,0)
          ctx.moveTo(5, 0);
          ctx.lineTo(-5, 5);
          ctx.lineTo(-5, -5);
          ctx.closePath();
          ctx.fillStyle = this.stroke;
          ctx.fill();
          ctx.restore();
          var p = this.calcLinePoints();
          var point = this.pointOnLine(this.point(p.x2, p.y2), this.point(p.x1, p.y1), 10)
          this.wavy(this.point(p.x1, p.y1), point, this.point(p.x2, p.y2), ctx);
          ctx.stroke();
        },
    
        point: function(x, y) {
          return {
            x: x,
            y: y
          };
        },
    
        wavy: function(from, to, endPoint, ctx) {
          var cx = 0,
            cy = 0,
            fx = from.x,
            fy = from.y,
            tx = to.x,
            ty = to.y,
            i = 0,
            step = 4,
            waveOffsetLength = 0,
    
            ang = Math.atan2(ty - fy, tx - fx),
            distance = Math.sqrt((fx - tx) * (fx - tx) + (fy - ty) * (fy - ty)),
            amplitude = -10,
            f = Math.PI * distance / 30;
    
          for (i; i <= distance; i += step) {
            waveOffsetLength = Math.sin((i / distance) * f) * amplitude;
            cx = from.x + Math.cos(ang) * i + Math.cos(ang - Math.PI / 2) * waveOffsetLength;
            cy = from.y + Math.sin(ang) * i + Math.sin(ang - Math.PI / 2) * waveOffsetLength;
            i > 0 ? ctx.lineTo(cx, cy) : ctx.moveTo(cx, cy);
          }
          ctx.lineTo(to.x, to.y);
          ctx.lineTo(endPoint.x, endPoint.y);
        },
    
        pointOnLine: function(point1, point2, dist) {
          var len = Math.sqrt(((point2.x - point1.x) * (point2.x - point1.x)) + ((point2.y - point1.y) * (point2.y - point1.y)));
          var t = (dist) / len;
          var x3 = ((1 - t) * point1.x) + (t * point2.x),
            y3 = ((1 - t) * point1.y) + (t * point2.y);
          return new fabric.Point(x3, y3);
        },
    
        toObject: function() {
          return fabric.util.object.extend(this.callSuper('toObject'), {
            customProps: this.customProps,
          });
        },
      });
      fabric.LineWithArrow.fromObject = function(object, callback) {
        function _callback(instance) {
          delete instance.points;
          callback && callback(instance);
        };
        var options = clone(object, true);
        options.points = [object.x1, object.y1, object.x2, object.y2];
        fabric.Object._fromObject('LineWithArrow', options, _callback, 'points');
      };
    })(typeof exports !== 'undefined' ? exports : this);
    canvas {
      border: 2px dotted black;
    }
    
    
    
    
    

提交回复
热议问题