在<canvas>中加点划线
我想这是不可能的,如CSS设置笔画属性是很容易的。 使用CSS我们已经虚线,虚线,坚实,但在绘制线条/笔画canvas时,这似乎不是一个选项。 你是怎么实现的?
我已经看到了一些例子,但是对于这样一个愚蠢的function他们真的很长。
例如:
http://groups.google.com/group/javascript-information-visualization-toolkit/browse_thread/thread/22000c0d0a1c54f9?pli=1
有趣的问题! 我写了一个自定义的虚线实现; 你可以在这里试试 。 我采取了Adobe Illustrator的路线,并允许您指定一个破折号/间隙长度的数组。
对于stackoverflow的后代,这里是我的实现(略有改变的S / O线宽度):
var CP = window.CanvasRenderingContext2D && CanvasRenderingContext2D.prototype; if (CP && CP.lineTo){ CP.dashedLine = function(x,y,x2,y2,dashArray){ if (!dashArray) dashArray=[10,5]; if (dashLength==0) dashLength = 0.001; // Hack for Safari var dashCount = dashArray.length; this.moveTo(x, y); var dx = (x2-x), dy = (y2-y); var slope = dx ? dy/dx : 1e15; var distRemaining = Math.sqrt( dx*dx + dy*dy ); var dashIndex=0, draw=true; while (distRemaining>=0.1){ var dashLength = dashArray[dashIndex++%dashCount]; if (dashLength > distRemaining) dashLength = distRemaining; var xStep = Math.sqrt( dashLength*dashLength / (1 + slope*slope) ); if (dx<0) xStep = -xStep; x += xStep y += slope*xStep; this[draw ? 'lineTo' : 'moveTo'](x,y); distRemaining -= dashLength; draw = !draw; } } }
要绘制一条从170,10
到170,10
的线,其170,10
为30px,然后是10px的空隙,可以使用:
myContext.dashedLine(20,150,170,10,[30,10]);
要绘制交替的破折号和点,请使用(例如):
myContext.lineCap = 'round'; myContext.lineWidth = 4; // Lines 4px wide, dots of diameter 4 myContext.dashedLine(20,150,170,10,[30,10,0,10]);
“非常短”的短划线长度为0
并与圆angular线相结合,会得到沿着线条的点。
如果有人知道一种方法来访问一个canvas上下文path的当前点,我很想知道它,因为它可以让我把它写成ctx.dashTo(x,y,dashes)
而不是要求你在方法调用中重新指定起始点。
Phrogz代码的这个简化版本利用了Canvas的内置转换function,还处理特殊情况,例如,当dx = 0
var CP = window.CanvasRenderingContext2D && CanvasRenderingContext2D.prototype; if (CP.lineTo) { CP.dashedLine = function(x, y, x2, y2, da) { if (!da) da = [10,5]; this.save(); var dx = (x2-x), dy = (y2-y); var len = Math.sqrt(dx*dx + dy*dy); var rot = Math.atan2(dy, dx); this.translate(x, y); this.moveTo(0, 0); this.rotate(rot); var dc = da.length; var di = 0, draw = true; x = 0; while (len > x) { x += da[di++ % dc]; if (x > len) x = len; draw ? this.lineTo(x, 0): this.moveTo(x, 0); draw = !draw; } this.restore(); } }
我认为我的计算是正确的,而且似乎performance良好。
目前,至lesssetLineDash([5,10])适用于Chrome,而ctx.mozDash = [5,10]适用于FF:
var c=document.getElementById("myCanvas"); var ctx=c.getContext("2d"); if ( ctx.setLineDash !== undefined ) ctx.setLineDash([5,10]); if ( ctx.mozDash !== undefined ) ctx.mozDash = [5,10]; ctx.beginPath(); ctx.lineWidth="2"; ctx.strokeStyle="green"; ctx.moveTo(0,75); ctx.lineTo(250,75); ctx.stroke();
设置为null会使线条固定。
Mozilla一直在研究对canvas 进行虚线描边的实现,所以我们可能在不久的将来会看到它被添加到spec中。
Phroz的解决scheme是伟大的。 但是当我在应用程序中使用它时,发现了两个错误。
下面的代码被debugging(和重构的可读性)版本的Phroz的。
// Fixed: Minus xStep bug (when x2 < x, original code bugs) // Fixed: Vertical line bug (when abs(x - x2) is zero, original code bugs because of NaN) var CP = window.CanvasRenderingContext2D && CanvasRenderingContext2D.prototype; if(CP && CP.lineTo) CP.dashedLine = function(x, y, x2, y2, dashArray){ if(! dashArray) dashArray=[10,5]; var dashCount = dashArray.length; var dx = (x2 - x); var dy = (y2 - y); var xSlope = (Math.abs(dx) > Math.abs(dy)); var slope = (xSlope) ? dy / dx : dx / dy; this.moveTo(x, y); var distRemaining = Math.sqrt(dx * dx + dy * dy); var dashIndex = 0; while(distRemaining >= 0.1){ var dashLength = Math.min(distRemaining, dashArray[dashIndex % dashCount]); var step = Math.sqrt(dashLength * dashLength / (1 + slope * slope)); if(xSlope){ if(dx < 0) step = -step; x += step y += slope * step; }else{ if(dy < 0) step = -step; x += slope * step; y += step; } this[(dashIndex % 2 == 0) ? 'lineTo' : 'moveTo'](x, y); distRemaining -= dashLength; dashIndex++; } }
有一个更简单的方法来做到这一点。 根据http://www.w3.org/TR/2dcontext/#dom-context-2d-strokestyle strokeStyle接受string,CanvasGradients或CanvasPatterns。 所以我们只需要像这样的图像:
<img src="images/dashedLineProto.jpg" id="cvpattern1" width="32" height="32" />
将其加载到canvas中,并用它画出我们的小矩形。
var img=document.getElementById("cvpattern1"); var pat=ctx.createPattern(img,"repeat"); ctx.strokeStyle = pat; ctx.strokeRect(20,20,150,100);
这不会导致一个完美的虚线,但它是非常简单和可修改的。 当你绘制水平或垂直的线条时,结果当然可能会变得不完美,点状图案可能对此有帮助。
PS。 请记住,当您尝试在代码中使用来自外部源的imgs时,SOP适用。
HTML5 Canvas规范目前不支持虚线。
看一下这个:
http://davidowens.wordpress.com/2010/09/07/html-5-canvas-and-dashed-lines/
要么
检查出Raphael JS库:
至less在Firefox中有它的支持
ctx.mozDash = [5,10];
看起来像ctx.webkitLineDash
之前工作,但他们删除它,因为它有一些compabillity问题 。
W3C规范说ctx.setLineDash([5,10]);
但似乎并没有在任何地方实施。
我修改了dashedLine
函数来添加抵消的支持。 如果浏览器支持ctx.setLineDash
和ctx.lineDashOffset
它将使用本地虚线。
例如: http : //jsfiddle.net/mLY8Q/6/
var CP = window.CanvasRenderingContext2D && CanvasRenderingContext2D.prototype; if (CP.lineTo) { CP.dashedLine = CP.dashedLine || function (x, y, x2, y2, da, offset) { if (!da) da = [10, 5]; if (!offset) offset = 0; if (CP.setLineDash && typeof (CP.lineDashOffset) == "number") { this.save(); this.setLineDash(da); this.lineDashOffset = offset; this.moveTo(x, y); this.lineTo(x2, y2); this.restore(); return; } this.save(); var dx = (x2 - x), dy = (y2 - y); var len = Math.sqrt(dx * dx + dy * dy); var rot = Math.atan2(dy, dx); this.translate(x, y); this.moveTo(0, 0); this.rotate(rot); var dc = da.length; var di = 0; var patternLength = 0; for (var i = 0; i < dc; i++) { patternLength += da[i]; } if (dc % 2 == 1) { patternLength *= 2; } offset = offset % patternLength; if (offset < 0) { offset += patternLength; } var startPos = 0; var startSegment = 0; while (offset >= startPos) { if (offset >= startPos + da[startSegment % dc]) { startPos += da[startSegment % dc]; startSegment++; } else { offset = Math.abs(offset - startPos); break; } if (startSegment > 100) break; } draw = startSegment % 2 === 0; x = 0; di = startSegment; while (len > x) { var interval = da[di++ % dc]; if (x < offset) { interval = Math.max(interval - offset, 1); offset = 0; } x += interval; if (x > len) x = len; draw ? this.lineTo(x, 0) : this.moveTo(x, 0); draw = !draw; } this.restore(); }; }
看起来像context.setLineDash是非常实施。 看到这个
“context.setLineDash([5])将产生一个虚线,其中破折号和空格的大小都是5个像素。
我在Mozilla规范中find属性mozDash
和mozDashOffset
:
http://mxr.mozilla.org/mozilla-central/source/dom/interfaces/canvas/nsIDOMCanvasRenderingContext2D.idl
他们很可能用来控制破折号,但我没有使用它们。