160

I'd like to copy ALL contents of one canvas and transfer them to another all on the client-side. I would think that I would use the canvas.toDataURL() and context.drawImage() method to implement this but I am running into a few issues.

My solution would be to get Canvas.toDataURL() and store this in an Image object in Javascript, and then use the context.drawImage() method to place it back.

However, I believe the toDataURL method returns a 64 bit encoded tag with "data:image/png;base64," prepended to it. This does not seem to be a valid tag, (I could always use some RegEx to remove this), but is that 64 bit encoded string AFTER the "data:image/png;base64," substring a valid image? Can I say image.src=iVBORw...ASASDAS, and draw this back on the canvas?

I've looked at some related issues: Display canvas image from one canvas to another canvas using base64

But the solutions don't appear to be correct.

Community
  • 1
  • 1
J Kao
  • 2,023
  • 2
  • 15
  • 16

2 Answers2

337

Actually you don't have to create an image at all. drawImage() will accept a Canvas as well as an Image object.

//grab the context from your destination canvas
var destCtx = destinationCanvas.getContext('2d');
    
//call its drawImage() function passing it the source canvas directly
destCtx.drawImage(sourceCanvas, 0, 0);

Way faster than using an ImageData object or Image element.

Note that sourceCanvas can be a HTMLImageElement, HTMLVideoElement, or a HTMLCanvasElement. As mentioned by Dave in a comment below this answer, you cannot use a canvas drawing context as your source. If you have a canvas drawing context instead of the canvas element it was created from, there is a reference to the original canvas element on the context under context.canvas.

Here is a jsPerf to demonstrate why this is the only right way to clone a canvas: http://jsperf.com/copying-a-canvas-element

Chalist
  • 3,160
  • 5
  • 39
  • 68
Robert Hurst
  • 8,902
  • 5
  • 42
  • 66
  • 83
    a small point which tripped me up: while you can draw a canvas (`HTMLCanvasElement`), you *cannot* draw a context (`CanvasRenderingContext2D`). Use `myContext.canvas` instead. – Dave Aug 28 '13 at 16:02
  • 5
    @Dave comment is a **MUST READ**... woud give +10 if possible ;). @Robert-Hurst must complement his answer with this comment since he doesn't specify from where `source canvas` comes from... – Paulo Bueno Nov 24 '15 at 16:43
  • Could you please provide an example ? – ShibinRagh Sep 03 '16 at 12:49
  • @RogerGajraj Actually the canvas does not have to be visible. This is demonstrated there => https://jsfiddle.net/d36wwtvj – Robert Hurst May 16 '17 at 06:08
8

@robert-hurst has a cleaner approach.

However, this solution may also be used, in places when you actually want to have a copy of Data Url after copying. For example, when you are building a website that uses lots of image/canvas operations.

    // select canvas elements
    var sourceCanvas = document.getElementById("some-unique-id");
    var destCanvas = document.getElementsByClassName("some-class-selector")[0];

    //copy canvas by DataUrl
    var sourceImageData = sourceCanvas.toDataURL("image/png");
    var destCanvasContext = destCanvas.getContext('2d');

    var destinationImage = new Image;
    destinationImage.onload = function(){
      destCanvasContext.drawImage(destinationImage,0,0);
    };
    destinationImage.src = sourceImageData;
Community
  • 1
  • 1
vishwarajanand
  • 1,021
  • 13
  • 23
  • I was needing to copy the contents of a canvas to use elsewhere - this was perfect as it generated ~80KB of data vs ~60MB using `getImageData` – Ryan Wheale Dec 19 '20 at 07:26