如何将Canvas转为SVG

之前做了个关系图谱的需求,因为导出png等图片放大会失真,所以希望导出为矢量图。但是G6自带的导出图谱功能再Canvas模式下无法导出为SVG,而SVG模式又有很多特性不支持,所以就想办法能不能把Canvas导出为SVG了。

本代码使用了一个多年的老库,叫做canvas-to-svg

该库模拟了canvas并且暴露了一些canvas的api,最后可以导出为svg

需要注意的是,部分canvas的api并不支持,比如 setTransform,resetTransform等,如果你需要用到这两个api,可以将其拆分为translate和rotate等

直接上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import CanvasToSVG from 'canvas-to-svg';
function addTextWaterMaskToCanvas(
context: CanvasRenderingContext2D,
textWaterMask: string,
) {
context.font = '16px Microsoft YaHei';
context.fillStyle = '#F2F3F5';
const { width, height } = context.canvas;
const textMaskCountHorizontal = Math.max(1, width / 150);
const textMaskCountVertical = Math.max(1, height / 150);
const fwidth = width / textMaskCountHorizontal;
const fheight = height / textMaskCountVertical;
for (let hh = 0; hh < height; hh += fheight) {
for (let ww = 0; ww < width; ww += fwidth) {
context.translate(ww, hh);
context.rotate(-Math.atan(height / width));
context.fillText(textWaterMask, -fwidth / 2, fheight / 2);
context.rotate(Math.atan(height / width));
context.translate(-ww, -hh);
}
}
}

export function downloadFullImageWithWaterMaskCreator(
graphInstance: IGraph | null,
textWaterMask: string,
) {
return function (
name?: string,
type?: DataUrlType,
imageConfig?: {
backgroundColor?: string;
padding?: number | number[];
},
callback?: (success: boolean) => void,
) {
graphInstance?.toFullDataURL(
res => {
const image = new Image();
image.src = res;
image.onload = function () {
try {
// const canvas = document.createElement('canvas');
// canvas.width = image.width;
// canvas.height = image.height;
// const context = canvas.getContext('2d');
const context = new CanvasToSVG(image.width, image.height);
if (context && name) {
context.rect(0, 0, image.width, image.height);
context.drawImage(image, 0, 0, image.width, image.height);

addTextWaterMaskToCanvas(context, textWaterMask);

// downloadCanvasImage(canvas, name, type);

const svg = context.getSerializedSvg(true);
const svgData = URL.createObjectURL(
new Blob([svg], { type: 'image/svg' }),
);
const downloadLink = document.createElement('a');
downloadLink.download = `${name}.svg`;
downloadLink.href = svgData;
downloadLink.click();

callback?.(true);
}
} catch {
callback?.(false);
}
};

image.onerror = function () {
callback?.(false);
};
},
type,
imageConfig,
);
};
}