You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用一个按钮保存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库实现:

  1. 先安装依赖:
npm install jszip
  1. 修改代码:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 14:53:24