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

时间:2017-01-16 14:12:02

标签: javascript canvas polygon fabricjs

版本

1.7.3

测试用例

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

在这个小提琴中,我也尝试使用以下代码创建形状以避免创建空多边形,但结果是相同的:

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
    })

重现的步骤

单击绿色按钮进入绘图模式,在画布内拖动鼠标以开始创建形状,松开鼠标,然后尝试选择新创建的形状。边界框大小与新多边形的大小相匹配,但它没有正确定位。

预期行为

如果不使用多边形,我会尝试创建任何其他类型的形状(矩形,圆形或三角形)。它的工作非常精细。

实际行为

由于多边形通过计算点使用不同的方法来计算宽度和高度,因为在这种创建形状的方法中,宽度/高度是在创建形状(使用鼠标:移动事件)后计算的。矩形将无法正确定位。

即使我尝试更改多边形的大小(我不想缩放形状,这也是一个坏主意),边界矩形的位置也不正确,你可以尝试一下上面的演示使用按钮旁边的数字输入字段。

PS

我没有编写calcPolygonPoints函数,我不久前在网上找到了它,不幸的是,我再也找不到它的链接来归功于这个神奇功能的创造者。

1 个答案:

答案 0 :(得分:3)

当你有一个小提琴是非常好的和快速的转换它在一个工作片段,因为那时得到答案很容易。

这么说,关键是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>