以前,我是在Line中使用FabricJS类来允许用户在画布上绘制线条。现在,我需要实现曲线,但是由于Fabric的Line类不支持二次曲线,因此我重新编写了代码,改为使用Path类。
以前,画一条线时,如果在画布上移动该线,x1,y1,x2和y2值将自动更新,这使得更新我创建的起点和终点锚点的位置变得容易(只是可以使用与在Adobe Illustrator中操纵锚点相同的方式来操纵画布上的矩形。
Path类不使用x1,y1,x2或y2,而是传递一个字符串,该字符串将转换为数组,例如M 100 100, Q 200 200 500 500
。然后,这些值可用作path
对象的一部分(其中100 100是路径起点的x和y,200 200是曲线,而500 500是终点),这意味着我可以得到起点和终点的方式与Line类类似。
问题在于,移动线本身时,路径对象不会更新。 left
和top
值会更新,但是我发现很难根据此值手动更新路径值。我觉得我可能缺少一些明显的东西(例如,自动更新路径对象或获得增量/差值的功能,可以用来手动在对象上移动锚点:移动功能)。
下面的摘录摘自Fabric网站上的quadratic curve demo。如果您拖动实际线(而不是锚点),则可以看到锚点仍保持在原处。
(function() {
var canvas = this.__canvas = new fabric.Canvas('c', {
height: 563,
width: 1000,
});
fabric.Object.prototype.originX = fabric.Object.prototype.originY = 'center';
canvas.on({
'object:selected': onObjectSelected,
'object:moving': onObjectMoving,
'before:selection:cleared': onBeforeSelectionCleared
});
(function drawQuadratic() {
var line = new fabric.Path('M 100 100, Q 200 200 200 200', { fill: '', stroke: 'black', objectCaching: false });
console.log(line);
console.log(line);
line.selectable = true;
canvas.add(line);
var p1 = makeCurvePoint(200, 200, null, line, null)
p1.name = "p1";
canvas.add(p1);
var p0 = makeCurveCircle(100, 100, line, p1, null);
p0.name = "p0";
canvas.add(p0);
var p2 = makeCurveCircle(300, 100, null, p1, line);
p2.name = "p2";
canvas.add(p2);
})();
function makeCurveCircle(left, top, line1, line2, line3) {
var c = new fabric.Circle({
left: left,
top: top,
strokeWidth: 5,
radius: 12,
fill: '#fff',
stroke: '#666'
});
c.hasBorders = c.hasControls = false;
c.line1 = line1;
c.line2 = line2;
c.line3 = line3;
return c;
}
function makeCurvePoint(left, top, line1, line2, line3) {
var c = new fabric.Circle({
left: left,
top: top,
strokeWidth: 8,
radius: 14,
fill: '#fff',
stroke: '#666'
});
c.hasBorders = c.hasControls = false;
c.line1 = line1;
c.line2 = line2;
c.line3 = line3;
return c;
}
function onObjectSelected(e) {
var activeObject = e.target;
if (activeObject.name == "p0" || activeObject.name == "p2") {
activeObject.line2.animate('opacity', '1', {
duration: 200,
onChange: canvas.renderAll.bind(canvas),
});
activeObject.line2.selectable = true;
}
}
function onBeforeSelectionCleared(e) {
var activeObject = e.target;
if (activeObject.name == "p0" || activeObject.name == "p2") {
activeObject.line2.animate('opacity', '0', {
duration: 200,
onChange: canvas.renderAll.bind(canvas),
});
activeObject.line2.selectable = false;
}
else if (activeObject.name == "p1") {
activeObject.animate('opacity', '0', {
duration: 200,
onChange: canvas.renderAll.bind(canvas),
});
activeObject.selectable = false;
}
}
function onObjectMoving(e) {
if (e.target.name == "p0" || e.target.name == "p2") {
var p = e.target;
if (p.line1) {
p.line1.path[0][1] = p.left;
p.line1.path[0][2] = p.top;
}
else if (p.line3) {
p.line3.path[1][3] = p.left;
p.line3.path[1][4] = p.top;
}
}
else if (e.target.name == "p1") {
var p = e.target;
if (p.line2) {
p.line2.path[1][1] = p.left;
p.line2.path[1][2] = p.top;
}
}
else if (e.target.name == "p0" || e.target.name == "p2") {
var p = e.target;
p.line1 && p.line1.set({ 'x2': p.left, 'y2': p.top });
p.line2 && p.line2.set({ 'x1': p.left, 'y1': p.top });
p.line3 && p.line3.set({ 'x1': p.left, 'y1': p.top });
p.line4 && p.line4.set({ 'x1': p.left, 'y1': p.top });
}
}
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.21/fabric.min.js"></script>
<canvas id="c"></canvas>
答案 0 :(得分:3)
这可能不是一个完美的解决方案。您需要从路径对象计算路径的终点。然后将其设置为所有三个圆圈。而且,目前尚无法设置路径对象的路径,因此您需要使用从点开始计算的路径来创建新的路径对象。
演示
(function() {
var line;
var canvas = this.__canvas = new fabric.Canvas('c', {
selection: false,
perPixelTargetFind: true,
targetFindTolerance: 10
});
fabric.Object.prototype.originX = fabric.Object.prototype.originY = 'center';
canvas.on({
'object:moving': onObjectMoving,
'object:modified': reinitpath
});
line = new fabric.Path('M 100 100 Q 200, 200, 300, 100', {
fill: '',
stroke: 'black',
name: 'line',
hasControls: false,
hasBorders: false,
objectCaching:false
});
canvas.add(line);
var p1 = makeCurvePoint(200, 200, null, line, null)
p1.name = "p1";
canvas.add(p1);
var p0 = makeCurveCircle(100, 100, line, p1, null);
p0.name = "p0";
canvas.add(p0);
var p2 = makeCurveCircle(300, 100, null, p1, line);
p2.name = "p2";
canvas.add(p2);
function makeCurveCircle(left, top, line1, line2, line3) {
var c = new fabric.Circle({
left: left,
top: top,
strokeWidth: 5,
radius: 12,
fill: '#fff',
stroke: '#666'
});
c.hasBorders = c.hasControls = false;
c.line1 = line1;
c.line2 = line2;
c.line3 = line3;
return c;
}
function makeCurvePoint(left, top, line1, line2, line3) {
var c = new fabric.Circle({
left: left,
top: top,
strokeWidth: 8,
radius: 14,
fill: '#fff',
stroke: '#666'
});
c.hasBorders = c.hasControls = false;
setLineToCircle(c, line1, line2, line3)
return c;
}
function onObjectMoving(e) {
var p = e.target;
if (p.name == "p0" || p.name == "p2") {
if (p.line1) {
p.line1.path[0][1] = p.left;
p.line1.path[0][2] = p.top;
} else if (p.line3) {
p.line3.path[1][3] = p.left;
p.line3.path[1][4] = p.top;
}
} else if (p.name == "p1") {
if (p.line2) {
p.line2.path[1][1] = p.left;
p.line2.path[1][2] = p.top;
}
} else if (p.name == "line") {
var transformedPoints = getTransformedPoint(p);
p0.left = transformedPoints[0].x;
p0.top = transformedPoints[0].y;
p2.left = transformedPoints[1].x;
p2.top = transformedPoints[1].y;
p1.left = transformedPoints[2].x;
p1.top = transformedPoints[2].y;
}
}
function reinitpath(e) {
p0.setCoords();
p1.setCoords();
p2.setCoords();
canvas.remove(line);
var path = line.path;
if (e.target.name == 'line') {
var transformedPoints = getTransformedPoint(line);
path = [
[],
[]
];
path[0][0] = 'M';
path[0][1] = transformedPoints[0].x;
path[0][2] = transformedPoints[0].y;
path[1][0] = 'Q';
path[1][1] = transformedPoints[2].x;
path[1][2] = transformedPoints[2].y;
path[1][3] = transformedPoints[1].x;
path[1][4] = transformedPoints[1].y;
};
line = new fabric.Path(path, {
fill: '',
stroke: 'black',
name: 'line',
hasControls: false,
hasBorders: false,
objectCaching:false
});
canvas.add(line);
setLineToCircle(p1, null, line, null)
setLineToCircle(p0, line, p1, null);
setLineToCircle(p2, null, p1, line);
}
function getTransformedPoint(p) {
var points = [];
var path = p.path;
points.push(new fabric.Point(path[0][1], path[0][2]));
points.push(new fabric.Point(path[1][3], path[1][4]));
points.push(new fabric.Point(path[1][1], path[1][2]));
var matrix = line.calcTransformMatrix();
return points.map(function(p) {
return new fabric.Point(p.x - line.minX - line.width / 2, p.y - line.minY - line.height / 2);
})
.map(function(p) {
return fabric.util.transformPoint(p, matrix);
});
}
function setLineToCircle(circle, line1, line2, line3) {
circle.line1 = line1;
circle.line2 = line2;
circle.line3 = line3;
}
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.22/fabric.js"></script>
<canvas id="c" width="600" height="600"></canvas>