【问题标题】:How to delete only a line from the canvas, not all the drawings?如何从画布中只删除一条线,而不是所有图纸?
【发布时间】:2015-04-17 06:45:50
【问题描述】:

我找到的解决方案是clear the whole canvas,但我只想删除画布上的一行而不是所有绘图。

我该怎么办?

【问题讨论】:

  • 和Qt有关系吗?
  • @Tay2510 是的,qml 也有一个画布,同样的答案也适用于那里。 Qml 是 qt 的一部分,因此其他人更容易搜索。
  • 请发布代码来演示您如何绘制以及在什么画布上。根本不清楚你在做什么。 html5画布可以修改,所以不太明白你的问题。

标签: javascript html qt canvas qml


【解决方案1】:

@danielfranca 说得对,画在画布上的线条变成了“画布的像素海洋中未记住的像素。”

他也说对了,在绘制每一行时保存画布的快照图像并恢复到其中一个保存的图像以“删除”一行是资源密集型的。 (不要使用那种技术!!)

但是,有一种有效的方法可以删除画布上以前绘制的线条!

是的,它确实会清除画布并重新绘制线条,但它非常快速且高效...我保证!

这里是如何做到这一点的大纲:

  • 像这样在对象内定义一行:{ x0:10, y0:15, x1:100, y1:75 }

  • 根据需要制作尽可能多的这些行并将它们推送到一个数组中:var lines=[];

  • 使用 lines[] 数组中的线条定义将线条绘制到画布上。

  • 监听mousemovemousedown 事件。

  • 在 mousemove 上,遍历行 [] 并找到离鼠标最近的行。这是计算哪条线最接近给定 [mx,my] 的算法的 sn-p:

    // Find the index of the line closest to mx,my
    function setClosestLine(mx,my) {
        //
        closestLineIndex=-1;
        var minDistanceSquared=100000000;
        //
        // examine each line & 
        // determine which line is closest to the mouse (mx,my)
        for(var i=0;i<lines.length;i++){
            var line=lines[i];
            var dx=line.x1-line.x0;
            var dy=line.y1-line.y0;
            var t=((mx-line.x0)*line.dx+(my-line.y0)*line.dy)/line.dx2dy2;
            var x=lerp(line.x0, line.x1, t);
            var y=lerp(line.y0, line.y1, t);
            var dx1=mx-x;
            var dy1=my-y;
            var distSquared=dx1*dx1+dy1*dy1;
            if(distSquared<minDistanceSquared){
                minDistanceSquared=distSquared;
                closestLineIndex=i;
                closestX=x;
                closestY=y;
            }
        }
    };
    
  • 在 mousedown 时,使用 lines.splice(targetIndex,1) 从 lines[] 数组中删除最近线的定义。然后清除画布并重新绘制剩余的线条。

这是带注释的代码和一个演示:

// canvas related variables
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
function reOffset(){
    var BB=canvas.getBoundingClientRect();
    offsetX=BB.left;
    offsetY=BB.top;        
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }


ctx.lineWidth=2;

// linear interpolation -- needed in setClosestLine()
var lerp=function(a,b,x){ return(a+x*(b-a)); };

// vars to track which line is closest to the mouse
var closestLineIndex=-1;
var closestX,closestY;

// make some random lines and save them in lines[]
var n=5;
var lines=[];
var randomX=function(){return(Math.random()*cw*.67);}
var randomY=function(){return(Math.random()*ch*.67);}
var lastX=randomX();
var lastY=randomY();
for(var i=0;i<n;i++){
    var x=Math.random()*cw*.67;
    var y=Math.random()*ch*.67;
    var dx=x-lastX;
    var dy=y-lastY;
    var line={
        x0:lastX,
        y0:lastY,
        x1:x,
        y1:y,
        weight:Math.round(Math.random()*20),
        // precalc often used values
        dx:dx,
        dy:dy,
        dx2dy2:dx*dx+dy*dy,
    };
    lines.push(line);
    lastX=x;
    lastY=y;
}


