利用 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);
jyp20121107