返回 AiToEarn
imageExport.ts
1 /**
2 * imageExport - 图片编辑导出配置与 Canvas 合成工具
3 * 支持图片输出格式选择与 JPEG 质量压缩
4 */
5
6 export const IMAGE_EXPORT_FORMATS = [
7 { value: 'image/png', label: 'PNG', extension: 'png' },
8 { value: 'image/jpeg', label: 'JPG', extension: 'jpg' },
9 ] as const
10
11 export const IMAGE_EXPORT_QUALITY = {
12 min: 30,
13 max: 100,
14 step: 5,
15 default: 85,
16 } as const
17
18 export const DEFAULT_IMAGE_EXPORT_FORMAT = IMAGE_EXPORT_FORMATS[0].value
19
20 export type ImageExportFormat = typeof IMAGE_EXPORT_FORMATS[number]['value']
21 export type ImageExportExtension = typeof IMAGE_EXPORT_FORMATS[number]['extension']
22
23 export interface ImageExportOptions {
24 format: ImageExportFormat
25 quality: number
26 }
27
28 export function getImageExportExtension(format: ImageExportFormat): ImageExportExtension {
29 return IMAGE_EXPORT_FORMATS.find(item => item.value === format)?.extension ?? 'png'
30 }
31
32 export function getImageExportLabel(format: ImageExportFormat) {
33 return IMAGE_EXPORT_FORMATS.find(item => item.value === format)?.label ?? 'PNG'
34 }
35
36 export function createEditedImageFileName(format: ImageExportFormat) {
37 return `edited_${Date.now()}.${getImageExportExtension(format)}`
38 }
39
40 function getCanvasExportQuality(format: ImageExportFormat, quality: number) {
41 if (format !== 'image/jpeg')
42 return undefined
43
44 const normalizedQuality = Math.min(
45 IMAGE_EXPORT_QUALITY.max,
46 Math.max(IMAGE_EXPORT_QUALITY.min, quality),
47 )
48
49 return normalizedQuality / 100
50 }
51
52 function fillJpegBackground(ctx: CanvasRenderingContext2D, width: number, height: number) {
53 const bodyBackground = getComputedStyle(document.body).backgroundColor
54
55 if (bodyBackground && bodyBackground !== 'rgba(0, 0, 0, 0)') {
56 ctx.fillStyle = bodyBackground
57 }
58 else {
59 ctx.fillStyle = 'Canvas'
60 }
61
62 ctx.fillRect(0, 0, width, height)
63 }
64
65 export function exportCanvasLayers(
66 imageCanvas: HTMLCanvasElement,
67 drawCanvas: HTMLCanvasElement,
68 options: ImageExportOptions,
69 ): Promise<Blob> {
70 return new Promise((resolve, reject) => {
71 const mergeCanvas = document.createElement('canvas')
72 mergeCanvas.width = imageCanvas.width
73 mergeCanvas.height = imageCanvas.height
74
75 const ctx = mergeCanvas.getContext('2d')
76 if (!ctx) {
77 reject(new Error('Failed to get canvas context'))
78 return
79 }
80
81 if (options.format === 'image/jpeg') {
82 fillJpegBackground(ctx, mergeCanvas.width, mergeCanvas.height)
83 }
84
85 ctx.drawImage(imageCanvas, 0, 0)
86 ctx.drawImage(drawCanvas, 0, 0)
87
88 mergeCanvas.toBlob(
89 (blob) => {
90 if (blob) {
91 resolve(blob)
92 return
93 }
94
95 reject(new Error('Failed to export image'))
96 },
97 options.format,
98 getCanvasExportQuality(options.format, options.quality),
99 )
100 })
101 }
102
102 lines TYPESCRIPT