通过鼠标拖动创建多边形形状不会将形状居中到其选择边框框

Creating polygon shapes via mouse drag does not center the shape to its selection border box

版本

1.7.3

测试用例

https://jsfiddle.net/human_a/twdgya93/

在这个 fiddle 中,我也尝试使用以下代码创建形状以避免创建空多边形,但结果是一样的:

const polygon = new fabric.Polygon(calcPolygonPoints(8, Math.abs( origX - pointer.x ) / 2 ), {
        objectCaching: false,
        left: origX,
        top: origY,
        originX: 'center',
        originY: 'center',
        fill: 'rgba(255,255,255, 1)',
        perPixelTargetFind: false,
        strokeWidth: 1,
        strokeDashArray: [0,0],
        objType: 'shape',
        stroke: 'rgba(17,17,17,1)',
        hasControls: false,
        hasBorders: false
    })

重现步骤

单击绿色按钮进入绘图模式,将鼠标拖入 canvas 开始创建形状,松开鼠标,然后尝试选择新创建的形状。边界框大小与新多边形的大小匹配,但定位不正确。

预期行为

如果我尝试创建任何其他类型的形状(矩形、圆形或三角形)而不是多边形,它会完美地工作。

实际行为

由于多边形使用不同的方法通过计算点来计算宽度和高度,并且由于在这种创建形状的方法中,width/height 是在创建形状后计算的(mouse:move 事件)边界矩形将无法正确定位。

此外,即使我尝试更改多边形的大小(我不想缩放形状,这是个坏主意)再次边界矩形定位不正确,您可以在上面的演示中尝试使用按钮旁边的数字输入字段。

PS

我没有写 calcPolygonPoints 函数,我前段时间在网上找到它,不幸的是,我找不到它 link 再次感谢这个神奇函数的创建者。

当你有一个 fiddle 时,将它转换成一个工作片段非常好而且很快,从那以后得到答案就很容易了。

这么说,重点是fabric.js中的多边形不支持点更新。您已经在调用 _calcDimensions() 但这还不够。

要使多边形在边界框中正确居中,您还必须使用更新后的值填充其 pathOffset 属性。

我在 mouseUp 事件的片段中添加了这个。

var canvas = new fabric.Canvas();
var el = document.getElementById('my-canvas');
var drawPoly = document.getElementById('draw-poly');
var changeSize = document.getElementById('change-size');
var origX, origY;

var calcPolygonPoints = (sideCount,radius) => {
    var sweep=Math.PI*2/sideCount;
    var cx=radius;
    var cy=radius;
    var points=[]

    for(var i=0;i<sideCount;i++){
        var x=cx+radius*Math.cos( i*sweep )
        var y=cy+radius*Math.sin( i*sweep )
        points.push( { x:x, y:y } )
    }
    return(points)
}

canvas.initialize(el, {
 backgroundColor: '#fff',
 width: 600,
 height: 600
});
canvas.renderAll();

drawPoly.addEventListener('click', (e)=>{
 canvas.defaultCursor = "crosshair";
 canvas.selection = false;
 canvas.discardActiveObject();
 canvas.discardActiveGroup();
 canvas.forEachObject(object=>{ object.selectable = false; });
 canvas.renderAll();

 canvas.on('mouse:down', opt => {
  
  if (canvas.selection) return;
  var pointer = canvas.getPointer(opt.e)

  origX = pointer.x;
  origY = pointer.y;

  // I have also tried initial calculations here
  // by using calcPolygonPoints(8, Math.abs( origX - pointer.x ) / 2 ) instead of []
  // The result is the same
  const polygon = new fabric.Polygon(calcPolygonPoints(8, Math.abs( origX - pointer.x ) / 2 ), {
   objectCaching: false,
   left: origX,
   top: origY,
   originX: 'center',
   originY: 'center',
   fill: 'rgba(255,255,255, 1)',
   perPixelTargetFind: false,
   strokeWidth: 1,
   strokeDashArray: [0,0],
   objType: 'shape',
   stroke: 'rgba(17,17,17,1)',
   hasControls: false,
   hasBorders: false
  })
  // polygon._calcDimensions()
  canvas.add(polygon).setActiveObject(polygon)

 }).on('mouse:move', opt => {

  if (canvas.selection || !canvas.getActiveObject()) return;
  const newShape = canvas.getActiveObject()
  var pointer = canvas.getPointer(opt.e)

  if (newShape) {
   newShape.set({
    points: calcPolygonPoints(8, Math.abs( origX - pointer.x ) / 2 )
   })
   newShape._calcDimensions()
  }
  changeSize.value = Math.abs( origX - pointer.x ) / 2;
  canvas.renderAll()

 }).on('mouse:up', opt => {
  // In my app I am using redux stores to turn off the drawing
  // Here I used the following if statement to turn off the drawing
  if (canvas.selection) return;

  const newShape = canvas.getActiveObject()
  if (newShape) {
   newShape.set({
    hasControls: true,
    hasBorders: true
   })
      newShape.pathOffset = {
        x: newShape.minX + newShape.width / 2,
        y: newShape.minY + newShape.height / 2
      };
      var pointer = canvas.getPointer(opt.e);
      var center = { x: (pointer.x + origX)/2, y: (pointer.y + origY)/2}
      newShape.setPositionByOrigin(center, 'center', 'center')
   newShape.setCoords()
   canvas.renderAll()
  }
  canvas.renderAll()
  canvas.selection = true;

  canvas.off('mouse:down').off('mouse:move')
  canvas.defaultCursor = "default";
  canvas.discardActiveObject()
  canvas.forEachObject(object=>{
   if (object.evented)  object.selectable = true;
  })
 })
})

changeSize.addEventListener('input', (e)=>{
 if (!canvas.getActiveObject()) return;
 
 canvas.getActiveObject().set({
  points: calcPolygonPoints(8, parseInt(e.target.value, 10) )
 })
 canvas.getActiveObject()._calcDimensions()
 canvas.renderAll()
})
button {
 border: 0 none;
 background: #2ecc70;
 border-radius: 5px;
 cursor: pointer;
 color: #fff;
 box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
 text-transform: uppercase;
 padding: 11px 22px;
 font-weight: 600;
 font-size: 13px;
 letter-spacing: 1px;
 margin: 10px auto;
 outline: 0 none;
}

input {
 border: 1px solid #ddd;
 box-shadow: none;
 padding: 11px;
 font-size: 13px;
 border-radius: 5px;
 margin-left: 10px;
 max-width: 50px;
 outline: 0 none !important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.3/fabric.min.js"></script>
<div id="wrapper">
 <canvas id="my-canvas"></canvas>
 <button id="draw-poly">Draw Polygon</button>
 <input type="number" id="change-size" value="0" />
</div>