Javascript <canvas> 中的虚线笔划

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4576724/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 13:03:09  来源:igfitidea点击:

dotted stroke in <canvas>

javascripthtmlcanvas

提问by thenengah

I guess it is not possible to set stroke property such as CSS which is quite easy. With CSS we have dashed, dotted, solid but on canvas when drawing lines/or strokes this doesn't seem to be an option. How have you implemented this?

我想不可能设置像 CSS 这样的笔触属性,这很容易。使用 CSS 我们有虚线、虚线、实线,但在画布上绘制线条/或笔划时,这似乎不是一个选项。你是如何实施的?

I've seen some examples but they are really long for such a silly function.

我看过一些例子,但他们真的很期待这样一个愚蠢的功能。

For example:

例如:

http://groups.google.com/group/javascript-information-visualization-toolkit/browse_thread/thread/22000c0d0a1c54f9?pli=1

http://groups.google.com/group/javascript-information-visualization-toolkit/browse_thread/thread/22000c0d0a1c54f9?pli=1

回答by Phrogz

Fun question! I've written a custom implementation of dashed lines; you can try it out here. I took the route of Adobe Illustrator and allow you to specify an array of dash/gap lengths.

有趣的问题!我写了一个自定义的虚线实现;你可以在这里试试。我采用了 Adob​​e Illustrator 的路线,并允许您指定一系列短划线/间隙长度。

For stackoverflow posterity, here's my implementation (slightly altered for s/o line widths):

对于 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;
    }
  }
}

To draw a line from 20,150to 170,10with dashes that are 30px long followed by a gap of 10px, you would use:

要使用 30 像素长的短划线和 10 像素的间隙从20,150到绘制一条线170,10,您可以使用:

myContext.dashedLine(20,150,170,10,[30,10]);

To draw alternating dashes and dots, use (for example):

要绘制交替的破折号和点,请使用(例如):

myContext.lineCap   = 'round';
myContext.lineWidth = 4; // Lines 4px wide, dots of diameter 4
myContext.dashedLine(20,150,170,10,[30,10,0,10]);

The "very short" dash length of 0combined with the rounded lineCap results in dots along your line.

“非常短”的划线长度0与圆形 lineCap 相结合,会沿着您的线条产生点。

If anyone knows of a way to access the current point of a canvas context path, I'd love to know about it, as it would allow me to write this as ctx.dashTo(x,y,dashes)instead of requiring you to re-specify the start point in the method call.

如果有人知道访问画布上下文路径当前点的方法,我很想知道它,因为它允许我将其编写为ctx.dashTo(x,y,dashes)而不是要求您在方法调用中重新指定起点.

回答by Rod MacDougall

This simplified version of Phrogz's code utilises the built-in transformation functionality of Canvas and also handles special cases e.g. when dx = 0

Phrogz 代码的简化版本利用了 Canvas 的内置转换功能,还处理了特殊情况,例如当 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();
    }
}

I think my calculations are correct and it seems to render OK.

我认为我的计算是正确的,它似乎呈现正常。

回答by Marconius

At the moment at least setLineDash([5,10]) works with Chrome and ctx.mozDash = [5,10] works with FF:

目前至少 setLineDash([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();

Setting to null makes the line solid.

设置为 null 使线条成为实线。

回答by robertc

Mozilla has been working on an implementation of dashed strokingfor canvas, so we may see it added to the spec in the near future.

Mozilla 一直致力于为画布实现虚线描边,因此我们可能会在不久的将来将其添加到规范中。

回答by saiya_moebius

Phroz's solution is great. But when I used it in my application, I found two bugs.

Phroz 的解决方案很棒。但是当我在我的应用程序中使用它时,我发现了两个错误。

Following code is debugged (and refactored for readability) version of Phroz's one.

以下代码是 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++;
    }
}

回答by jcfrei

There's a much simpler way to do this. According to http://www.w3.org/TR/2dcontext/#dom-context-2d-strokestylestrokeStyle accepts strings, CanvasGradients, or CanvasPatterns. So we just take an image like this:

有一种更简单的方法可以做到这一点。根据http://www.w3.org/TR/2dcontext/#dom-context-2d-strokestylestrokeStyle 接受字符串、CanvasGradients 或 CanvasPatterns。所以我们只需要这样一张图片:

  <img src="images/dashedLineProto.jpg" id="cvpattern1" width="32" height="32" />

load it into a canvas, and draw our little rectangle with it.

将它加载到画布中,并用它绘制我们的小矩形。

  var img=document.getElementById("cvpattern1");
  var pat=ctx.createPattern(img,"repeat");
  ctx.strokeStyle = pat;
  ctx.strokeRect(20,20,150,100);

that doesnt result in a perfect dashed line, but it's really straightforward and modifiable. Results may of course become imperfect when you're drawing lines which arent horizontal or vertical, a dotted pattern might help there.

这不会产生完美的虚线,但它非常简单且可修改。当您绘制非水平或垂直的线条时,结果当然可能会变得不完美,虚线图案可能会有所帮助。

PS. keep in mind SOP applies when you're trying to use imgs from external sources in your code.

附注。请记住,当您尝试在代码中使用来自外部来源的 imgs 时,SOP 适用。

回答by Dale

There is currently no support in HTML5 Canvas specification for dashed lines.

HTML5 Canvas 规范目前不支持虚线。

check this out:

看一下这个:

http://davidowens.wordpress.com/2010/09/07/html-5-canvas-and-dashed-lines/

http://davidowens.wordpress.com/2010/09/07/html-5-canvas-and-dashed-lines/

or

或者

Check out the Raphael JS Library:

查看 Raphael JS 库:

http://raphaeljs.com/

http://raphaeljs.com/

回答by pdschuller

Looks like context.setLineDash is pretty much implemented. See this.

看起来 context.setLineDash 已经实现了。看到这个

" context.setLineDash([5]) will result in a dashed line where both the dashes and spaces are 5 pixels in size. "

" context.setLineDash([5]) 将产生一条虚线,其中虚线和空格的大小均为 5 像素。"

回答by Jake

I made modified the dashedLinefunction to add support for offsetting. It utilizes native dashed lines if the browser supports ctx.setLineDashand ctx.lineDashOffset.

我修改了dashedLine函数以添加对偏移的支持。如果浏览器支持ctx.setLineDash和 ,它会使用本机虚线ctx.lineDashOffset

Example: http://jsfiddle.net/mLY8Q/6/

示例: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();
    };
}

回答by Ahtenus

There are support for it in Firefox at least

至少Firefox 中有对它的支持

ctx.mozDash = [5,10];

seems like ctx.webkitLineDashworked before, but they removed it because it had some compabillity issues.

似乎ctx.webkitLineDash以前工作过,但他们删除了它,因为它有一些兼容性问题

The W3C specssays ctx.setLineDash([5,10]);but it doesn't seem to be implemented yet anywhere.

W3C规范说,ctx.setLineDash([5,10]);但似乎并没有被任何地方尚未实施。