【问题标题】:Erasing previously drawn lines on an HTML5 canvas擦除 HTML5 画布上先前绘制的线条
【发布时间】:2011-09-09 17:45:05
【问题描述】:

为了玩转 HTML5 画布,我决定制作一个绘制模拟表盘的应用程序。一切都很好,只是旧行没有像我期望的那样被删除。我在下面包含了部分代码 - DrawHands() 每秒调用一次:

var hoursPoint = new Object();
var minutesPoint = new Object();
var secondsPoint = new Object();

function drawHands()
{
    var now = new Date();

    drawLine(centerX, centerY, secondsPoint.X, secondsPoint.Y, "white", 1);
    var seconds = now.getSeconds();
    secondsPoint = getOtherEndOfLine(centerX, centerY, 2 * Math.PI / 60 * seconds, 0.75 * radius);
    drawLine(centerX, centerY, secondsPoint.X, secondsPoint.Y, "black", 1);

    drawLine(centerX, centerY, minutesPoint.X, minutesPoint.Y, "white", 3);
    var minutes = now.getMinutes();
    minutesPoint = getOtherEndOfLine(centerX, centerY, 2 * Math.PI / 60 * minutes, 0.75 * radius);
    drawLine(centerX, centerY, minutesPoint.X, minutesPoint.Y, "black", 3);

    drawLine(centerX, centerY, hoursPoint.X, hoursPoint.Y, "white", 3);
    var hours = now.getHours();
    if (hours >= 12) { hours -= 12; } // Hours are 0-11
    hoursPoint = getOtherEndOfLine(centerX, centerY, (2 * Math.PI / 12 * hours) + (2 * Math.PI / 12 / 60 * minutes), 0.6 * radius);
    drawLine(centerX, centerY, hoursPoint.X, hoursPoint.Y, "black", 3);
}

为了理解上述内容,有两个辅助函数:

  • drawLine(x1, y1, x2, y2, 颜色, 粗细)
  • getOtherEndOfLine(x, y, 角度, 长度)

问题在于,虽然所有的手都按预期绘制成黑色,但它们永远不会被擦除。我希望由于同一条线是用白色(背景色)绘制的,它会有效地擦除之前在该点绘制的内容。但这似乎并非如此。

我缺少什么吗?

