svg转成canvas

1,458 阅读1分钟

利用 html2canvas 将指定元素转成图片进行打印或下载。但是元素区域中包含 svg,svg图像无法被 html2canvas 绘制出来。因此,需要借助 canvg 插件实现。

-依赖

{
    "canvg": "^4.0.1",
    "html2canvas": "^1.4.1",
    "print-js": "^1.6.0",
    "vue": "^3.2.41",
}
  • 代码
import html2canvas from "html2canvas";
import { Canvg } from "canvg";
import print from "print-js";
import { nextTick } from "vue"; // 所有使用 nextTick的地方 都可以用 setTimeout 代替

const html2canvasOptions = {
  useCORS: true,
  scale: window.devicePixelRatio < 2 ? 2 : window.devicePixelRatio,
  tainttest: true, // 检测每张图片已经加载完成
  isDownload: false,
};
const html2canvasFn = (
  cloneEl: HTMLElement,
  options: any,
  originEl: Element
) => {
  return new Promise((resolve, reject) => {
    html2canvas(cloneEl, options).then((canvas) => {
      const base64File = canvas.toDataURL("image/png");
      const blob = base64ToFile(base64File);
      const url = URL.createObjectURL(blob);
      if (options.isDownload) {
        dowloadFn(url);
      } else {
        printFn(base64File, cloneEl);
      }
      nextTick(() => {
        URL.revokeObjectURL(url);
        originEl.removeChild(cloneEl);
        resolve(true);
      });
    });
  });
};

const base64ToFile = (dataUrl: string) => {
  const arr = dataUrl.split(",");
  const mime = arr[0].match(/:(.*?);/)![0];
  const bstr = atob(arr[1]);
  let n = bstr.length;
  const u8arr = new Uint8Array(n);
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n);
  }
  return new Blob([u8arr], { type: mime });
};

const dowloadFn = (url: string) => {
  const a = document.createElement("a");
  a.setAttribute("href", url);
  a.setAttribute("download", new Date().getTime() + ".png");
  a.click();
  nextTick(() => {
    a.remove();
  });
};

const printFn = (url: string, parentEl: HTMLElement) => {
  const id = "_printImg";
  const img = document.createElement("img");
  img.src = url;
  img.id = id;
  parentEl.appendChild(img);
  onPrint(id);
  nextTick(() => {
    parentEl.removeChild(img);
  });
};

const onPrint = (elId: string) => {
  print({
    printable: elId,
    type: "html",
    scanStyles: false,
  });
};

const printContentById = (elId: string, options: any) => {
  return new Promise((resolove, reject) => {
    const originEl = document.querySelector(elId);
    if (originEl) {
      const cloneEl = originEl.cloneNode(true) as HTMLElement;
      originEl.appendChild(cloneEl);
      svg2Canvas(cloneEl).then((res) => {
        html2canvasFn(
          cloneEl,
          Object.assign({}, html2canvasOptions, options),
          originEl
        ).then((res) => {
          // 延迟设置完成状态
          setTimeout(() => {
            resolove(true);
          }, 1000);
        });
      });
    } else {
      console.error("未找到打印元素");
      reject(false);
    }
  });
};

const svg2Canvas = (cloneEl: HTMLElement) => {
  return new Promise((resolve, reject) => {
    let index = 0;
    let elLength = 0;
    const callback = () => {
      if (index === elLength) {
        resolve(true);
      }
    };

    try {
      const svgEls = cloneEl.querySelectorAll("svg");
      const imgEls = cloneEl.querySelectorAll("img");
      const els = [...Array.from(svgEls), ...Array.from(imgEls)];
      elLength = els.length;
      els.forEach(async (el) => {
        const parentNode = el.parentNode;
        const canvas = document.createElement("canvas");
        canvas.style.zIndex = "0";
        if (el.tagName === "svg") {
          const svg = el.outerHTML.trim();
          const { width, height } = el.getBoundingClientRect();
          canvas.width = width;
          canvas.height = height;
          const ctx = canvas.getContext("2d");
          const v = await Canvg.from(ctx!, svg);
          if (el.style.position) {
            canvas.style.position = el.style.position;
            canvas.style.left = el.style.left;
            canvas.style.top = el.style.top;
          }
          v.start(); // 在页面上绘制 转换好的 canvas图像
          index++;
          callback();
        }

        if (el.tagName === "IMG") {
          canvas.width = el.width as number;
          canvas.height = el.height as number;
          canvas.getContext("2d")!.drawImage(el as any, 0, 0);
          index++;
          callback();
        }
        parentNode!.appendChild(canvas);
        parentNode!.removeChild(el);
      });
    } catch (error) {
      reject(error);
    }
  });
};