【问题标题】:How to mark the size of the double head arrows in fabric.js如何在fabric.js中标记双头箭头的大小
【发布时间】:2020-12-09 11:28:21
【问题描述】:

首先感谢this answer,它解决了我画双箭头的需要。

但在此基础上我还有一个问题:

  1. 如何在绘制双头箭头后添加Textbox来表示线的长度?
  2. 如何保持Textbox 与直线平行?这意味着需要计算角度值。
  3. 以上两点完成后,LineArrowTextbox如何结合使用fabric.Group

// Extended fabric line class
fabric.LineArrow = fabric.util.createClass(fabric.Line, {

  type: 'lineArrow',

  initialize: function(element, options) {
    options || (options = {});
    this.callSuper('initialize', element, options);
  },

  toObject: function() {
    return fabric.util.object.extend(this.callSuper('toObject'));
  },

  _render: function(ctx) {
    this.ctx = ctx;
    this.callSuper('_render', ctx);
    let p = this.calcLinePoints();
    let xDiff = this.x2 - this.x1;
    let yDiff = this.y2 - this.y1;
    let angle = Math.atan2(yDiff, xDiff);
    this.drawArrow(angle, p.x2, p.y2);
    ctx.save();
    xDiff = -this.x2 + this.x1;
    yDiff = -this.y2 + this.y1;
    angle = Math.atan2(yDiff, xDiff);
    this.drawArrow(angle, p.x1, p.y1);
  },

  drawArrow: function(angle, xPos, yPos) {
    this.ctx.save();
    this.ctx.translate(xPos, yPos);
    this.ctx.rotate(angle);
    this.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)
    this.ctx.moveTo(10, 0);
    this.ctx.lineTo(-15, 15);
    this.ctx.lineTo(-15, -15);
    this.ctx.closePath();
    this.ctx.fillStyle = this.stroke;
    this.ctx.fill();
    this.ctx.restore();
  }
});



fabric.LineArrow.fromObject = function(object, callback) {
  callback && callback(new fabric.LineArrow([object.x1, object.y1, object.x2, object.y2], object));
};

fabric.LineArrow.async = true;


var Arrow = (function() {
  function Arrow(canvas) {
    this.canvas = canvas;
    this.className = 'Arrow';
    this.isDrawing = false;
    this.bindEvents();
  }

  Arrow.prototype.bindEvents = function() {
    var inst = this;
    inst.canvas.on('mouse:down', function(o) {
      inst.onMouseDown(o);
    });
    inst.canvas.on('mouse:move', function(o) {
      inst.onMouseMove(o);
    });
    inst.canvas.on('mouse:up', function(o) {
      inst.onMouseUp(o);
    });
    inst.canvas.on('object:moving', function(o) {
      inst.disable();
    })
  }

  Arrow.prototype.onMouseUp = function(o) {
    var inst = this;
    // Calculate the length of the LineArrow
    var a = this.line.x2 - this.line.x1
    var b = this.line.y2 - this.line.y1
    var lineLength = Math.floor(Math.sqrt(a * a + b * b));
    if (lineLength === 0) {
      inst.canvas.remove(this.line);
      return;
    }

    var textBox = new fabric.Textbox(lineLength.toFixed(), {
      hasBorders: true,
      hasControls: false,
      selectable: true,
      borderColor: 'red',
      fill: 'red',
      left: this.line.left,
      top: this.line.top
    }).setCoords();

    textBox.set('angle', this.line.angle / (Math.PI / 180));

    inst.canvas.add(textBox).setActiveObject(textBox);

    this.line.set({
      dirty: true,
      objectCaching: true
    });
    inst.canvas.renderAll();
    inst.disable();
  };

  Arrow.prototype.onMouseMove = function(o) {
    var inst = this;
    if (!inst.isEnable()) {
      return;
    }

    var pointer = inst.canvas.getPointer(o.e);
    var activeObj = inst.canvas.getActiveObject();
    activeObj.set({
      x2: pointer.x,
      y2: pointer.y
    });
    activeObj.setCoords();
    inst.canvas.renderAll();
  };

  Arrow.prototype.onMouseDown = function(o) {
    var inst = this;
    inst.enable();
    var pointer = inst.canvas.getPointer(o.e);

    var points = [pointer.x, pointer.y, pointer.x, pointer.y];
    this.line = new fabric.LineArrow(points, {
      strokeWidth: 5,
      fill: 'red',
      stroke: 'red',
      originX: 'center',
      originY: 'center',
      hasBorders: false,
      hasControls: false,
      objectCaching: false,
      perPixelTargetFind: true
    });

    inst.canvas.add(this.line).setActiveObject(this.line);
  };

  Arrow.prototype.isEnable = function() {
    return this.isDrawing;
  }

  Arrow.prototype.enable = function() {
    this.isDrawing = true;
  }

  Arrow.prototype.disable = function() {
    this.isDrawing = false;
  }

  return Arrow;
}());

