如何用一个按钮保存Fabric.js中的两个Canvas图像?
一键下载两个Fabric.js Canvas的解决方案
问题分析
你当前的代码里,连续对this.href和this.download赋值时,后面的内容会直接覆盖前面的,最终只会触发最后一个Canvas的下载。浏览器的<a>标签一次只能处理一个下载请求,没法通过多个download属性实现多文件下载。
解决方案1:动态创建临时标签触发多次下载
通过封装下载函数,每次下载时动态生成临时<a>标签,触发下载后移除标签,就能实现单按钮下载两个Canvas:
import "./styles.css"; import { fabric } from "fabric"; var mainCanvas = new fabric.Canvas("canvas"); var secCanvas = new fabric.Canvas("canvas2"); // 注意:同一个fabric对象不能同时添加到两个Canvas,需要克隆后添加 var rect = new fabric.Rect({ fill: "#ff0000", width: 100, height: 100, id: 1 }); mainCanvas.add(rect); secCanvas.add(rect.clone()); // 克隆对象再添加到第二个Canvas var circle = new fabric.Circle({ fill: "#ffff00", radius: 50, left: 150, top: 150, originX: "center", originY: "center", id: 2 }); mainCanvas.add(circle); var text = new fabric.IText("Type here...", { fontSize: 27, top: 10, left: 10 }); mainCanvas.add(text); fabric.Image.fromURL( "https://placekitten.com/200/303", function(img) { var image = img.set({ selectable: false, evented: false, left: 230 }); mainCanvas.add(image); mainCanvas.sendToBack(image); mainCanvas.renderAll(); }, { crossOrigin: "anonymous" } ); // 封装Canvas下载函数 function downloadCanvas(canvas, filename) { const tempLink = document.createElement('a'); tempLink.href = canvas.toDataURL({ format: 'png' }); tempLink.download = filename; document.body.appendChild(tempLink); tempLink.click(); document.body.removeChild(tempLink); } var saveBoth = document.getElementById("save2"); saveBoth.addEventListener( "click", function(e) { e.preventDefault(); // 阻止原标签的默认跳转行为 // 依次触发两个Canvas的下载 downloadCanvas(secCanvas, "secCanvas.png"); downloadCanvas(mainCanvas, "mainCanvas.png"); }, false );
解决方案2:打包为ZIP压缩包下载(可选)
如果希望用户只下载一个压缩包包含两张图片,可以使用JSZip库实现:
- 先安装依赖:
npm install jszip
- 修改代码:
import "./styles.css"; import { fabric } from "fabric"; import JSZip from 'jszip'; var mainCanvas = new fabric.Canvas("canvas"); var secCanvas = new fabric.Canvas("canvas2"); // 克隆对象添加到两个Canvas var rect = new fabric.Rect({ fill: "#ff0000", width: 100, height: 100, id: 1 }); mainCanvas.add(rect); secCanvas.add(rect.clone()); // 其余Canvas初始化代码和之前一致... var saveBoth = document.getElementById("save2"); saveBoth.addEventListener( "click", async function(e) { e.preventDefault(); const zip = new JSZip(); // 将Canvas的base64数据添加到ZIP(需去掉data URL前缀) zip.file("mainCanvas.png", mainCanvas.toDataURL().split(',')[1], { base64: true }); zip.file("secCanvas.png", secCanvas.toDataURL().split(',')[1], { base64: true }); // 生成ZIP并下载 const zipBlob = await zip.generateAsync({ type: 'blob' }); const tempLink = document.createElement('a'); tempLink.href = URL.createObjectURL(zipBlob); tempLink.download = "canvases.zip"; document.body.appendChild(tempLink); tempLink.click(); document.body.removeChild(tempLink); URL.revokeObjectURL(tempLink.href); // 释放资源 }, false );
额外注意点
原代码中直接将同一个rect对象添加到两个Canvas,这会导致该对象仅存在于最后添加的Canvas中(fabric对象只能属于一个Canvas实例),因此需要使用clone()方法复制对象后再添加到第二个Canvas。
内容的提问来源于stack exchange,提问作者Nad Ham
相关产品推荐
相关产品推荐

