在fabricjs中的箭头

时间:2015-07-06 04:55:14

标签: javascript fabricjs

我尝试使用fabricjs创建箭头形状。到目前为止,我最好的方法是添加一条线和一个三角形,并将它们组合成一个复合组。然而问题是,当我调整箭头大小时,箭头会被拉伸并且效果不佳。

我要问的是,你如何在fabricjs上创建一个箭头对象,只有在不拉伸箭头的情况下才能纵向调整大小。 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>
&#13;
&#13;
&#13;

2 个答案:

答案 0 :(得分:2)

我遇到了同样的问题,并最终做了数学运算来计算围绕一条线构成箭头形状并使用多边形对象的点。

它的核心看起来像:

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
    }
];

然后从点创建一个多边形。

https://jsfiddle.net/6e17oxc3/

答案 1 :(得分:0)

您可以做的是在拉伸对象后计算新尺寸,并在同一个确切区域上绘制另一个对象并删除前一个对象。

var obj = canvas.getActiveObject();
var width = obj.getWidth();
var height = obj.getHeight;
var top = obj.getTop();

现在,如果您只有一个被拉伸的对象,您可以简单地使用上面的数据在画布上绘制另一个看起来很漂亮的对象。 如果您有倍数,那么您需要获取所有这些数据并逐个绘制它们。