4

I am using HTML5 canvas as follows:

  1. Display an image that fills the canvas area.
  2. Display a black text label over the image.
  3. On click of the text label highlight it by drawing a filled red rect + white text.

I have that part all working fine. Now what I want to do is remove the red rect and restore the image background that was originally behind it. I'm new to canvas and have read a fair amount, however I can't see how to do this. That said I am sure it must be quite simple.

Phrogz
  • 296,393
  • 112
  • 651
  • 745
nevf
  • 4,596
  • 6
  • 31
  • 32

3 Answers3

10

I think there are some ways...

Redraw all stuff after the click release

This is simple but not really efficient.

Redraw only the altered part

drawImage with 9 arguments to redraw only the altered background image part, then redraw the black text over.

Save image data before click and then restore it

This uses getImageData and putImageData of the 2D context. (Not sure that it's widely implemented though.)

Here the specification:

ImageData getImageData(in double sx, in double sy, in double sw, in double sh);
void putImageData(in ImageData imagedata, in double dx, in double dy, in optional double dirtyX, in double dirtyY, in double dirtyWidth, in double dirtyHeight);

So for instance if the altered part is in the rect from (20,30) to (180,70) pixels, simply do:

var ctx = canvas.getContext("2d");
var saved_rect = ctx.getImageData(20, 30, 160, 40);
// highlight the image part ...

// restore the altered part
ctx.putImageData(saved_rect, 20, 30);

Use two superposed canvas

The second canvas, positioned over the first, will hold the red rect and the white text, and will be cleared when you want to "restore" the original image.

Pierre
  • 1,322
  • 11
  • 17
  • +1 You offer a great deal of good ways. Note that save/restore of the image can be done [faster](http://phrogz.net/tmp/canvas_copy_benchmark.html) by using `drawImage()` and a second canvas. I'll add an answer showing this. – Phrogz Feb 01 '11 at 04:40
  • Thanks Pierre. I had considered redrawing everything, however as you say this is not efficient. I'll try the two canvas approach you and Phrogz suggest. I'm a little surprised there isn't a function provided by canvas to accomplish what I consider a fairly basic requirement. – nevf Feb 01 '11 at 06:49
  • @nevf HTML Canvas is a low-level _non-retained_ (or _immediate_) drawing mode graphics API. Unlike HTML or SVG, it does not remember what you drew. Each drawing command paints instantly-drying pixels onto the canvas, completely obliterating whatever was underneath. Concepts like 'dragging' or 'moving' or 'zooming' must be implemented on top of the low-level API. Good luck! – Phrogz Feb 01 '11 at 14:18
  • Note that, unlike my answer, the simplest way of drawing and restoring is the last suggestion above. Instead of drawing on the canvas API, create another HTML Element (div, image, canvas) and absolutely position it over the canvas. When you don't want it, remove it (or hide it). This uses the _retained drawing mode_ nature of HTML to make your life simpler. – Phrogz Feb 01 '11 at 14:22
  • You give the width and height of the region as arguments to the method "putImageData" which is wrong as outlined [here](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Canvas_tutorial/Pixel_manipulation_with_canvas). You are only supposed to pass the data and the position. – Prior99 Jun 07 '14 at 10:37
  • 1
    @Prior you are right, it should be `(20,30,0,0,160,40)` or simply `(20,30)`. Look at this spec for full doc: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D#putImageData() – Pierre Jun 10 '14 at 09:27
4

For another Stack Overflow question I created an example showing how to save and restore a section of a canvas. In summary:

function copyCanvasRegionToBuffer( canvas, x, y, w, h, bufferCanvas ){
  if (!bufferCanvas) bufferCanvas = document.createElement('canvas');
  bufferCanvas.width  = w;
  bufferCanvas.height = h;
  bufferCanvas.getContext('2d').drawImage( canvas, x, y, w, h, 0, 0, w, h );
  return bufferCanvas;
}
Community
  • 1
  • 1
Phrogz
  • 296,393
  • 112
  • 651
  • 745
  • Thanks Phrogz, I'll try your suggestion. See my other reply above. – nevf Feb 01 '11 at 06:50
  • Very nice. More benchmark info can be found on [jsperf](https://jsperf.com/canvas-drawimage-vs-putimagedata/93) (from [this SO question](https://stackoverflow.com/a/7722892)). – djvg Nov 28 '18 at 11:27
  • @phrogz: Sorry for following up on this age-old answer, but in your [example](http://phrogz.net/tmp/canvas_copy.html), it appears that pixels at the boundaries of the buffer region are not restored. This becomes apparent, for example, when I hit "draw" many times, then hit "load." Any idea why this happens? Should a slightly larger buffer region be used? – djvg Nov 29 '18 at 13:53
0
function draw(e){
    ctx.drawImage(img, 0, 0);
    if(e){
        ctx.fillStyle='red';
        ctx.fillRect(5, 5, 50, 15);
        ctx.fillStyle='white';
    }else{
        ctx.fillStyle='black';
    }
    ctx.fillText('Label', 10, 17);
}
draw();
document.onclick=draw;
cuixiping
  • 24,167
  • 8
  • 82
  • 93