如何在HTML5的Canvas中清除特定行?

13

我完全不了解Canvas元素。我能够在画布上画线,但不能清除特定的线条,整个画布变成空白。

尝试过以下方法: HTML:

<canvas id="cvs" width="400" height="400"></canvas>
<hr />
<input type="submit" id="reDrowA" value="Draw A" />
<input type="submit" id="reDrowB" value="Draw B" />
<hr />
<input type="submit" id="clearA" value="Clear A" />
<input type="submit" id="clearB" value="Clear B" />

脚本

$(document).ready(function(){
    canvas =  document.getElementById("cvs");    
    $("#reDrowA").on("click",function(){
        a = canvas.getContext('2d');
        a.translate(0.5, 0.5);
        a.beginPath();
        a.setLineDash([2,10]);
        a.moveTo(10,10);
        a.lineTo(300,10);
        a.lineTo(300,300);
        a.stroke();
    });
    $("#reDrowB").on("click",function(){
        b = canvas.getContext('2d');
        b.translate(0.5, 0.5);
        b.beginPath();
        b.setLineDash([2,10]);
        b.moveTo(10,10);
        b.lineTo(10,300);
        b.lineTo(300,300);
        b.stroke();
    });
    $("#clearA").on("click",function(){
       a.clearRect(0, 0, canvas.width, canvas.height);
    });
    $("#clearB").on("click",function(){
        b.clearRect(0, 0, canvas.width, canvas.height);
    });

});

Fiddle: http://jsfiddle.net/8YNvu/


3
据我所知,在画布中无法访问特定元素,您必须完全清除它。我不知道您的要求是什么,但如果您正在绘制像图表之类的东西并且想要访问单个项目、响应其事件等,请查看 SVG... - T J
3
你不能这样做。画布是一个位图。你在上面绘制的所有东西都会留在那里。你无法随后操纵特定的绘画。你只能整体清除它。如果你想在画布上绘制“对象”,并能够随后操纵和删除它们,你需要自己跟踪这些对象(在数组中),然后有一个绘制函数定期更新画布并绘制数组中所有当前的对象。要清除特定的对象,只需从数组中清除并重新绘制即可。 - HaukurHaf
那么,为了管理两行,我需要创建两个画布元素吗? - Dharam Mali
1
如果您想管理多条线路,您需要使用多个层(画布)或者(如上面HaukurHaf所说的评论)注册您绘制的每一条线路,并在清除所有内容后重新绘制您需要的线路。 - ViliusL
我们无法访问画布中的元素,必须清除并重新绘制所有画布元素。 - lokeshpahal
为什么您在同一个画布上获得了2个上下文?我认为您想要有两个“独立的”画布。 - XCS
3个回答

17

关于Canvas、Canvas元素以及“元素”可见性...

当Canvas上的任何元素需要更改(移动、擦除等)时,标准方法是完全擦除Canvas并重新绘制Canvas,使元素处于新位置(如果已被擦除,则不重新绘制元素)。

这是因为Canvas不“记住”它绘制任何单个元素的位置,因此无法单独移动或删除任何元素。

您需要“记住”关于元素的足够信息,在Canvas被擦除后重新绘制它。

演示:http://jsfiddle.net/m1erickson/Wrk2e/

因此,在您的示例中,您可以创建javascript对象ab来表示您的右上角和左下角线路径。

每个对象都将具有定义其线路径的点和指示其是否可见的标志(visible == 在Canvas上重新绘制)。

// create an object containing the top-right lines
// the object contains its path points & if it is visible or not
var a={
  path:[10,10, 300,10, 300,300],
  isVisible:false,
}

// create an object containing the left-bottom lines
// the object contains its path points & if it is visible or not
var b={
  path:[10,10, 10,300, 300,300],
  isVisible:false,
}

为了方便处理,您可以将所有的线路径对象放入一个数组中:

// an array containing all the line-path objects
var myObjects=[a,b];

当你清除画布时,只需使用每个对象的线路径信息重新绘制线条。如果特定对象的可见性标志为false,则不要重新绘制该特定对象。

// clear the entire canvas 
// redraw any line-paths that are visible
function redrawAll(myObjects){
    context.clearRect(0,0,canvas.width,canvas.height);
    for(var i=0;i<myObjects.length;i++){
        if(myObjects[i].isVisible){
            drawLinePath(myObjects[i]);
        }
    }
}

// redraw 1 line-path
function drawLinePath(theObject){
    var points=theObject.path;
    // save the current untranslated context state
    context.save();

    // draw lines through each point in the objects path
    context.translate(0.5, 0.5);
    context.beginPath();
    context.setLineDash([2,10]);
    context.moveTo(points[0],points[1]);
    for(var i=2;i<points.length;i+=2){
        context.lineTo(points[i],points[i+1]);
    }
    context.stroke();

    // restore the context to its untranslated state
    context.restore();
}

在这一切都准备就绪后,您的按钮只需更改特定线路路径对象的可见性标志,然后清除/重绘整个画布。

// use buttons to set & clear the visibility flags on objects
// In all cases, clear the entire canvas and redraw any visible objects

$("#reDrowA").on("click",function(){
    a.isVisible=true;
    redrawAll(myObjects);
});
$("#reDrowB").on("click",function(){
    b.isVisible=true;
    redrawAll(myObjects);
});
$("#clearA").on("click",function(){
    a.isVisible=false;
    redrawAll(myObjects);
});
$("#clearB").on("click",function(){
    b.isVisible=false;
    redrawAll(myObjects);
});

我认为这是我正在寻找的完美解决方案。 - Dharam Mali
2
为了完整起见,如果想要清除特定的线条或形状(就像我所做的那样),而不是矩形区域,可以设置 context.globalCompositeOperation = "destination-out" -- 正如 andrewmu 在 类似问题 中提到的那样 -- 这将基本上使您的填充/描边操作就像橡皮擦工具一样。请参阅此合成和剪辑教程。但这并不能删除分层元素。 - TheMadDeveloper
1
@TheMadDeveloper。您提到了一个好的跟进点。请记住,使用destination-out混合模式来清除描边线条有时是无效的,因为画布添加的抗锯齿通常不会被擦除。但对于context.fill,destination-out效果很好。 :-) - markE
ReactJS通过其持续渲染解决了这个问题吗? - Sapinder Singh

3
< p>画布是透明的。在单个画布标签中不可能实现。因为clearRect功能是基于宽度和高度清除的,我们没有给出清除画布的确切位置。尝试使用两个画布标签来实现该场景。

Fiddle


谢谢,我完全明白了。 - Dharam Mali
@sudharsan。处理像canvas中的“元素”(如线路径)有一种更好的方式(和更标准的方式)。请看我的帖子。;-) - markE

0

我正在创建一些流程图类型的功能,所以我猜你提供的建议可能不适用。用户可以在其中创建N条线和对象。在这种情况下,这将行不通。如果我错了,请纠正我。 - Dharam Mali

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接