Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Saving Canvas as blob and then blob as file

This is more or less an icon on the page that when clicked does something, but the icon on the page is currently an anchor. I can change this to a div (all the rest of the icons are divs for that matter in my UI), but for some reason the following doesn't work.

Why does the first click register nothing?

$('#a').click(save);

function save(ev) {
  $('#canvas')[0].toBlob((blob) => {
    let URLObj = window.URL || window.webkitURL;
    ev.target.href = URLObj.createObjectURL(blob)
    ev.target.download = "untitled.png";
  });
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id='canvas'></canvas>
<a id='a'>click me to save</a>

Fiddle to see behavior: https://jsfiddle.net/udbq3fb7/

like image 446
simon Avatar asked Jan 02 '18 01:01

simon


2 Answers

Through much trial and effort, without using a 3rd party, here is an answer that works too:

$('#a').click(save);

function save(ev) {
   $('#canvas')[0].toBlob((blob) => {
        let URLObj = window.URL || window.webkitURL;
        let a = document.createElement("a");  
        a.href = URLObj.createObjectURL(blob);
        a.download = "untitled.png";
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
   });
}

fiddle: https://jsfiddle.net/rkqkr47w/

like image 118
simon Avatar answered Oct 01 '22 05:10

simon


Here is a walk-through the current situation:

  1. User clicks.
  2. Event gets dispatched in the document.
  3. EventHandlers fire [if any].
  4. default click behavior happens [if not prevented in 3.].

Now, what your code is trying to do is to overwrite the default behavior of the click (4.) action, by changing your <a>'s href in the event handlers (3.).

This trick normally works, because 3. happens before 4., but in your case, you are calling an asynchronous method in 3., and the href is only changed after your async callback gets called.

So you have
...
3. Canvas.toBlob(callback)
4. default click behavior happens
...
n. callback fires (change default behavior)

When your callback gets called, your click event is long dead, and changing its href will have incidence only on the next click.


You could call directly the <a>'s click method inside this callback, but it would lead to an infinite loop, except if you remove the event handler and reassign it again, but it would probably be easier to create a new <a> element altogether, that you would remove afterward, and even easier to use a library like FileSaver.js which does exactly this along with handling some quirks in older browsers.

$('#a').click(save);

function save(ev) {
  $('#canvas')[0].toBlob((blob) => {
    saveAs(blob, "untitled.png");
  });
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.3/FileSaver.js"></script>
<canvas id='canvas'></canvas>
<a id='a'>click me to save</a>
like image 24
Kaiido Avatar answered Oct 01 '22 04:10

Kaiido