Gue*_*dio 5 javascript canvas fabricjs
我正在使用FabricJS来创建用于绘制特定线条和形状的画布.其中一条线是一条波浪线,箭头与此类似:
我已经用箭头端点成功创建了这个版本,但找不到任何如何创建波浪线的示例.用户可以根据需要绘制线条,因此线条中的"峰值"和"波谷"数量需要相应调整(如上图所示的短线可能有4个峰值但是长度为两倍的线条会有8个峰值,不仅仅是较短线的拉伸版本).
这是我用来绘制带箭头端点的直线的代码.请注意,该行的起点是在mousedown上绘制的,并且端点是在mouseup上绘制的.
import LineWithArrow from './LineWithArrow';
drawLineWithArrow = (item, points, color) => (
new LineWithArrow(points, {
customProps: item,
strokeWidth: 2,
stroke: color,
})
)
selectLine = (item, points) => {
switch (item.type) {
case 'line_with_arrow':
return this.drawLineWithArrow(item, points, colors.BLACK);
case 'wavy_line_with_arrow':
return this.drawWavyLineWithArrow(item, points);
// no default
}
return null;
}
let line;
let isDown;
fabricCanvas.on('mouse:down', (options) => {
isDown = true;
const pointer = fabricCanvas.getPointer(options.e);
const points = [pointer.x, pointer.y, pointer.x, pointer.y];
line = this.selectLine(item, points);
fabricCanvas
.add(line)
.setActiveObject(line)
.renderAll();
});
fabricCanvas.on('mouse:move', (options) => {
if (!isDown) return;
const pointer = fabricCanvas.getPointer(options.e);
line.set({ x2: pointer.x, y2: pointer.y });
fabricCanvas.renderAll();
});
fabricCanvas.on('mouse:up', () => {
isDown = false;
line.setCoords();
fabricCanvas.setActiveObject(line).renderAll();
});
Run Code Online (Sandbox Code Playgroud)
和LineWithArrow文件:
import { fabric } from 'fabric';
const LineWithArrow = fabric.util.createClass(fabric.Line, {
type: 'line_with_arrow',
initialize(element, options) {
options || (options = {});
this.callSuper('initialize', element, options);
// Set default options
this.set({
hasBorders: false,
hasControls: false,
});
},
_render(ctx) {
this.callSuper('_render', ctx);
ctx.save();
const xDiff = this.x2 - this.x1;
const yDiff = this.y2 - this.y1;
const angle = Math.atan2(yDiff, xDiff);
ctx.translate((this.x2 - this.x1) / 2, (this.y2 - this.y1) / 2);
ctx.rotate(angle);
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)
ctx.moveTo(5, 0);
ctx.lineTo(-5, 5);
ctx.lineTo(-5, -5);
ctx.closePath();
ctx.fillStyle = this.stroke;
ctx.fill();
ctx.restore();
},
toObject() {
return fabric.util.object.extend(this.callSuper('toObject'), {
customProps: this.customProps,
});
},
});
export default LineWithArrow;
Run Code Online (Sandbox Code Playgroud)
我不是真正的专家,但我试图自己实现波浪线。
结果是这样的:
我用这个fabric.Group类来对构成我们波浪线的线条进行分组。
const WavyLineWithArrow = fabric.util.createClass(fabric.Group, {
/* ... */
};
Run Code Online (Sandbox Code Playgroud)
每次更改后,这些行将被删除并添加到对象中:
this.forEachObject(function(o) {
this.remove(o);
}, this);
for(var i=1;i<polyPoints.length;++i) {
this.add(new fabric.Line([
polyPoints[i-1].x,
polyPoints[i-1].y,
polyPoints[i].x,
polyPoints[i].y
], options));
}
Run Code Online (Sandbox Code Playgroud)
行尾的箭头也是一个对象:
this.add(new fabric.Polyline([
{x: len/2, y: -arrowSize/2},
{x: len/2 + arrowSize/2, y: 0},
{x: len/2, y: arrowSize/2},
{x: len/2, y: -arrowSize/2}
], arrOptions));
Run Code Online (Sandbox Code Playgroud)
所有艰巨的任务都是计算函数值、缩放等,但这只是无聊的几何。
我测试了我的波浪线实现,即使您支持其他功能(不是正弦),它似乎也能很好地工作。
我看到的只有一个问题,那就是在您的示例中,您从一个角到另一个角渲染了线条。
旋转波浪线没什么大不了的,但这就是我注意到的理想解决方案的所有差异。
我做了以下漂亮的箭头:
// Default: sine
null
// Custom: tangens
[
function(x) { return Math.max(-10, Math.min(Math.tan(x/2) / 3, 10)); },
4 * Math.PI
]
// Custom: Triangle function
[
function(x) {
let g = x % 6;
if(g<=3) return g*5;
if(g>3) return (6-g)*5;
},
6
]
// Custom: Square function
[
function(x) {
let g = x % 6;
if(g<=3) return 15;
if(g>3) return -15;
},
6
]
Run Code Online (Sandbox Code Playgroud)
下面我附上我剪下的工作波浪线。
您还可以在codepen.io上查看该片段
const WavyLineWithArrow = fabric.util.createClass(fabric.Group, {
/* ... */
};
Run Code Online (Sandbox Code Playgroud)
this.forEachObject(function(o) {
this.remove(o);
}, this);
for(var i=1;i<polyPoints.length;++i) {
this.add(new fabric.Line([
polyPoints[i-1].x,
polyPoints[i-1].y,
polyPoints[i].x,
polyPoints[i].y
], options));
}
Run Code Online (Sandbox Code Playgroud)
由于我们从两个角绘制一条线,因此您可以在_render自定义类的方法中绘制波浪线。从最后我画一条线到 mid ,以显示它与箭头的连接。
演示
var line, isDown, evented;
var canvas = new fabric.Canvas('canvas', {
perPixelTargetFind: true
});
draw();
function selection() {
changeObjSelection(true);
canvas.off('mouse:down');
canvas.off('mouse:move');
canvas.off('mouse:up');
evented = false;
}
function draw() {
changeObjSelection(false);
if (!evented) {
canvas.on('mouse:down', onMouseDown);
canvas.on('mouse:move', onMouseMove);
canvas.on('mouse:up', onMouseUp);
evented = true;
}
}
function clearCanvas() {
canvas.clear();
}
function changeObjSelection(value) {
canvas.selection = value;
canvas.forEachObject(function(obj) {
obj.selectable = value;
})
canvas.requestRenderAll();
}
function onMouseDown(options) {
isDown = true;
var pointer = canvas.getPointer(options.e);
var points = [pointer.x, pointer.y, pointer.x, pointer.y];
line = selectLine(points);
canvas.add(line);
}
function onMouseMove(options) {
if (!isDown) return;
var pointer = canvas.getPointer(options.e);
line.set({
x2: pointer.x,
y2: pointer.y
});
canvas.renderAll();
}
function onMouseUp(options) {
isDown = false;
line.setCoords();
canvas.requestRenderAll();
}
function drawLineWithArrow(points, color) {
return new fabric.LineWithArrow(points, {
strokeWidth: 2,
stroke: color,
objectCaching: false,
selectable: false
})
}
function selectLine(points) {
return drawLineWithArrow(points, 'black');
}
//Wavy line
(function(global) {
'use strict';
if (fabric.LineWithArrow) {
fabric.warn('fabric.LineWithArrow is already defined.');
return;
}
var clone = fabric.util.object.clone;
fabric.LineWithArrow = fabric.util.createClass(fabric.Line, {
type: 'lineWithArrow',
initialize: function(element, options) {
options || (options = {});
this.callSuper('initialize', element, options);
// Set default options
this.set({
hasBorders: false,
hasControls: false,
});
},
_render: function(ctx) {
// this.callSuper('_render', ctx);
ctx.save();
const xDiff = this.x2 - this.x1;
const yDiff = this.y2 - this.y1;
const angle = Math.atan2(yDiff, xDiff);
ctx.translate(xDiff / 2, yDiff / 2);
ctx.rotate(angle);
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)
ctx.moveTo(5, 0);
ctx.lineTo(-5, 5);
ctx.lineTo(-5, -5);
ctx.closePath();
ctx.fillStyle = this.stroke;
ctx.fill();
ctx.restore();
var p = this.calcLinePoints();
var point = this.pointOnLine(this.point(p.x2, p.y2), this.point(p.x1, p.y1), 10)
this.wavy(this.point(p.x1, p.y1), point, this.point(p.x2, p.y2), ctx);
ctx.stroke();
},
point: function(x, y) {
return {
x: x,
y: y
};
},
wavy: function(from, to, endPoint, ctx) {
var cx = 0,
cy = 0,
fx = from.x,
fy = from.y,
tx = to.x,
ty = to.y,
i = 0,
step = 4,
waveOffsetLength = 0,
ang = Math.atan2(ty - fy, tx - fx),
distance = Math.sqrt((fx - tx) * (fx - tx) + (fy - ty) * (fy - ty)),
amplitude = -10,
f = Math.PI * distance / 30;
for (i; i <= distance; i += step) {
waveOffsetLength = Math.sin((i / distance) * f) * amplitude;
cx = from.x + Math.cos(ang) * i + Math.cos(ang - Math.PI / 2) * waveOffsetLength;
cy = from.y + Math.sin(ang) * i + Math.sin(ang - Math.PI / 2) * waveOffsetLength;
i > 0 ? ctx.lineTo(cx, cy) : ctx.moveTo(cx, cy);
}
ctx.lineTo(to.x, to.y);
ctx.lineTo(endPoint.x, endPoint.y);
},
pointOnLine: function(point1, point2, dist) {
var len = Math.sqrt(((point2.x - point1.x) * (point2.x - point1.x)) + ((point2.y - point1.y) * (point2.y - point1.y)));
var t = (dist) / len;
var x3 = ((1 - t) * point1.x) + (t * point2.x),
y3 = ((1 - t) * point1.y) + (t * point2.y);
return new fabric.Point(x3, y3);
},
toObject: function() {
return fabric.util.object.extend(this.callSuper('toObject'), {
customProps: this.customProps,
});
},
});
fabric.LineWithArrow.fromObject = function(object, callback) {
function _callback(instance) {
delete instance.points;
callback && callback(instance);
};
var options = clone(object, true);
options.points = [object.x1, object.y1, object.x2, object.y2];
fabric.Object._fromObject('LineWithArrow', options, _callback, 'points');
};
})(typeof exports !== 'undefined' ? exports : this);Run Code Online (Sandbox Code Playgroud)
canvas {
border: 2px dotted black;
}Run Code Online (Sandbox Code Playgroud)
<script src="https://rawgit.com/kangax/fabric.js/master/dist/fabric.js"></script>
<button type="button" onclick="selection()">selection</button>
<button type="button" onclick="draw()">draw</button>
<button type="button" onclick="clearCanvas()">clear</button>
<canvas id="canvas" width="400" height="400"></canvas>Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
947 次 |
| 最近记录: |