【问题讨论】:

    标签: javascript html html5-canvas


    【解决方案1】:

    与其删除你不想要的东西,你可以:

    1. 保存画布的状态
    2. 画出你不想要的东西
    3. 将画布恢复到保存状态以“擦除”它们

    这可以使用ImageData 轻松完成:

    var canvas = document.querySelector('canvas'),
        context = canvas.getContext('2d');
    
    context.fillStyle = 'blue';
    context.fillRect(0,0,200,200);
    
    // save the state of  the canvas here
    var imageData = context.getImageData(0,0,canvas.width,canvas.height);
    
    // draw a red rectangle that we'll get rid of in a second
    context.fillStyle = 'red';
    context.fillRect(50,50,100,100);
    
    setTimeout(function () {
        // return the canvas to the state right after we drew the blue rect
        context.putImageData(imageData, 0, 0);
    }, 1000);
    <canvas width=200 height=200>

    【讨论】:

    • 此方法比使用 clearRectangle 有一个优势,因为您可以在绘图的任何阶段获取 ImageData 并恢复到该级别,而 clearRectangle 将始终将您带回空白画布。出于这个原因,我投了赞成票。
    • ...如果需要恢复到,您甚至可以在绘图过程中的多个点拍摄快照。
    • 如果您将图像用作背景,这也可以让您不必重新加载图像。
    【解决方案2】:

    出于我可以扩展的原因,您应该考虑清除画布并完全重新绘制它,除非有性能或合成原因不这样做。

    你想要clearRect,像这样:

    //clear the canvas so we can draw a fresh clock
    ctx.clearRect(0, 0, canvasWidth, canvasHeight);
    
    //redraw your clock here
    /* ... */
    

    【讨论】:

    • 如果想要清除特定的线条或形状(如我所做的那样),而不是清除矩形区域,请设置 ctx.globalCompositeOperation = "destination-out" - 正如 andrewmu 对 similar question 的解决方案中所述 - - 几乎会让您的填充/描边操作就像橡皮擦工具一样。完成后记得将合成模式改回来。有关详细信息,请参阅Mozilla's compositing and clipping
    • 不必总是完全清除画布。请参阅 Simon Sarris 使用 getImageData / putImageData 的回答,它允许您将绘图恢复到绘图的任何先前阶段。
    • 对上述评论的更正-应该阅读mrmcgreg的答案。
    【解决方案3】:

    您不能只用白色重新绘制线条并希望它消除旧线条的原因是因为可能存在一些抗锯齿/出血。您还会注意到在一个像素上绘制的水平直线与在半像素上绘制的水平直线看起来非常不同。

    当您“擦除”白色线条时,请尝试使用较大的 lineWidth 大约 3 或 4 来绘制它们。这应该适合您的情况。

    您还应该先绘制所有白线,然后再绘制所有黑线,以防它们相交。

    【讨论】:

      【解决方案4】:

      清除画布的一种快速简便的方法是设置宽度:

      context.canvas.width = context.canvas.width;
      

      【讨论】:

      • 闻起来像是依赖于特定浏览器中的某些特定实现,这是在其他浏览器中未定义行为的一种简单方法。
      【解决方案5】:

      我的解决方案是双缓冲:

      var shapes = 
        [{type:"circle", x:50, y:50, radious:40, lineWidth:2, strokeStyle:"#FF0000", fillStyle:"#800000"}
        ,{type:"rectangle", x:50, y:50, width:100, height: 100, lineWidth:2, strokeStyle:"#00FF00", fillStyle:"#008000"}
        ,{type:"line", x1:75, y1:100, x2:170, y2:75, lineWidth:3, strokeStyle:"#0000FF"}
        ];
      
      step1();
      setTimeout(function () {
        step2();
        setTimeout(function () {
          step3();
        }, 1000);
      }, 1000);
      
      function step1() {
        clearCanvas('myCanvas1');
        shapes.forEach((sh) => { drawShape('myCanvas1', sh); });
      };
      
      function step2() {
        clearCanvas('myCanvas2');
        shapes.pop();
        shapes.forEach((sh) => { drawShape('myCanvas2', sh); });
        showOtherCanvas('myCanvas2', 'myCanvas1');
      };
      
      function step3() {
        clearCanvas('myCanvas1');
        shapes.pop();
        shapes.forEach((sh) => { drawShape('myCanvas1', sh); });
        showOtherCanvas('myCanvas1', 'myCanvas2');
      };
      
      function showOtherCanvas(cnv1, cnv2) {
        var c1 = document.getElementById(cnv1);
        var c2 = document.getElementById(cnv2);
        
        c1.style['z-index'] = 3;
        c2.style['z-index'] = 1;
        c1.style['z-index'] = 2;
      }
      
      function clearCanvas(canvasID) {
        var canvas = document.getElementById(canvasID);
        var ctx = canvas.getContext('2d');
        
        ctx.fillStyle="#FFFFFF";
        ctx.fillRect(0,0,480,320);
      } 
      
      function drawShape (canvasID, info) {
        switch (info.type) {
          case "line"      : drawLine(canvasID, info);
          case "rectangle" : drawRectangle(canvasID, info);
          case "circle"    : drawCircle(canvasID, info);
        }
      }
      
      function drawLine (canvasID, info) {
        var canvas = document.getElementById(canvasID);
        var ctx = canvas.getContext('2d');
        
        ctx.strokeStyle = info.strokeStyle;
        ctx.lineWidth = info.lineWidth
      
        ctx.beginPath();
        ctx.moveTo(info.x1, info.y1);
        ctx.lineTo(info.x2, info.y2);
        ctx.stroke();
      }
      
      function drawRectangle (canvasID, info) {
        var canvas = document.getElementById(canvasID);
        var ctx = canvas.getContext('2d');
        
        ctx.fillStyle = info.fillStyle;
        ctx.strokeStyle = info.strokeStyle;
        ctx.lineWidth = info.lineWidth
      
        ctx.fillRect(info.x, info.y, info.width, info.height);
        ctx.strokeRect(info.x, info.y, info.width, info.height);
      }
      
      function drawCircle (canvasID, info) {
        var canvas = document.getElementById(canvasID);
        var ctx = canvas.getContext('2d');
        
        ctx.fillStyle = info.fillStyle;
        ctx.strokeStyle = info.strokeStyle;
        ctx.lineWidth = info.lineWidth
      
        ctx.beginPath();
        ctx.arc(info.x, info.y, info.radious, 0, 2 * Math.PI);
        ctx.fill();
      
        ctx.beginPath();
        ctx.arc(info.x, info.y, info.radious, 0, 2 * Math.PI);
        ctx.stroke();
      }
      <canvas id="myCanvas2" width="480" height="320"
      	style="border: 1px solid #000000; position: absolute; top: 10; left: 10; z-index:1">
      </canvas>
      <canvas id="myCanvas1" width="480" height="320"
      	style="border: 1px solid #000000; position: absolute; top: 10; left: 10; z-index:2">
      </canvas>

      变化如此之快,你不会看到任何闪烁。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-14
        • 2020-04-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-09-09
        相关资源
        最近更新 更多