【问题标题】:Issue on Drawing Multiple Circles on HTML5 Canvas在 HTML5 Canvas 上绘制多个圆圈的问题
【发布时间】:2014-07-03 07:45:49
【问题描述】:

能否请您进行look at this 演示,让我知道如何在画布上绘制多个坐标不同的圆圈,而无需重复一堆代码?

正如您在 Demo 和以下代码中看到的那样

var ctx = $('#canvas')[0].getContext("2d");
ctx.fillStyle = "#00A308";
ctx.beginPath();
ctx.arc(150, 50, 5, 0, Math.PI * 2, true);
ctx.arc(20, 85, 5, 0, Math.PI * 2, true);
ctx.arc(160, 95, 5, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fill();

我试图将它们放在ctx 下,但它不正确,所以我尝试使用 for 循环来创建 50 个点,但我在重复和添加类似 ctx.fill() 的代码时遇到问题;为他们所有人。 你能告诉我如何解决这个问题吗?

谢谢

【问题讨论】:

    标签: javascript jquery html html5-canvas


    【解决方案1】:

    不断创建和关闭新路径并不是一个好建议。

    您应该将所有相同样式的填充/笔触批处理在一起,并在一次绘制调用中执行它们。随着多边形数量的增加,这些方法之间的性能差异会很快变得明显。

    解决方法是移动笔并为每个圆圈进行路径构建调用;一次完成中风/填充。然而,这里有一个怪癖。当您将点移到中心并绘制圆时,您仍然会看到一条水平半径线,从圆的中心到圆周。

    为了避免这种伪影,我们不是移动到中心,而是移动到圆周。这会跳过半径绘图。基本上所有这些命令都是为了跟踪路径,如果不调用closePath,就无法描述不连续性;通常moveTo 会这样做,但 HTML5 canvas API 不会。这是一个简单的解决方法。

    const pi2 = Math.PI * 2;
    const radius = 5;
    ctx.fillStyle = '#00a308';
    ctx.beginPath();
    
    for( let i=0, l=coords.length; i < l; i++ )
    {
        const p = coords[i],
        x = p.x,
        y = p.y;
    
        ctx.moveTo( x + radius, y ); // This was the line you were looking for
        ctx.arc( x, y, radius, 0, pi2 );
    }
    
    // Finally, draw outside loop
    ctx.stroke();
    ctx.fill();
    

    另外值得考虑的是,使用转换,并绘制相对于本地参考框架的所有内容。

    ctx.fillStyle = '#00a308';
    ctx.beginPath();
    
    for( let i=0, l=coords.length; i < l; i++ )
    {
        const p = coords[i];
    
        ctx.save();
            ctx.translate( p.x + radius, p.y );
            ctx.moveTo( 0, 0 );
            ctx.arc( 0, 0, radius, 0, pi2 );
        ctx.restore();
    }
    
    ctx.stroke();
    ctx.fill();
    

    【讨论】:

    • 这是一个很好的建议。演示:jsfiddle.net/michaschwab/Lmuyqb27/3
    • 这是一个更好的答案,因为它允许批量绘制。我很困惑,因为似乎在 rect 的幕后执行了一个隐含的 moveTo(或其他一些不需要显式调用的机制),如果没有这个显式的moveTo,所有arc 调用都会加入绘制时在一起。
    • @thedarklord47 我继续在接受的答案中添加了一个性能更高的版本。虽然这确实是一种更好的方法,但我想指出,我的回答主要基于 OP 遇到的问题以及他们想要做的事情:“我在重复和添加 ctx.fill() 之类的代码时遇到问题; 对于所有这些。你能告诉我如何解决这个问题吗?".
    • @Adria 哪一种感觉更好,接近 #1 还是 #2?
    【解决方案2】:

    这是因为您没有关闭路径,使用fill() 或closePath() 将关闭路径,因此它不会尝试连接所有项目。 fill() 填充圆圈并关闭路径,以便我们可以使用它。您还需要使用beginPath(),以便它们彼此分开。这是你的三个圈子:

    var coords = [ [150,50], [20,85], [160,95] ];
    
    for(var i = 0; i < coords.length; i++){
        ctx.beginPath();
        ctx.arc(coords[i][0], coords[i][1], 5, 0, Math.PI * 2, true);
        ctx.fill();
    }
    

    为了不重复一堆代码并拥有唯一的坐标,将您的X 和Y 位置存储在一个数组中,并使用for 循环来遍历它。

    更新:

    实现相同效果的更有效方法是仅使用单个路径并使用moveTo(),而不是在绘制每个圆圈时创建新路径:

    ctx.beginPath();
    for(var i = 0; i < coords.length; i++){
        ctx.moveTo(coords[i][0], coords[i][1]);
        ctx.arc(coords[i][0], coords[i][1], 5, 0, Math.PI * 2, true);
    }
    ctx.fill();
    

    【讨论】:

    • 谢谢 Spencer,能否请您告诉我如何设置点的描边?
    • 你的意思是喜欢使用ctx.stroke()而不是ctx.fill()?
    • 我做到了,这是小提琴:jsfiddle.net/56jhq/2。我没有在示例代码中包含诸如获取ctx 之类的内容。
    • 下面阿德里亚的回答更好,更多路径 = 浪费时间。
    • @Gajen 否,但您可以将其添加到自己的画布中并检查鼠标位置是否与圆相交。例如here is a modification of the fiddle,它使用圆形对象和距离公式来检查交叉点。 (注意它显示在控制台日志中)
    【解决方案3】:
      ctx.beginPath();
      points.forEach(point => {
        ctx.moveTo( point.x, point.y );
        ctx.arc(point.x,point.y,1,0,Math.PI*2,false);
      });
      ctx.fill();
    

    【讨论】:

    • 虽然此代码可能会回答问题,但提供有关它如何和/或为什么解决问题的额外上下文将提高​​答案的长期价值。
    • 这比公认的答案具有更好的性能。它只调用一次fil()
    【解决方案4】:

    您可以使用 for 循环轻松创建多个圆圈。你真的只需要画一条弧线并每次都填充它。使用你的例子,你可以做这样的事情。

    var ctx = $('#canvas')[0].getContext("2d");
    ctx.fillStyle = "#00A308";
    for (var i = 0; i < 3; i++) {
      ctx.arc(50 * (i+1), 50 + 15 * i, 5, 0, Math.PI * 2, true);
      ctx.fill();
    }
    

    【讨论】:

    • 嗨 Zack,感谢您的回复,但也可以让我知道如何为每个圆圈设置边框?有没有办法为每个圆圈使用自定义坐标?再次感谢
    【解决方案5】:

    Example Fiddle 在循环中绘制的不同位置的许多圆圈。

    var canvas = document.getElementById('canvas');
    var ctx = canvas.getContext( '2d' );
    var cx = canvas.width/2;
    var cy = canvas.height/2;
    ctx.fillStyle = "#00A308";
    
    var total_circles = 50;
    var radius = 100;
    for(i = 0; i < total_circles; i++){
        var angle = i * 2 * Math.PI/total_circles;
        var x = cx + Math.cos(angle) * radius;
        var y = cy + Math.sin(angle) * radius;   
        ctx.beginPath();
        ctx.arc(x, y, 2, 0, Math.PI * 2, true);
        ctx.closePath();
        ctx.fill();
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-04-08
      • 1970-01-01
      • 2014-09-25
      • 1970-01-01
      • 1970-01-01
      • 2013-03-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多