How to delete only a line from the canvas, not all

2019-06-07 20:50发布

The solution I found is to clear the whole canvas, but I only want to delete one line not all drawings on the canvas.

What should I do?

3条回答
老娘就宠你
2楼-- · 2019-06-07 21:03

There's no simple way to do that, as the previous information of the pixels are lost after you draw anything. Here you've a better answer: clear line on HTML5 Canvas

In computer graphics when drawing something, you draw to a buffer. And when you call lineTo and stroke the buffer is updated and all information that were in the underlying pixels are lost (or partly lost if you use transparency) and there is no way to get it back by undoing (unless there is an implementation containing loads of old drawings, but that would be really heavy for the memory).

So to be able to undo a stroke might save alot of CPU/GPU time BUT whould heavily increase the memory

So the only way seems to use clearRect.

查看更多
女痞
3楼-- · 2019-06-07 21:12

@danielfranca is right that a line drawn onto the canvas becomes "unremembered pixels in the canvas's sea of pixels."

He's also right that saving snapshot images of the canvas as each line is drawn and reverting to one of those saved images to "delete" a line is resource intensive. (Don't use that technique!!)

But, there is an efficient way to delete previously drawn lines on a canvas!

Yes, it does clear the canvas and redraw the lines, but it's very fast & efficient...I promise!

Here's an outline of how to do it:

  • Define a line inside an object like this: { x0:10, y0:15, x1:100, y1:75 }

  • Make as many of those lines as desired and push them into an array: var lines=[];

  • Use the line definitions in the lines[] array to draw your lines onto the canvas.

  • Listen for mousemove and mousedown events.

  • On mousemove, iterate throught lines[] and find the line closest to the mouse. Here's a snippet of the algorithm that calculates which line is closest to a given [mx,my]:

    // 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;
            }
        }
    };
    
  • On mousedown, use lines.splice(targetIndex,1) to remove the definition of the closest line from the lines[] array. Then clear the canvas and redraw the remaining lines.

Here's annotated code and a Demo:

// 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>

查看更多
Evening l夕情丶
4楼-- · 2019-06-07 21:15

Maybe you would give a javascript drawing library a try. There is, for example the oCanvas library, where you draw more object oriented. There is a remove function which removes drawn objects from the canvas.

查看更多
登录 后发表回答