redraw();

$("#canvas").mousedown(function(e){handleMouseDown(e);});
$("#canvas").mousemove(function(e){handleMouseMove(e);});


//////////////////////////////
// functions

// Find the index of the line closest to mx,my
function setClosestLine(mx,my) {
    //
    closestLineIndex=-1;
    var minDistanceSquared=100000000;
    //
    // examine each line & 
    // determine which line is closest to the mouse (mx,my)
    for(var i=0;i<lines.length;i++){
        var line=lines[i];
        var dx=line.x1-line.x0;
        var dy=line.y1-line.y0;
        var t=((mx-line.x0)*line.dx+(my-line.y0)*line.dy)/line.dx2dy2;
        var x=lerp(line.x0, line.x1, t);
        var y=lerp(line.y0, line.y1, t);
        var dx1=mx-x;
        var dy1=my-y;
        var distSquared=dx1*dx1+dy1*dy1;
        if(distSquared<minDistanceSquared){
            minDistanceSquared=distSquared;
            closestLineIndex=i;
            closestX=x;
            closestY=y;
        }
    }
};

// clear & redraw all lines
function redraw(){
    
    // clear the canvas
    ctx.clearRect(0,0,cw,ch);
    
    // draw all lines
    ctx.strokeStyle='black';
    for(var i=0;i<lines.length;i++){   
        var line=lines[i];
        ctx.beginPath();
        ctx.moveTo(line.x0,line.y0);
        ctx.lineTo(line.x1,line.y1);
        ctx.stroke();
    }

    // draw the line closest to the mouse in red
    if(closestLineIndex<0){return;}
    var line=lines[closestLineIndex];
    ctx.strokeStyle='red';
    ctx.beginPath();
    ctx.moveTo(line.x0,line.y0);
    ctx.lineTo(line.x1,line.y1);
    ctx.stroke();
}

// On mousemove, find line closest to mouse
function handleMouseMove(e){
  e.preventDefault();
  e.stopPropagation();

  mouseX=parseInt(e.clientX-offsetX);
  mouseY=parseInt(e.clientY-offsetY);

  setClosestLine(mouseX,mouseY);

  redraw();

}

// On mousedown, remove line that was closest to mouse
function handleMouseDown(e){
    e.preventDefault();
    e.stopPropagation();

    if(closestLineIndex>=0){
        lines.splice(closestLineIndex,1);
        redraw();
    }
}
body {
  background-color: ivory;
}
#canvas {
  border: 1px solid red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>Closest line to mouse is drawn in red<br>Click to remove that line.</h4>
<canvas id="canvas" width=300 height=300></canvas>

【讨论】:

    【解决方案2】:

    没有简单的方法可以做到这一点,因为在绘制任何内容后像素的先前信息都会丢失。 在这里你有一个更好的答案:clear line on HTML5 Canvas

    在计算机图形学中,当你画一些东西时,你会画到一个缓冲区。和 当您调用 lineTo 并描边时,缓冲区会更新并且所有 底层像素中的信息丢失(或部分丢失) 如果你使用透明度会丢失)并且没有办法恢复它 撤消(除非有一个包含旧负载的实现 图纸,但这对记忆来说真的很重)。

    因此,能够撤消笔划可能会节省大量 CPU/GPU 时间,但是 会大大增加内存

    所以唯一的方法似乎是使用 clearRect。

    【讨论】:

      【解决方案3】:

      也许你会尝试一个 javascript 绘图库。例如oCanvas 库,您可以在其中绘制更多面向对象的内容。有一个remove 函数可以从画布中删除绘制的对象。

      【讨论】:

        猜你喜欢
        • 2013-10-03
        • 1970-01-01
        • 1970-01-01
        • 2021-10-07
        • 1970-01-01
        • 1970-01-01
        • 2016-07-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多