Canvas: Restore Image after removing object on it

Viewed 191

I have following code:

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');

const image = new Image(60, 45); // Using optional size for image
image.onload = drawImageActualSize; // Draw when image has loaded

// Load an image of intrinsic size 300x227 in CSS pixels
image.src = 'https://mdn.mozillademos.org/files/5397/rhino.jpg';

function drawImageActualSize() {
  // Use the intrinsic size of image in CSS pixels for the canvas element
  canvas.width = this.naturalWidth;
  canvas.height = this.naturalHeight;

  // Will draw the image as 300x227, ignoring the custom size of 60x45
  // given in the constructor
  ctx.drawImage(this, 0, 0);

  // To use the custom size we'll have to specify the scale parameters 
  // using the element's width and height properties - lets draw one 
  // on top in the corner:
  ctx.drawImage(this, 0, 0, this.width, this.height);
  ctx.save();
  ctx.fillStyle = 'green';
ctx.fillRect(10, 10, 150, 100);

ctx.restore();
ctx.clearRect(10, 10, 150, 100);
}
<canvas id="canvas" width=300 height=200></canvas>

Here ctx.clearRect(10, 10, 150, 100) method, removes green part as well as image and shows white canvas.

I want to remove only green part and restore previous image.

How can i achieve this ?

1 Answers

Redraw

The standard way to add and remove dynamic content is to re render the canvas every time there is a change. This is how HTML canvas games running at 60fps do it and they can have 100s of items changing per frame.

There are other methods that require making a copy of what is under the rectangle each time it is drawn, and then drawing the copy over the rectangle when you want it removed.

This is complex and requires at least one extra canvas per dynamic object being drawn and is thus a memory hog. Or use getImageData and putImageData also a memory hog, is supa slow, will result in a lot of GC action, and is not available for unsecured content (images across domains, or from local file store).

Redraw is by far the simplest way. You would only consider other methods if it took more than a few 100ms to render all content (for non animated content)

Example

In the example the variable rectOn if true add a green rectangle to the canvas (Draws both the image and the rectangle). If not true then the rectangle is removed (only the image is drawn).

The function draw renders the canvas according to the state of the variable rectOn

Click button to add/remove rectangle.

const ctx = canvas.getContext('2d');
var rectOn = false; 
const image = new Image();
image.src = 'https://mdn.mozillademos.org/files/5397/rhino.jpg';
image.addEventListener("load", ready, {once:true});
function ready() {
    canvas.width = this.naturalWidth;
    canvas.height = this.naturalHeight;
    loading.classList.add("hide");
    addRemove.classList.remove("hide");
    addRemove.addEventListener("click", toggle);
    draw();
}
function draw() {
    ctx.drawImage(image, 0, 0);
    if (rectOn) {
        ctx.fillStyle = "#0F0";
        ctx.fillRect(10, 50, 150, 100);    
    }
}
function toggle() {
    rectOn = !rectOn;     
    addRemove.textContent = (rectOn ? "Remove": "Add" ) + " rect";
    draw();
}
button { cursor: pointer; width: 120px }
.hide {display:none}
canvas { position: absolute; top: 0px; left: 0px; z-index:-1; }
<span id="loading">Loading image</span><button id="addRemove" class="hide">Add green rect</button><br>
<canvas id="canvas"></canvas>

Related