fabric.js 箭头和箭头旋转
fabric.js arrows and arrow head rotating
我想在 fabric.js 中创建将两个对象连接在一起的箭头。
我这里有一个 jsFiddle link:http://jsfiddle.net/xvcyzh9p/45/(感谢@gco)。
以上允许您创建两个对象(两个矩形)并用一条线将它们连接在一起。
function addChildLine(options) {
canvas.off('object:selected', addChildLine);
// add the line
var fromObject = canvas.addChild.start;
var toObject = options.target;
var from = fromObject.getCenterPoint();
var to = toObject.getCenterPoint();
var line = new fabric.Line([from.x, from.y, to.x, to.y], {
fill: 'red',
stroke: 'red',
strokeWidth: 5,
selectable: false
});
canvas.add(line);
// so that the line is behind the connected shapes
line.sendToBack();
// add a reference to the line to each object
fromObject.addChild = {
// this retains the existing arrays (if there were any)
from: (fromObject.addChild && fromObject.addChild.from) || [],
to: (fromObject.addChild && fromObject.addChild.to)
}
fromObject.addChild.from.push(line);
toObject.addChild = {
from: (toObject.addChild && toObject.addChild.from),
to: (toObject.addChild && toObject.addChild.to) || []
}
toObject.addChild.to.push(line);
// to remove line references when the line gets removed
line.addChildRemove = function () {
fromObject.addChild.from.forEach(function (e, i, arr) {
if (e === line)
arr.splice(i, 1);
});
toObject.addChild.to.forEach(function (e, i, arr) {
if (e === line)
arr.splice(i, 1);
});
}
canvas.addChild = undefined;
}
function addChildMoveLine(event) {
canvas.on(event, function (options) {
var object = options.target;
var objectCenter = object.getCenterPoint();
// udpate lines (if any)
if (object.addChild) {
if (object.addChild.from)
object.addChild.from.forEach(function (line) {
line.set({ 'x1': objectCenter.x, 'y1': objectCenter.y });
})
if (object.addChild.to)
object.addChild.to.forEach(function (line) {
line.set({ 'x2': objectCenter.x, 'y2': objectCenter.y });
})
}
canvas.renderAll();
});
}
我曾尝试查看其他示例以在 fabric.js 中创建箭头,但在 gco 的 fiddle 中实施一直很痛苦。
我在这方面的最佳尝试可以在这里找到:http://example.legalobjects.com/
看起来像这样:
我遇到的一些问题是:
- 箭头未按正确方向移动
- 当向 canvas 添加多个箭头时,箭头(或多个箭头)断裂 - 由于某种原因,它们得到 "stuck"。
- 箭头head/line不围绕对象移动
添加多个箭头时会发生这种情况:
如果有人有任何想法或可以提供帮助,我将不胜感激!
谢谢。
让我们来解决您的问题。
The arrow head (or multiple arrow heads) breaking when more than one arrow is added to the canvas - they get "stuck" for some reason.
这是因为你使用了一个单一的triangle
全局变量,这意味着实际上只能有一个三角形。虽然这很容易解决 - 只需将 triangle
替换为 line.triangle
,这样它就变成了它所属行的 属性。
例如而不是
triangle = new fabric.Triangle({
简单使用
line.triangle = new fabric.Triangle({
并因此替换
line.addChildRemove();
line.remove();
line.triangle.remove();
与
line.triangle.remove();
line.addChildRemove();
line.remove();
The arrow head not moving around the in the correct direction
这是您的方向逻辑的一个简单缺点。没有任何部分是真正错误的,只是它没有按照您的意愿进行。我将其修改为以下代码段:
object.addChild.to.forEach(function(line) {
var x = objectCenter.x;
var y = objectCenter.y;
var xdis = REC_WIDTH/2 + TRI_WIDTH/2;
var ydis = REC_HEIGHT/2 + TRI_HEIGHT/2;
var horizontal = Math.abs(x - line.x2) > Math.abs(y - line.y2);
line.set({
'x2': x + xdis * (horizontal ? (x < line.x2 ? 1 : -1) : 0),
'y2': y + ydis * (horizontal ? 0 : (y < line.y2 ? 1 : -1))
});
line.triangle.set({
'left': line.x2, 'top': line.y2,
'angle': calcArrowAngle(line.x1, line.y1, line.x2, line.y2)
});
});
基本思路是偏移或者 X 或者 Y坐标,基于两者的X或者Y距离对象更大。
Arrow head/line not moving around the object.
上面发布的代码片段中的另一个逻辑缺陷。使用 x2
和 y2
计算三角形是正确的,因为您将它的位置作为 target 矩形的基础。然而,对于该行,您希望根据 source 矩形的位置进行计算,因此您需要分别使用 x1
和 y1
。所以我们可以把上面的代码再改成:
object.addChild.to.forEach(function(line) {
var x = objectCenter.x;
var y = objectCenter.y;
var xdis = REC_WIDTH/2 + TRI_WIDTH/2;
var ydis = REC_HEIGHT/2 + TRI_HEIGHT/2;
var horizontal = Math.abs(x - line.x1) > Math.abs(y - line.y1);
line.set({
'x2': x + xdis * (horizontal ? (x < line.x1 ? 1 : -1) : 0),
'y2': y + ydis * (horizontal ? 0 : (y < line.y1 ? 1 : -1))
});
line.triangle.set({
'left': line.x2, 'top': line.y2,
'angle': calcArrowAngle(line.x1, line.y1, line.x2, line.y2)
});
});
为了获得更加无缝的体验,您还需要更改调用重新计算的方式。目前您只更新连接到您实际移动的矩形的那部分线,而不更新连接的另一半。
我通过用一个简单的单个 lines
数组替换 to
和 from
数组,并向每个行元素添加属性 fromObject
和 toObject
来实现这一点 - on每次更新,行的两端都会更新,如下所示:
if (object.addChild && object.addChild.lines) {
object.addChild.lines.forEach(function(line) {
var fcenter = line.fromObject.getCenterPoint(),
fx = fcenter.x,
fy = fcenter.y,
tcenter = line.toObject.getCenterPoint(),
tx = tcenter.x,
ty = tcenter.y,
xdis = REC_WIDTH/2 + TRI_WIDTH/2,
ydis = REC_HEIGHT/2 + TRI_HEIGHT/2,
horizontal = Math.abs(tx - line.x1) > Math.abs(ty - line.y1)
line.set({
'x1': fx,
'y1': fy,
'x2': tx + xdis * (horizontal ? (tx < line.x1 ? 1 : -1) : 0),
'y2': ty + ydis * (horizontal ? 0 : (ty < line.y1 ? 1 : -1)),
});
line.triangle.set({
'left': line.x2, 'top': line.y2,
'angle': calcArrowAngle(line.x1, line.y1, line.x2, line.y2)
});
});
}
除此之外,您还有一个未处理的用例:如果您 select 一个框,删除它然后单击“添加子项”,则会出现错误。您可以通过简单地在 addChild
函数的顶部测试 null
来防止这种情况,如下所示:
if(canvas.getActiveObject() == null)
{
return;
}
有了以上所有,我创建了 an updated fiddle。
多年后的更新:原来你可以拖动到 select 多个矩形,这将它们放在一个组中,你可以作为一个整体移动,这样做会在我的后面留下箭头上面的解决方案(如评论中的“sugars”所述)。解决方案是将更新代码嵌入到它自己的函数中,并递归调用该事件的目标对象的每个对象是一个组。然后你也 运行 进入对象坐标相对于组中心的问题,所以你也必须为此进行调整。我也修复了这个问题 updated fiddle。
新的更新函数代码为:
function addChildMoveLine(event) {
canvas.on(event, function(options) {
if((function updateObject(object, offset) {
// update child objects, if any
if (object.getObjects) {
var off = object.getCenterPoint();
return object.getObjects().reduce(function(flag, obj) {
return updateObject(obj, off) || flag;
}, false);
}
if (!offset)
offset = { x: 0, y: 0 };
// otherwise udpate lines, if any
if (object.addChild && object.addChild.lines) {
object.addChild.lines.forEach(function(line) {
var fcenter = line.sourceObject.getCenterPoint(),
fx = fcenter.x + offset.x,
fy = fcenter.y + offset.y,
tcenter = line.targetObject.getCenterPoint(),
tx = tcenter.x + offset.x,
ty = tcenter.y + offset.y,
xdis = REC_WIDTH/2 + TRI_WIDTH/2,
ydis = REC_HEIGHT/2 + TRI_HEIGHT/2,
horizontal = Math.abs(tx - line.x1) > Math.abs(ty - line.y1);
line.set({
'x1': fx,
'y1': fy,
'x2': tx + xdis * (horizontal ? (tx < line.x1 ? 1 : -1) : 0),
'y2': ty + ydis * (horizontal ? 0 : (ty < line.y1 ? 1 : -1)),
});
line.triangle.set({
'left': line.x2, 'top': line.y2,
'angle': calcArrowAngle(line.x1, line.y1, line.x2, line.y2)
});
});
return true; // re-render
}
return false; // no re-render needed
})(options.target)) {
canvas.renderAll();
}
});
}
我想在 fabric.js 中创建将两个对象连接在一起的箭头。
我这里有一个 jsFiddle link:http://jsfiddle.net/xvcyzh9p/45/(感谢@gco)。
以上允许您创建两个对象(两个矩形)并用一条线将它们连接在一起。
function addChildLine(options) {
canvas.off('object:selected', addChildLine);
// add the line
var fromObject = canvas.addChild.start;
var toObject = options.target;
var from = fromObject.getCenterPoint();
var to = toObject.getCenterPoint();
var line = new fabric.Line([from.x, from.y, to.x, to.y], {
fill: 'red',
stroke: 'red',
strokeWidth: 5,
selectable: false
});
canvas.add(line);
// so that the line is behind the connected shapes
line.sendToBack();
// add a reference to the line to each object
fromObject.addChild = {
// this retains the existing arrays (if there were any)
from: (fromObject.addChild && fromObject.addChild.from) || [],
to: (fromObject.addChild && fromObject.addChild.to)
}
fromObject.addChild.from.push(line);
toObject.addChild = {
from: (toObject.addChild && toObject.addChild.from),
to: (toObject.addChild && toObject.addChild.to) || []
}
toObject.addChild.to.push(line);
// to remove line references when the line gets removed
line.addChildRemove = function () {
fromObject.addChild.from.forEach(function (e, i, arr) {
if (e === line)
arr.splice(i, 1);
});
toObject.addChild.to.forEach(function (e, i, arr) {
if (e === line)
arr.splice(i, 1);
});
}
canvas.addChild = undefined;
}
function addChildMoveLine(event) {
canvas.on(event, function (options) {
var object = options.target;
var objectCenter = object.getCenterPoint();
// udpate lines (if any)
if (object.addChild) {
if (object.addChild.from)
object.addChild.from.forEach(function (line) {
line.set({ 'x1': objectCenter.x, 'y1': objectCenter.y });
})
if (object.addChild.to)
object.addChild.to.forEach(function (line) {
line.set({ 'x2': objectCenter.x, 'y2': objectCenter.y });
})
}
canvas.renderAll();
});
}
我曾尝试查看其他示例以在 fabric.js 中创建箭头,但在 gco 的 fiddle 中实施一直很痛苦。
我在这方面的最佳尝试可以在这里找到:http://example.legalobjects.com/
看起来像这样:
我遇到的一些问题是:
- 箭头未按正确方向移动
- 当向 canvas 添加多个箭头时,箭头(或多个箭头)断裂 - 由于某种原因,它们得到 "stuck"。
- 箭头head/line不围绕对象移动
添加多个箭头时会发生这种情况:
如果有人有任何想法或可以提供帮助,我将不胜感激!
谢谢。
让我们来解决您的问题。
The arrow head (or multiple arrow heads) breaking when more than one arrow is added to the canvas - they get "stuck" for some reason.
这是因为你使用了一个单一的triangle
全局变量,这意味着实际上只能有一个三角形。虽然这很容易解决 - 只需将 triangle
替换为 line.triangle
,这样它就变成了它所属行的 属性。
例如而不是
triangle = new fabric.Triangle({
简单使用
line.triangle = new fabric.Triangle({
并因此替换
line.addChildRemove();
line.remove();
line.triangle.remove();
与
line.triangle.remove();
line.addChildRemove();
line.remove();
The arrow head not moving around the in the correct direction
这是您的方向逻辑的一个简单缺点。没有任何部分是真正错误的,只是它没有按照您的意愿进行。我将其修改为以下代码段:
object.addChild.to.forEach(function(line) {
var x = objectCenter.x;
var y = objectCenter.y;
var xdis = REC_WIDTH/2 + TRI_WIDTH/2;
var ydis = REC_HEIGHT/2 + TRI_HEIGHT/2;
var horizontal = Math.abs(x - line.x2) > Math.abs(y - line.y2);
line.set({
'x2': x + xdis * (horizontal ? (x < line.x2 ? 1 : -1) : 0),
'y2': y + ydis * (horizontal ? 0 : (y < line.y2 ? 1 : -1))
});
line.triangle.set({
'left': line.x2, 'top': line.y2,
'angle': calcArrowAngle(line.x1, line.y1, line.x2, line.y2)
});
});
基本思路是偏移或者 X 或者 Y坐标,基于两者的X或者Y距离对象更大。
Arrow head/line not moving around the object.
上面发布的代码片段中的另一个逻辑缺陷。使用 x2
和 y2
计算三角形是正确的,因为您将它的位置作为 target 矩形的基础。然而,对于该行,您希望根据 source 矩形的位置进行计算,因此您需要分别使用 x1
和 y1
。所以我们可以把上面的代码再改成:
object.addChild.to.forEach(function(line) {
var x = objectCenter.x;
var y = objectCenter.y;
var xdis = REC_WIDTH/2 + TRI_WIDTH/2;
var ydis = REC_HEIGHT/2 + TRI_HEIGHT/2;
var horizontal = Math.abs(x - line.x1) > Math.abs(y - line.y1);
line.set({
'x2': x + xdis * (horizontal ? (x < line.x1 ? 1 : -1) : 0),
'y2': y + ydis * (horizontal ? 0 : (y < line.y1 ? 1 : -1))
});
line.triangle.set({
'left': line.x2, 'top': line.y2,
'angle': calcArrowAngle(line.x1, line.y1, line.x2, line.y2)
});
});
为了获得更加无缝的体验,您还需要更改调用重新计算的方式。目前您只更新连接到您实际移动的矩形的那部分线,而不更新连接的另一半。
我通过用一个简单的单个 lines
数组替换 to
和 from
数组,并向每个行元素添加属性 fromObject
和 toObject
来实现这一点 - on每次更新,行的两端都会更新,如下所示:
if (object.addChild && object.addChild.lines) {
object.addChild.lines.forEach(function(line) {
var fcenter = line.fromObject.getCenterPoint(),
fx = fcenter.x,
fy = fcenter.y,
tcenter = line.toObject.getCenterPoint(),
tx = tcenter.x,
ty = tcenter.y,
xdis = REC_WIDTH/2 + TRI_WIDTH/2,
ydis = REC_HEIGHT/2 + TRI_HEIGHT/2,
horizontal = Math.abs(tx - line.x1) > Math.abs(ty - line.y1)
line.set({
'x1': fx,
'y1': fy,
'x2': tx + xdis * (horizontal ? (tx < line.x1 ? 1 : -1) : 0),
'y2': ty + ydis * (horizontal ? 0 : (ty < line.y1 ? 1 : -1)),
});
line.triangle.set({
'left': line.x2, 'top': line.y2,
'angle': calcArrowAngle(line.x1, line.y1, line.x2, line.y2)
});
});
}
除此之外,您还有一个未处理的用例:如果您 select 一个框,删除它然后单击“添加子项”,则会出现错误。您可以通过简单地在 addChild
函数的顶部测试 null
来防止这种情况,如下所示:
if(canvas.getActiveObject() == null)
{
return;
}
有了以上所有,我创建了 an updated fiddle。
多年后的更新:原来你可以拖动到 select 多个矩形,这将它们放在一个组中,你可以作为一个整体移动,这样做会在我的后面留下箭头上面的解决方案(如评论中的“sugars”所述)。解决方案是将更新代码嵌入到它自己的函数中,并递归调用该事件的目标对象的每个对象是一个组。然后你也 运行 进入对象坐标相对于组中心的问题,所以你也必须为此进行调整。我也修复了这个问题 updated fiddle。
新的更新函数代码为:
function addChildMoveLine(event) {
canvas.on(event, function(options) {
if((function updateObject(object, offset) {
// update child objects, if any
if (object.getObjects) {
var off = object.getCenterPoint();
return object.getObjects().reduce(function(flag, obj) {
return updateObject(obj, off) || flag;
}, false);
}
if (!offset)
offset = { x: 0, y: 0 };
// otherwise udpate lines, if any
if (object.addChild && object.addChild.lines) {
object.addChild.lines.forEach(function(line) {
var fcenter = line.sourceObject.getCenterPoint(),
fx = fcenter.x + offset.x,
fy = fcenter.y + offset.y,
tcenter = line.targetObject.getCenterPoint(),
tx = tcenter.x + offset.x,
ty = tcenter.y + offset.y,
xdis = REC_WIDTH/2 + TRI_WIDTH/2,
ydis = REC_HEIGHT/2 + TRI_HEIGHT/2,
horizontal = Math.abs(tx - line.x1) > Math.abs(ty - line.y1);
line.set({
'x1': fx,
'y1': fy,
'x2': tx + xdis * (horizontal ? (tx < line.x1 ? 1 : -1) : 0),
'y2': ty + ydis * (horizontal ? 0 : (ty < line.y1 ? 1 : -1)),
});
line.triangle.set({
'left': line.x2, 'top': line.y2,
'angle': calcArrowAngle(line.x1, line.y1, line.x2, line.y2)
});
});
return true; // re-render
}
return false; // no re-render needed
})(options.target)) {
canvas.renderAll();
}
});
}