13

I am totally new to this Canvas Element. I am able to draw line in canvas, but not able to clear only specif line. Whole canvas become blank.

Tried this: 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" />

Script

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

Dharam Mali
  • 907
  • 1
  • 11
  • 24
  • 3
    As far as i know, you can't access a specific element in canvas, you've to clear it entirely. I don't know what your requirement is, but if you're drawing something like charts and want to access individual items, respond to it's events etc, look into svg... – T J Jun 10 '14 at 12:21
  • 3
    You cannot do that. The canvas is a bitmap. Everything you draw on it stays there. You cannot manipulate specific drawings afterwards. You can only clear it as a whole. If you want to be able to draw "objects" on the canvas and manipulate them later and remove them, you need to keep track of those objects yourself (in an array) and then have a draw function which updates the canvas regularly and raws all the current objects in the array. To clear a specific object, just clear it from the array and redraw. – HaukurHaf Jun 10 '14 at 12:21
  • So to manage two line, Do I need to create two Canvas element ? – Dharam Mali Jun 10 '14 at 12:25
  • 1
    If you want to manage multiple lines, you need to use multiple layers (canvas) or (as comment above by HaukurHaf says) register every line you draw and after you clear everything - redraw lines you need. – ViliusL Jun 10 '14 at 12:29
  • We cannot access elements in the canvas, we have to clear and redraw all canvas elements. – lokeshpahal Jun 10 '14 at 12:31
  • Why do you get 2 contexts for the same canvas? I think you wanted to have two `distinct` canvases. – XCS Jun 10 '14 at 12:37

3 Answers3

17

About Canvas, Canvas 'elements', and the visibility of `elements' ...

When any element on the canvas needs to change (move, erase, etc), the standard method is to completely erase the canvas and redraw the canvas with the elements in their new positions (or not redraw the elements if they are erased).

That's because canvas does not "remember" where it drew any individual element and therefore cannot individually move or erase any element.

It's up to you to "remember" enough information about an element to redraw it after the canvas has been erased.

A Demo: http://jsfiddle.net/m1erickson/Wrk2e/

So in your example you could create javascript objects a and b to represent your top-right and left-bottom line paths.

Each object would have the points which define its line-path and a flag indicating if it is visible (visible == redrawn on the 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,
}

For easy processing you can put all your line-path objects in an array:

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

Then when you clear the canvas you simply use each objects line-path information to redraw the line. If a particular objects visibility flag is false then don't redraw that particular object.

// 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();
}

With all this in place, your buttons simply change the visibility flag on a particular line-path object and then clear/redraw the entire canvas.

// 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);
});
markE
  • 102,905
  • 11
  • 164
  • 176
  • I think it's a perfect solution which I am looking for. – Dharam Mali Jun 11 '14 at 07:57
  • 2
    Just for the sake of completeness, if one were to want to clear a specific line or shape (as I did), rather than a rectangular region, you can set `context.globalCompositeOperation = "destination-out"` -- as mentioned in andrewmu's solution to a [similar question](http://stackoverflow.com/questions/3328906/erasing-in-html5-canvas) -- will basically make your fill/stroke operations act just like an eraser tool. See this [compositing and clipping tutorial](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Compositing). This wouldn't enable deletion of layered elements, though. – TheMadDeveloper Nov 22 '15 at 11:49
  • 1
    @TheMadDeveloper. You mention a good follow-on point. Keep in mind that erasing a stroked line with destination-out compositing is sometimes ineffective because the anti-aliasing that canvas adds to the line is often not erased.But destination-out works well for context.fill's. :-) – markE Nov 22 '15 at 19:29
  • does reactjs solve this problem with its continuous rendering? – Sapinder Singh Jun 24 '20 at 03:02
3

Canvas is transparent. It is not possible to acheive in single canvas tag. because the clearRect functionality to clear based on width and height. we didn't give the exact position to clear the canvas. Try the fiddle . you acheive the scenario with two canvas tags.

Fiddle

Sudharsan S
  • 15,336
  • 3
  • 31
  • 49
  • @sudharsan. There's a better way (and a more standard way) of dealing with 'elements' like line-paths in canvas. Kindly see my post. ;-) – markE Jun 10 '14 at 16:28
0

You just have to re-paint the lines that should persist after you clear the Canvas.
Maybe like this: http://jsfiddle.net/8YNvu/10/

BrendanMullins
  • 587
  • 3
  • 18
  • I am creating some Flow-chart type functionality, so I guess suggestion you provided might not work. User can able to create N number of lines and objects in it. In that case this will not work. correct me if I am wrong. – Dharam Mali Jun 10 '14 at 12:45