CatPtain commited on
Commit
11c8c53
·
verified ·
1 Parent(s): 4822fa6

Upload canvasRenderer.ts

Browse files
Files changed (1) hide show
  1. frontend/src/utils/canvasRenderer.ts +246 -0
frontend/src/utils/canvasRenderer.ts ADDED
@@ -0,0 +1,246 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Canvas渲染工具
3
+ * 用于将DOM元素渲染为Canvas并转换为Base64图像
4
+ */
5
+
6
+ import html2canvas from 'html2canvas';
7
+
8
+ // 渲染选项接口
9
+ interface RenderOptions {
10
+ scale?: number;
11
+ backgroundColor?: string | null;
12
+ useCORS?: boolean;
13
+ timeout?: number;
14
+ format?: 'png' | 'jpeg' | 'webp';
15
+ quality?: number;
16
+ }
17
+
18
+ /**
19
+ * 将DOM元素渲染到Canvas
20
+ * @param element 要渲染的DOM元素
21
+ * @param options 渲染选项
22
+ * @returns Promise<HTMLCanvasElement>
23
+ */
24
+ export async function renderElementToCanvas(
25
+ element: HTMLElement,
26
+ options: RenderOptions = {}
27
+ ): Promise<HTMLCanvasElement> {
28
+ const {
29
+ scale = 1,
30
+ backgroundColor = null,
31
+ useCORS = true,
32
+ timeout = 5000
33
+ } = options;
34
+
35
+ return new Promise((resolve, reject) => {
36
+ const timeoutId = setTimeout(() => {
37
+ reject(new Error('Canvas rendering timeout'));
38
+ }, timeout);
39
+
40
+ try {
41
+ // 使用html2canvas进行渲染
42
+ html2canvas(element, {
43
+ scale,
44
+ backgroundColor,
45
+ useCORS,
46
+ allowTaint: !useCORS,
47
+ foreignObjectRendering: true,
48
+ logging: false,
49
+ width: element.offsetWidth,
50
+ height: element.offsetHeight,
51
+ windowWidth: element.offsetWidth,
52
+ windowHeight: element.offsetHeight
53
+ }).then((canvas) => {
54
+ clearTimeout(timeoutId);
55
+ resolve(canvas);
56
+ }).catch((error) => {
57
+ clearTimeout(timeoutId);
58
+ reject(new Error(`html2canvas rendering failed: ${error.message}`));
59
+ });
60
+ } catch (error) {
61
+ clearTimeout(timeoutId);
62
+ reject(error);
63
+ }
64
+ });
65
+ }
66
+
67
+ /**
68
+ * 将SVG元素渲染到Canvas
69
+ * @param svgElement SVG元素
70
+ * @param options 渲染选项
71
+ * @returns Promise<HTMLCanvasElement>
72
+ */
73
+ export async function renderSVGToCanvas(
74
+ svgElement: SVGElement,
75
+ options: RenderOptions = {}
76
+ ): Promise<HTMLCanvasElement> {
77
+ // 对于SVG元素,我们可以直接使用html2canvas
78
+ // 或者使用传统的SVG序列化方法
79
+ const {
80
+ scale = 1,
81
+ backgroundColor = null,
82
+ timeout = 5000
83
+ } = options;
84
+
85
+ return new Promise((resolve, reject) => {
86
+ const timeoutId = setTimeout(() => {
87
+ reject(new Error('SVG Canvas rendering timeout'));
88
+ }, timeout);
89
+
90
+ try {
91
+ // 获取SVG尺寸
92
+ const rect = svgElement.getBoundingClientRect();
93
+ const width = rect.width || svgElement.clientWidth || 300;
94
+ const height = rect.height || svgElement.clientHeight || 200;
95
+
96
+ // 创建Canvas
97
+ const canvas = document.createElement('canvas');
98
+ const ctx = canvas.getContext('2d');
99
+
100
+ if (!ctx) {
101
+ clearTimeout(timeoutId);
102
+ reject(new Error('Failed to get 2D context'));
103
+ return;
104
+ }
105
+
106
+ canvas.width = width * scale;
107
+ canvas.height = height * scale;
108
+ ctx.scale(scale, scale);
109
+
110
+ // 设置背景色
111
+ if (backgroundColor) {
112
+ ctx.fillStyle = backgroundColor;
113
+ ctx.fillRect(0, 0, width, height);
114
+ }
115
+
116
+ // 获取SVG的XML字符串
117
+ const svgData = new XMLSerializer().serializeToString(svgElement);
118
+ const svgBlob = new Blob([svgData], { type: 'image/svg+xml;charset=utf-8' });
119
+ const url = URL.createObjectURL(svgBlob);
120
+
121
+ const img = new Image();
122
+ img.onload = () => {
123
+ try {
124
+ ctx.drawImage(img, 0, 0, width, height);
125
+ URL.revokeObjectURL(url);
126
+ clearTimeout(timeoutId);
127
+ resolve(canvas);
128
+ } catch (error) {
129
+ URL.revokeObjectURL(url);
130
+ clearTimeout(timeoutId);
131
+ reject(new Error(`Failed to draw SVG to Canvas: ${error}`));
132
+ }
133
+ };
134
+
135
+ img.onerror = () => {
136
+ URL.revokeObjectURL(url);
137
+ clearTimeout(timeoutId);
138
+ reject(new Error('SVG image loading failed'));
139
+ };
140
+
141
+ img.src = url;
142
+ } catch (error) {
143
+ clearTimeout(timeoutId);
144
+ reject(new Error(`SVG serialization failed: ${error}`));
145
+ }
146
+ });
147
+ }
148
+
149
+
150
+
151
+ /**
152
+ * 将Canvas转换为Base64数据URL
153
+ * @param canvas Canvas元素
154
+ * @param options 转换选项
155
+ * @returns Base64数据URL字符串
156
+ */
157
+ export function canvasToBase64(
158
+ canvas: HTMLCanvasElement,
159
+ options: RenderOptions = {}
160
+ ): string {
161
+ const { format = 'png', quality = 0.95 } = options;
162
+
163
+ try {
164
+ if (format === 'jpeg') {
165
+ return canvas.toDataURL('image/jpeg', quality);
166
+ } else if (format === 'webp') {
167
+ return canvas.toDataURL('image/webp', quality);
168
+ } else {
169
+ return canvas.toDataURL('image/png');
170
+ }
171
+ } catch (error) {
172
+ throw new Error(`Canvas to Base64 conversion failed: ${error}`);
173
+ }
174
+ }
175
+
176
+ /**
177
+ * 直接将DOM元素渲染为Base64图像
178
+ * @param element 要渲染的DOM元素
179
+ * @param options 渲染和转换选项
180
+ * @returns Promise<string> Base64数据URL
181
+ */
182
+ export async function renderElementToBase64(
183
+ element: HTMLElement,
184
+ options: RenderOptions = {}
185
+ ): Promise<string> {
186
+ try {
187
+ console.log('Starting element to Base64 conversion:', {
188
+ element: element.tagName,
189
+ className: element.className,
190
+ options
191
+ });
192
+
193
+ const canvas = await renderElementToCanvas(element, options);
194
+ const base64 = canvasToBase64(canvas, options);
195
+
196
+ console.log('Element to Base64 conversion completed:', {
197
+ canvasSize: `${canvas.width}x${canvas.height}`,
198
+ base64Length: base64.length,
199
+ preview: base64.substring(0, 100) + '...'
200
+ });
201
+
202
+ return base64;
203
+ } catch (error) {
204
+ console.error('Element to Base64 conversion failed:', error);
205
+ throw new Error(`Element rendering failed: ${error}`);
206
+ }
207
+ }
208
+
209
+ /**
210
+ * 检查浏览器是否支持Canvas渲染
211
+ * @returns boolean
212
+ */
213
+ export function isCanvasRenderSupported(): boolean {
214
+ try {
215
+ const canvas = document.createElement('canvas');
216
+ const ctx = canvas.getContext('2d');
217
+ return !!(ctx && typeof ctx.drawImage === 'function' && typeof window !== 'undefined');
218
+ } catch {
219
+ return false;
220
+ }
221
+ }
222
+
223
+ /**
224
+ * 获取元素的有效尺寸
225
+ * @param element DOM元素
226
+ * @returns 尺寸对象
227
+ */
228
+ export function getElementDimensions(element: HTMLElement) {
229
+ const rect = element.getBoundingClientRect();
230
+ const computedStyle = window.getComputedStyle(element);
231
+
232
+ return {
233
+ width: rect.width || element.offsetWidth || parseFloat(computedStyle.width) || 0,
234
+ height: rect.height || element.offsetHeight || parseFloat(computedStyle.height) || 0,
235
+ clientWidth: element.clientWidth,
236
+ clientHeight: element.clientHeight,
237
+ offsetWidth: element.offsetWidth,
238
+ offsetHeight: element.offsetHeight,
239
+ boundingRect: {
240
+ width: rect.width,
241
+ height: rect.height,
242
+ top: rect.top,
243
+ left: rect.left
244
+ }
245
+ };
246
+ }