Fabric.js中的箭头 [英] Arrows in fabricjs
问题描述
我正在尝试使用fabricjs创建箭头形状.到目前为止,我最好的方法是添加一条线和一个三角形,然后将它们组合成一个复合组.但是问题是,当我调整箭头的大小时,箭头会拉长,效果不好.
I'm trying to create an arrow shape using fabricjs. Thus far my best approach has been to add a line and a triangle and combine them into a composite group. The problem however is, when I resize the arrow, the arrow head gets stretched and its not a nice effect.
我要问的是,您将如何在fabricjs上创建一个箭头对象,该对象只能在不拉伸箭头的情况下纵向调整大小. http://jsfiddle.net/skela/j45czqge/
What I'm asking is, how would you go about creating an arrow object on fabricjs that can be resized lengthwise only without stretching the arrow head. http://jsfiddle.net/skela/j45czqge/
<html>
<head>
<script src='http://fabricjs.com/build/files/text,gestures,easing,parser,freedrawing,interaction,serialization,image_filters,gradient,pattern,shadow,node.js'></script> <meta charset="utf-8">
<style>
html,body
{
height: 100%; min-height:100%;
width: 100%; min-width:100%;
background-color:transparent;
margin:0;
}
button
{
height:44px;
margin:0;
}
</style>
</head>
<body>
<span id="dev">
<button id="draw_mode" onclick="toggleDraw()">Draw</button>
<button onclick="addRect()">Add Rect</button>
<button onclick="addCircle()">Add Circle</button>
<button onclick="addTriangle()">Add Triangle</button>
<button onclick="addLine()">Add Line</button>
<button onclick="addArrow()">Add Arrow</button>
<button onclick="clearCanvas()">Clear</button>
<button onclick="saveCanvas()">Save</button>
<button onclick="loadCanvas()">Load</button>
</span>
<span id="selected" style="visibility:hidden;">
<button onclick="removeSelected()">Remove</button>
</span>
<canvas id="c" style="border:1px solid #aaa;"></canvas>
<script>
fabric.Object.prototype.toObject = (function (toObject)
{
return function ()
{
return fabric.util.object.extend(toObject.call(this),
{
id:this.id,
});
};
})(fabric.Object.prototype.toObject);
fabric.LineArrow = fabric.util.createClass(fabric.Line, {
type: 'lineArrow',
initialize: function(element, options) {
options || (options = {});
this.callSuper('initialize', element, options);
},
toObject: function() {
return fabric.util.object.extend(this.callSuper('toObject'));
},
_render: function(ctx){
this.callSuper('_render', ctx);
// do not render if width/height are zeros or object is not visible
if (this.width === 0 || this.height === 0 || !this.visible) return;
ctx.save();
var xDiff = this.x2 - this.x1;
var yDiff = this.y2 - this.y1;
var angle = Math.atan2(yDiff, xDiff);
ctx.translate((this.x2 - this.x1) / 2, (this.y2 - this.y1) / 2);
ctx.rotate(angle);
ctx.beginPath();
//move 10px in front of line to start the arrow so it does not have the square line end showing in front (0,0)
ctx.moveTo(10,0);
ctx.lineTo(-20, 15);
ctx.lineTo(-20, -15);
ctx.closePath();
ctx.fillStyle = this.stroke;
ctx.fill();
ctx.restore();
}
});
fabric.LineArrow.fromObject = function (object, callback) {
callback && callback(new fabric.LineArrow([object.x1, object.y1, object.x2, object.y2],object));
};
fabric.LineArrow.async = true;
var canvas = new fabric.Canvas('c');
canvas.isDrawingMode = false;
canvas.freeDrawingBrush.width = 5;
setColor('red');
var sendToApp = function(_key, _val)
{
var iframe = document.createElement("IFRAME");
iframe.setAttribute("src", _key + ":##drawings##" + _val);
document.documentElement.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
iframe = null;
};
canvas.on('object:selected',function(options)
{
if (options.target)
{
//console.log('an object was selected! ', options.target.type);
var sel = document.getElementById("selected");
sel.style.visibility = "visible";
sendToApp("object:selected","");
}
});
canvas.on('selection:cleared',function(options)
{
//console.log('selection cleared');
var sel = document.getElementById("selected");
sel.style.visibility = "hidden";
sendToApp("selection:cleared","");
});
canvas.on('object:modified',function(options)
{
if (options.target)
{
//console.log('an object was modified! ', options.target.type);
sendToApp("object:modified","");
}
});
canvas.on('object:added',function(options)
{
if (options.target)
{
if (typeof options.target.id == 'undefined')
{
options.target.id = 1337;
}
//console.log('an object was added! ', options.target.type);
sendToApp("object:added","");
}
});
canvas.on('object:removed',function(options)
{
if (options.target)
{
//console.log('an object was removed! ', options.target.type);
sendToApp("object:removed","");
}
});
window.addEventListener('resize', resizeCanvas, false);
function resizeCanvas()
{
canvas.setHeight(window.innerHeight);
canvas.setWidth(window.innerWidth);
canvas.renderAll();
}
function color()
{
return canvas.freeDrawingBrush.color;
}
function setColor(color)
{
canvas.freeDrawingBrush.color = color;
}
function toggleDraw()
{
setDrawingMode(!canvas.isDrawingMode);
}
function setDrawingMode(isDrawingMode)
{
canvas.isDrawingMode = isDrawingMode;
var btn = document.getElementById("draw_mode");
btn.innerHTML = canvas.isDrawingMode ? "Drawing" : "Draw";
sendToApp("mode",canvas.isDrawingMode ? "drawing" : "draw");
}
function setLineControls(line)
{
line.setControlVisible("tr",false);
line.setControlVisible("tl",false);
line.setControlVisible("br",false);
line.setControlVisible("bl",false);
line.setControlVisible("ml",false);
line.setControlVisible("mr",false);
}
function createLine(points)
{
var line = new fabric.Line(points,
{
strokeWidth: 5,
stroke: color(),
originX: 'center',
originY: 'center',
lockScalingX:true,
//lockScalingY:false,
});
setLineControls(line);
return line;
}
function createArrowHead(points)
{
var headLength = 15,
x1 = points[0],
y1 = points[1],
x2 = points[2],
y2 = points[3],
dx = x2 - x1,
dy = y2 - y1,
angle = Math.atan2(dy, dx);
angle *= 180 / Math.PI;
angle += 90;
var triangle = new fabric.Triangle({
angle: angle,
fill: color(),
top: y2,
left: x2,
height: headLength,
width: headLength,
originX: 'center',
originY: 'center',
// lockScalingX:false,
// lockScalingY:true,
});
return triangle;
}
function addRect()
{
canvas.add(new fabric.Rect({left:100,top:100,fill:color(),width:50,height:50}));
}
function addCircle()
{
canvas.add(new fabric.Circle({left:150,top:150,fill:color(),radius:50/2}));
}
function addTriangle()
{
canvas.add(new fabric.Triangle({left:200,top:200,fill:color(),height:50,width:46}));
}
function addLine()
{
var line = createLine([100,100,100,200]);
canvas.add(line);
}
function addArrow()
{
var pts = [100,100,100,200];
var triangle = createArrowHead(pts);
var line = createLine(pts);
var grp = new fabric.Group([triangle,line]);
setLineControls(grp);
canvas.add(grp);
// var arrow = new fabric.LineArrow(pts,{left:100,top:100,fill:color()});
// setLineControls(arrow);
// canvas.add(arrow);
}
function removeSelected()
{
var grp = canvas.getActiveGroup();
var obj = canvas.getActiveObject();
if (obj!=null)
{
canvas.remove(obj);
}
if (grp!=null)
{
grp.forEachObject(function(o){ canvas.remove(o) });
canvas.discardActiveGroup().renderAll();
}
}
function clearCanvas()
{
canvas.clear();
}
function saveCanvas()
{
var js = JSON.stringify(canvas);
return js;
}
function loadCanvas()
{
var js = '{"objects":[{"type":"circle","originX":"left","originY":"top","left":150,"top":150,"width":50,"height":50,"fill":"red","stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","globalCompositeOperation":"source-over","id":1234,"radius":25,"startAngle":0,"endAngle":6.283185307179586}],"background":""}';
canvas.loadFromJSON(js);
}
resizeCanvas();
</script>
</body>
</html>
推荐答案
我遇到了同样的问题,最后进行了数学计算,以计算出围绕一条线的箭头形状的点,并改为使用多边形对象.
I had the same problem and ended up doing math to calculate the points that would make up an arrow shape around a line and using a polygon object instead.
它的核心看起来像:
var angle = Math.atan2(toy - fromy, tox - fromx);
var headlen = 15; // arrow head size
// bring the line end back some to account for arrow head.
tox = tox - (headlen) * Math.cos(angle);
toy = toy - (headlen) * Math.sin(angle);
// calculate the points.
var points = [
{
x: fromx, // start point
y: fromy
}, {
x: fromx - (headlen / 4) * Math.cos(angle - Math.PI / 2),
y: fromy - (headlen / 4) * Math.sin(angle - Math.PI / 2)
},{
x: tox - (headlen / 4) * Math.cos(angle - Math.PI / 2),
y: toy - (headlen / 4) * Math.sin(angle - Math.PI / 2)
}, {
x: tox - (headlen) * Math.cos(angle - Math.PI / 2),
y: toy - (headlen) * Math.sin(angle - Math.PI / 2)
},{
x: tox + (headlen) * Math.cos(angle), // tip
y: toy + (headlen) * Math.sin(angle)
}, {
x: tox - (headlen) * Math.cos(angle + Math.PI / 2),
y: toy - (headlen) * Math.sin(angle + Math.PI / 2)
}, {
x: tox - (headlen / 4) * Math.cos(angle + Math.PI / 2),
y: toy - (headlen / 4) * Math.sin(angle + Math.PI / 2)
}, {
x: fromx - (headlen / 4) * Math.cos(angle + Math.PI / 2),
y: fromy - (headlen / 4) * Math.sin(angle + Math.PI / 2)
},{
x: fromx,
y: fromy
}
];
然后从这些点创建一个多边形.
Then create a polygon from the points.
https://jsfiddle.net/6e17oxc3/
这篇关于Fabric.js中的箭头的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!