var canvas = new fabric.Canvas('canvas', {
  selection: false
});
var arrow = new Arrow(canvas);
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.17/fabric.min.js"></script>
Please draw arrow here

<div id="canvasContainer">
  <canvas id="canvas" width="400" height="400" style="border: solid 1px"></canvas>
</div>

以上是我修改后的代码,其实并不完美,希望有人能帮帮我。

提前致谢。

【问题讨论】:

    标签: javascript canvas html5-canvas fabricjs


    【解决方案1】:

    至于问题 2,下面似乎在您的 onMouseUp 函数中起作用:

    var angle = Math.atan2(this.line.y2 - this.line.y1, this.line.x2 - this.line.x1);
    
    textBox.set('angle', angle / (Math.PI / 180));
    

    所以,基本上我们计算箭头和x轴之间的atan并相应地旋转。

    第 3 个问题 - 下面似乎有点把它打包成一个组,检查下面的 sn-p 是否有帮助(在您的 onMouseUp 底部:

        var group = new fabric.Group();
        group.addWithUpdate(fabric.util.object.clone(this.line));
        group.addWithUpdate(textBox);
        inst.canvas.add(group).setActiveObject(group);
        inst.canvas.remove(this.line);
        inst.canvas.renderAll();
        inst.disable();
    

    注意底部功能内容。

    这是一个汇总的 sn-p:

    // Extended fabric line class
    fabric.LineArrow = fabric.util.createClass(fabric.Line, {
    
      type: 'lineArrow',
    
      initialize: function(element, options) {
        options || (options = {});
        this.callSuper('initialize', element, options);
      },
    
      toObject: function() {
        return fabric.util.object.extend(this.callSuper('toObject'));
      },
    
      _render: function(ctx) {
        this.ctx = ctx;
        this.callSuper('_render', ctx);
        let p = this.calcLinePoints();
        let xDiff = this.x2 - this.x1;
        let yDiff = this.y2 - this.y1;
        let angle = Math.atan2(yDiff, xDiff);
        this.drawArrow(angle, p.x2, p.y2);
        ctx.save();
        xDiff = -this.x2 + this.x1;
        yDiff = -this.y2 + this.y1;
        angle = Math.atan2(yDiff, xDiff);
        this.drawArrow(angle, p.x1, p.y1);
      },
    
      drawArrow: function(angle, xPos, yPos) {
        this.ctx.save();
        this.ctx.translate(xPos, yPos);
        this.ctx.rotate(angle);
        this.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)
        this.ctx.moveTo(10, 0);
        this.ctx.lineTo(-15, 15);
        this.ctx.lineTo(-15, -15);
        this.ctx.closePath();
        this.ctx.fillStyle = this.stroke;
        this.ctx.fill();
        this.ctx.restore();
      }
    });
    
    
    
    fabric.LineArrow.fromObject = function(object, callback) {
      callback && callback(new fabric.LineArrow([object.x1, object.y1, object.x2, object.y2], object));
    };
    
    fabric.LineArrow.async = true;
    
    
    var Arrow = (function() {
      function Arrow(canvas) {
        this.canvas = canvas;
        this.className = 'Arrow';
        this.isDrawing = false;
        this.bindEvents();
      }
    
      Arrow.prototype.bindEvents = function() {
        var inst = this;
        inst.canvas.on('mouse:down', function(o) {
          inst.onMouseDown(o);
        });
        inst.canvas.on('mouse:move', function(o) {
          inst.onMouseMove(o);
        });
        inst.canvas.on('mouse:up', function(o) {
          inst.onMouseUp(o);
        });
        inst.canvas.on('object:moving', function(o) {
          inst.disable();
        })
      }
    
      Arrow.prototype.onMouseUp = function(o) {
        var inst = this;
        // Calculate the length of the LineArrow
        var a = this.line.x2 - this.line.x1
        var b = this.line.y2 - this.line.y1
        var lineLength = Math.floor(Math.sqrt(a * a + b * b));
        if (lineLength === 0) {
          inst.canvas.remove(this.line);
          return;
        }
    
        var textBox = new fabric.Textbox(lineLength.toFixed(), {
          hasBorders: true,
          hasControls: false,
          selectable: true,
          borderColor: 'red',
          fill: 'red',
          left: this.line.left,
          top: this.line.top
        }).setCoords();
        
        var angle = Math.atan2(this.line.y2 - this.line.y1, this.line.x2 - this.line.x1);
    
        textBox.set('angle', angle / (Math.PI / 180));
    
        this.line.set({
          dirty: true,
          objectCaching: true
        });
        
        var group = new fabric.Group();
        group.addWithUpdate(fabric.util.object.clone(this.line));
        group.addWithUpdate(textBox);
        inst.canvas.add(group).setActiveObject(group);
        inst.canvas.remove(this.line);
        inst.canvas.renderAll();
        inst.disable();
      };
    
      Arrow.prototype.onMouseMove = function(o) {
        var inst = this;
        if (!inst.isEnable()) {
          return;
        }
    
        var pointer = inst.canvas.getPointer(o.e);
        var activeObj = inst.canvas.getActiveObject();
        activeObj.set({
          x2: pointer.x,
          y2: pointer.y
        });
        activeObj.setCoords();
        inst.canvas.renderAll();
      };
    
      Arrow.prototype.onMouseDown = function(o) {
        var inst = this;
        inst.enable();
        var pointer = inst.canvas.getPointer(o.e);
    
        var points = [pointer.x, pointer.y, pointer.x, pointer.y];
        this.line = new fabric.LineArrow(points, {
          strokeWidth: 5,
          fill: 'red',
          stroke: 'red',
          originX: 'center',
          originY: 'center',
          hasBorders: false,
          hasControls: false,
          objectCaching: false,
          perPixelTargetFind: true
        });
    
        inst.canvas.add(this.line).setActiveObject(this.line);
      };
    
      Arrow.prototype.isEnable = function() {
        return this.isDrawing;
      }
    
      Arrow.prototype.enable = function() {
        this.isDrawing = true;
      }
    
      Arrow.prototype.disable = function() {
        this.isDrawing = false;
      }
    
      return Arrow;
    }());
    
    var canvas = new fabric.Canvas('canvas', {
      selection: false
    });
    var arrow = new Arrow(canvas);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.17/fabric.min.js"></script>
    Please draw arrow here
    
    <div id="canvasContainer">
      <canvas id="canvas" width="400" height="400" style="border: solid 1px"></canvas>
    </div>

    至于第 1 点 - 之后 是什么意思,即。 在时间或空间之后? :D

    【讨论】:

    • 嗨 :) 非常感谢您的回答。其实第一点已经解决了,就是说Textbox是在触发mouseup的时候绘制的。但是还有一些问题没有解决,比如Textbox的位置没有居中。
    • 我想计算余弦:var cosAngle = Math.acos((this.line.x2 - this.line.x1) / lineLength);,然后加上一半长度乘以余弦到leftleft: this.line.left + (lineLength/2) * Math.cos(cosAngle),top 类比正弦。但由于某种原因无法让它工作......
    • 否则我可能错了。它似乎位于中心,仅使用 left: this.line.left。我猜你必须将它移动文本框宽度的一半乘以角度。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多