【问题标题】:Drawing a dot on HTML5 canvas [duplicate]在 HTML5 画布上绘制一个点 [重复]
【发布时间】:2011-12-10 09:10:29
【问题描述】:

使用 context.moveTo()context.lineTo() 函数在 HTML5 画布上画线非常简单。

我不太确定是否可以绘制一个点,即为单个像素着色。 lineTo 函数不会绘制一条像素线(很明显)。

有没有办法做到这一点?

【问题讨论】:

    标签: html canvas


    【解决方案1】:

    如果你打算绘制大量的像素,使用画布的图像数据进行像素绘制会效率更高。

    var canvas = document.getElementById("myCanvas");
    var canvasWidth = canvas.width;
    var canvasHeight = canvas.height;
    var ctx = canvas.getContext("2d");
    var canvasData = ctx.getImageData(0, 0, canvasWidth, canvasHeight);
    
    // That's how you define the value of a pixel
    function drawPixel (x, y, r, g, b, a) {
        var index = (x + y * canvasWidth) * 4;
        
        canvasData.data[index + 0] = r;
        canvasData.data[index + 1] = g;
        canvasData.data[index + 2] = b;
        canvasData.data[index + 3] = a;
    }
    
    // That's how you update the canvas, so that your
    // modification are taken in consideration
    function updateCanvas() {
        ctx.putImageData(canvasData, 0, 0);
    }
    

    那么,你可以这样使用它:

    drawPixel(1, 1, 255, 0, 0, 255);
    drawPixel(1, 2, 255, 0, 0, 255);
    drawPixel(1, 3, 255, 0, 0, 255);
    updateCanvas();
    

    有关更多信息,您可以查看这篇 Mozilla 博客文章:http://hacks.mozilla.org/2009/06/pushing-pixels-with-canvas/

    【讨论】:

    • 如果你把这段代码放在 标签中的 部分,为什么这段代码不起作用。如果我把它放在正文中就可以了,但我喜欢将所有脚本代码放在 HTML 的 部分中。
    • @DougHauf 确保它在页面加载/domcontentready 之后执行,否则画布元素将不会被定义。
    • 相反,如果你绘制几个像素,这会更慢。 :(
    • 真是天才,干得好!
    • 我认为将整个颜色作为单个十六进制数字/颜色常量传递比三个颜色值更有效和直观。
    【解决方案2】:

    看起来很奇怪,但是 HTML5 支持绘制直线、圆形、矩形和许多其他基本形状,它没有任何适合绘制基本点的东西。这样做的唯一方法是用你拥有的任何东西来模拟一个点。

    所以基本上有3种可能的解决方案:

    • 将点画成线
    • 将点绘制为多边形
    • 将点画成圆形

    它们各有缺点。


    线

    function point(x, y, canvas){
      canvas.beginPath();
      canvas.moveTo(x, y);
      canvas.lineTo(x+1, y+1);
      canvas.stroke();
    }
    

    请记住,我们正在向东南方向绘制,如果这是边缘,则可能会出现问题。但你也可以画在任何其他方向。


    矩形

    function point(x, y, canvas){
      canvas.strokeRect(x,y,1,1);
    }
    

    或者以更快的方式使用 fillRect,因为渲染引擎只会填充一个像素。

    function point(x, y, canvas){
      canvas.fillRect(x,y,1,1);
    }
    

    圈子

    圆圈的一个问题是引擎更难渲染它们

    function point(x, y, canvas){
      canvas.beginPath();
      canvas.arc(x, y, 1, 0, 2 * Math.PI, true);
      canvas.stroke();
    }
    

    与使用填充实现的矩形相同。

    function point(x, y, canvas){
      canvas.beginPath();
      canvas.arc(x, y, 1, 0, 2 * Math.PI, true);
      canvas.fill();
    }
    

    所有这些解决方案的问题:

    • 很难跟踪您要绘制的所有点。
    • 放大后看起来很难看

    如果您想知道,绘制点的最佳方法是什么,我会选择填充矩形。你可以看到我的jsperf here with comparison tests

    【讨论】:

    • 感谢您承认这很奇怪。我认为这种疯狂始于 OpenGL 和纹理,像素的概念被抛到了窗外,再也没有任何意义了。绘制 1x1 矩形并在想要填充像素时必须指定宽度、高度是奇怪且不自然的。
    • 如果你习惯了 OpenGL,那就不是 :)
    • 不错的答案。画布提示很难获得。
    • canvas.beginPath();抛出错误。 beginPath 是上下文的函数: var context = canvas.getContext('2d');
    【解决方案3】:

    上面声称“如果您打算绘制大量像素,使用画布的图像数据进行像素绘制会更有效”似乎是非常错误的 - 至少对于 Chrome 31.0.1650.57 m 或取决于您对“大量像素”的定义。我更愿意直接对相应的帖子发表评论 - 但不幸的是,我还没有足够的 stackoverflow 点数:

    我认为我正在绘制“很多像素”,因此我首先遵循了相应的建议以获得良好的测量结果,后来我将我的实现更改为每个绘制点的简单 ctx.fillRect(..),请参阅http://www.wothke.ch/webgl_orbittrap/Orbittrap.htm

    有趣的是,在我的示例中,愚蠢的 ctx.fillRect() 实现实际上至少是基于 ImageData 的双缓冲方法的两倍。

    至少在我的场景中,内置的 ctx.getImageData/ctx.putImageData 实际上慢得令人难以置信。 (知道在基于 ImageData 的方法可能领先之前需要触摸的像素百分比会很有趣..)

    结论:如果您需要优化性能,您必须分析您的代码并根据您的发现采取行动..

    【讨论】:

    • 我很想知道在什么情况下 fillRect 更好,而 getImageData 很慢。如果你以这个 JSPerf :jsperf.com/canvas-pixel-painting 为例,getImageData/putImageData 会好很多。
    • 也许像素是使用 getImageData/putImageData 为每个像素绘制的?这或许可以解释。
    【解决方案4】:

    在我的 Firefox 中,这个技巧有效:

    function SetPixel(canvas, x, y)
    {
      canvas.beginPath();
      canvas.moveTo(x, y);
      canvas.lineTo(x+0.4, y+0.4);
      canvas.stroke();
    }
    

    小偏移在屏幕上不可见,但会强制渲染引擎实际绘制一个点。

    【讨论】:

    • 为什么是 0.4?为什么不说,0.5?
    • 因为 0.5 可以四舍五入为 1?我想这是特定于浏览器的。
    【解决方案5】:

    这应该可以完成工作

    //get a reference to the canvas
    var ctx = $('#canvas')[0].getContext("2d");
    
    //draw a dot
    ctx.beginPath();
    ctx.arc(20, 20, 10, 0, Math.PI*2, true);
    ctx.closePath();
    ctx.fill();
    

    【讨论】:

    • getContent("2d") 是否必须在代码中,或者是否可以是 3d。是什么让 2d 引用画布。
    • 这是正确答案。谢谢0。
    【解决方案6】:

    出于性能原因,如果可以避免,请不要画圆。只需画一个宽高为 1 的矩形:

    ctx.fillRect(10,10,1,1); // fill in the pixel at (10,10)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-02-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多