将 Canvas 保存为 Blob,然后将 Blob 保存为文件

5

这个页面上有一个图标,当被点击时会触发某些操作。但是当前这个图标是一个锚点。我可以将其改为 div(事实上我的界面中所有的图标都是 div),但出于某种原因以下代码无法正常工作。

为什么第一次点击没有反应?

$('#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: https://jsfiddle.net/udbq3fb7/


1
最好包括在现场重现问题的逻辑,而不仅仅是链接到外部粘贴服务。 - Taplar
1
当异步的 toBlob 回调触发时,您的点击事件已经很久之前就结束了。默认行为已经被执行了,并且仅仅覆盖您的 <a> href 是没有直接效果的。您需要自己触发 <a>click() 方法,但这会创建一个无限循环的 save... 所以最好使用一个新的 anchor,或者甚至更好的是使用一个 ,它甚至可以处理怪癖。 - Kaiido
谢谢Kaiido,我会尝试在回调函数内创建一个新元素,并向您反馈。 - simon
无法弄清楚,抱歉 @Kaiido - simon
为什么当事件在函数头中指定时,在代码运行时它会失效? - simon
显示剩余6条评论
2个回答

6

经过多次尝试和努力,不使用第三方工具,以下是一个同样有效的答案:

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


似乎这会导致 Blob 泄露,因为没有调用 revokeObjectUrl()? https://dev59.com/OVUM5IYBdhLWcg3wMN19 和 https://w3c.github.io/FileAPI/#url-intro - Aralox
可能不是这种情况,因为锚点被从DOM中移除了,我会这样假设。 - simon

1

以下是当前情况的步骤:

  1. 用户点击。
  2. 事件在文档中被分派。
  3. 事件处理程序触发[如果有]。
  4. 默认的点击行为发生[如果在3.中未被阻止]。

现在,您的代码尝试通过在事件处理程序(3.)中更改<a>href来覆盖点击(4.)的默认行为。这个技巧通常有效,因为3.发生在4.之前,但在您的情况下,在3.中调用了一个异步方法,href只有在异步回调被调用后才会被更改。

所以你有
...
3. Canvas.toBlob(callback)
4. 默认点击行为发生
...
n. 回调触发(更改默认行为)

当您的回调被调用时,您的点击事件已经不存在了,更改它的href只会对下一次点击产生影响。


你可以在这个回调函数内直接调用<a>click方法,但这会导致无限循环,除非你移除事件处理程序并重新分配它,但最好创建一个新的<a>元素,之后再删除它,或者更简单地使用像FileSaver.js这样的库,它可以处理一些旧浏览器中的问题。

$('#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>


这一切都源于我的第一个调用使用toDataURL()生成的一些画布数据太大而无法适应锚本身。 - simon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接