vue3-image-compressor 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +174 -0
  2. package/dist/assets/imageWorker-DyeUTFOy.js +67 -0
  3. package/dist/components/ImageCompressor.vue.d.ts +38 -0
  4. package/dist/composables/useCompression.d.ts +170 -0
  5. package/dist/composables/useEncoderRegistry.d.ts +17 -0
  6. package/dist/composables/useWorker.d.ts +11 -0
  7. package/dist/constants/encoders.d.ts +5 -0
  8. package/dist/constants/resizeMethods.d.ts +39 -0
  9. package/dist/index.d.ts +15 -0
  10. package/dist/style.css +1 -0
  11. package/dist/types/compression.d.ts +35 -0
  12. package/dist/types/encoder.d.ts +100 -0
  13. package/dist/types/processor.d.ts +30 -0
  14. package/dist/types/worker.d.ts +21 -0
  15. package/dist/utils/file.d.ts +23 -0
  16. package/dist/utils/image.d.ts +31 -0
  17. package/dist/vue-image-compressor.js +660 -0
  18. package/dist/vue-image-compressor.umd.cjs +1 -0
  19. package/dist/workers/imageWorker.d.ts +4 -0
  20. package/dist/workers/utils/emscripten.d.ts +6 -0
  21. package/package.json +49 -0
  22. package/src/components/ImageCompressor.vue +304 -0
  23. package/src/composables/useCompression.ts +314 -0
  24. package/src/composables/useEncoderRegistry.ts +70 -0
  25. package/src/composables/useWorker.ts +132 -0
  26. package/src/constants/encoders.ts +137 -0
  27. package/src/constants/resizeMethods.ts +23 -0
  28. package/src/index.ts +63 -0
  29. package/src/types/compression.ts +38 -0
  30. package/src/types/encoder.ts +144 -0
  31. package/src/types/processor.ts +36 -0
  32. package/src/types/worker.ts +29 -0
  33. package/src/utils/file.ts +48 -0
  34. package/src/utils/image.ts +90 -0
  35. package/src/workers/imageWorker.ts +107 -0
  36. package/src/workers/utils/emscripten.ts +16 -0
@@ -0,0 +1,48 @@
1
+ /**
2
+ * 文件工具函数
3
+ */
4
+
5
+ /**
6
+ * 格式化文件大小
7
+ */
8
+ export function formatBytes(bytes: number, decimals = 2): string {
9
+ if (bytes === 0) return '0 Bytes';
10
+ const k = 1024;
11
+ const dm = decimals < 0 ? 0 : decimals;
12
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
13
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
14
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
15
+ }
16
+
17
+ /**
18
+ * 计算节省百分比
19
+ */
20
+ export function calculateSavings(originalSize: number, compressedSize: number): number {
21
+ if (originalSize === 0) return 0;
22
+ return Math.round(((originalSize - compressedSize) / originalSize) * 100);
23
+ }
24
+
25
+ /**
26
+ * 生成压缩后的文件名
27
+ */
28
+ export function generateCompressedFilename(
29
+ originalName: string,
30
+ extension: string
31
+ ): string {
32
+ const baseName = originalName.replace(/\.[^.]+$/, '');
33
+ return `${baseName}_compressed.${extension}`;
34
+ }
35
+
36
+ /**
37
+ * 创建 Blob URL
38
+ */
39
+ export function createBlobUrl(blob: Blob): string {
40
+ return URL.createObjectURL(blob);
41
+ }
42
+
43
+ /**
44
+ * 释放 Blob URL
45
+ */
46
+ export function revokeBlobUrl(url: string): void {
47
+ URL.revokeObjectURL(url);
48
+ }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * 图像工具函数
3
+ */
4
+
5
+ /**
6
+ * 从 Blob 解码图片为 ImageData
7
+ * 使用浏览器内置的 createImageBitmap
8
+ */
9
+ export async function blobToImageData(blob: Blob): Promise<ImageData> {
10
+ const bitmap = await createImageBitmap(blob);
11
+ const canvas = document.createElement('canvas');
12
+ canvas.width = bitmap.width;
13
+ canvas.height = bitmap.height;
14
+ const ctx = canvas.getContext('2d')!;
15
+ ctx.drawImage(bitmap, 0, 0);
16
+ return ctx.getImageData(0, 0, canvas.width, canvas.height);
17
+ }
18
+
19
+ /**
20
+ * ImageData 转 Blob(用于浏览器编码器如 JPEG/PNG)
21
+ */
22
+ export async function imageDataToBlob(
23
+ imageData: ImageData,
24
+ mimeType: string,
25
+ quality?: number
26
+ ): Promise<Blob> {
27
+ const canvas = document.createElement('canvas');
28
+ canvas.width = imageData.width;
29
+ canvas.height = imageData.height;
30
+ const ctx = canvas.getContext('2d')!;
31
+ ctx.putImageData(imageData, 0, 0);
32
+ return new Promise((resolve) => {
33
+ canvas.toBlob(
34
+ (blob) => resolve(blob!),
35
+ mimeType,
36
+ quality
37
+ );
38
+ });
39
+ }
40
+
41
+ /**
42
+ * 从文件获取图片尺寸
43
+ */
44
+ export function getImageDimensions(file: File): Promise<{ width: number; height: number }> {
45
+ return new Promise((resolve, reject) => {
46
+ const img = new Image();
47
+ img.onload = () => {
48
+ resolve({ width: img.width, height: img.height });
49
+ URL.revokeObjectURL(img.src);
50
+ };
51
+ img.onerror = reject;
52
+ img.src = URL.createObjectURL(file);
53
+ });
54
+ }
55
+
56
+ /**
57
+ * 检测图片 MIME 类型
58
+ */
59
+ export async function sniffMimeType(blob: Blob): Promise<string> {
60
+ const header = new Uint8Array(await blob.slice(0, 4).arrayBuffer());
61
+ if (header[0] === 0x89 && header[1] === 0x50) return 'image/png';
62
+ if (header[0] === 0xff && header[1] === 0xd8) return 'image/jpeg';
63
+ if (header[0] === 0x47 && header[1] === 0x49) return 'image/gif';
64
+ if (header[0] === 0x52 && header[1] === 0x49) return 'image/webp';
65
+ if (header[0] === 0x00 && header[1] === 0x00) return 'image/avif';
66
+ return blob.type || 'image/jpeg';
67
+ }
68
+
69
+ /**
70
+ * 检查浏览器是否支持解码某图片格式
71
+ */
72
+ export function canDecodeImageType(mimeType: string): Promise<boolean> {
73
+ return new Promise((resolve) => {
74
+ const img = new Image();
75
+ img.onload = () => resolve(true);
76
+ img.onerror = () => resolve(false);
77
+ img.src = `data:${mimeType};base64,`;
78
+ });
79
+ }
80
+
81
+ /**
82
+ * ArrayBuffer 转 File
83
+ */
84
+ export function arrayBufferToFile(
85
+ buffer: ArrayBuffer,
86
+ filename: string,
87
+ mimeType: string
88
+ ): File {
89
+ return new File([buffer], filename, { type: mimeType });
90
+ }
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Web Worker 入口
3
+ * 对标 Squoosh: src/features-worker/index.ts
4
+ * 聚合所有编解码器,通过消息传递暴露 API
5
+ */
6
+
7
+ import type { WorkerApi } from '../types/worker';
8
+
9
+ // 简化版:使用 postMessage 通信
10
+ // 实际使用 comlink 时会更简洁
11
+
12
+ const ctx = self as DedicatedWorkerGlobalScope;
13
+
14
+ // 缓存 WASM 模块
15
+ const moduleCache = new Map<string, any>();
16
+
17
+ async function getModule(name: string, factory: any) {
18
+ if (!moduleCache.has(name)) {
19
+ moduleCache.set(name, factory());
20
+ }
21
+ return moduleCache.get(name);
22
+ }
23
+
24
+ // 模拟编码器实现(实际项目中加载 WASM)
25
+ const workerApi: WorkerApi = {
26
+ // Decoders
27
+ async avifDecode(blob: Blob): Promise<ImageData> {
28
+ // 实际加载 codecs/avif/dec/avif_dec.js
29
+ throw new Error('AVIF decode not implemented in worker');
30
+ },
31
+ async jxlDecode(blob: Blob): Promise<ImageData> {
32
+ throw new Error('JXL decode not implemented in worker');
33
+ },
34
+ async qoiDecode(blob: Blob): Promise<ImageData> {
35
+ throw new Error('QOI decode not implemented in worker');
36
+ },
37
+ async webpDecode(blob: Blob): Promise<ImageData> {
38
+ throw new Error('WebP decode not implemented in worker');
39
+ },
40
+ async wp2Decode(blob: Blob): Promise<ImageData> {
41
+ throw new Error('WebP2 decode not implemented in worker');
42
+ },
43
+
44
+ // Encoders
45
+ async avifEncode(data: ImageData, options: any): Promise<Uint8Array> {
46
+ throw new Error('AVIF encode not implemented in worker');
47
+ },
48
+ async jxlEncode(data: ImageData, options: any): Promise<Uint8Array> {
49
+ throw new Error('JXL encode not implemented in worker');
50
+ },
51
+ async mozjpegEncode(data: ImageData, options: any): Promise<Uint8Array> {
52
+ // 实际加载 codecs/mozjpeg/enc/mozjpeg_enc.js
53
+ // const module = await initEmscriptenModule(mozjpeg_enc);
54
+ // return module.encode(data.data, data.width, data.height, options);
55
+ throw new Error('MozJPEG encode not implemented in worker');
56
+ },
57
+ async oxipngEncode(data: ImageData, options: any): Promise<Uint8Array> {
58
+ throw new Error('OxiPNG encode not implemented in worker');
59
+ },
60
+ async qoiEncode(data: ImageData, options: any): Promise<Uint8Array> {
61
+ throw new Error('QOI encode not implemented in worker');
62
+ },
63
+ async webpEncode(data: ImageData, options: any): Promise<Uint8Array> {
64
+ throw new Error('WebP encode not implemented in worker');
65
+ },
66
+ async wp2Encode(data: ImageData, options: any): Promise<Uint8Array> {
67
+ throw new Error('WebP2 encode not implemented in worker');
68
+ },
69
+
70
+ // Preprocessors
71
+ async rotate(data: ImageData, options: any): Promise<ImageData> {
72
+ // 实际加载 codecs/rotate/rotate.js
73
+ throw new Error('Rotate not implemented in worker');
74
+ },
75
+
76
+ // Processors
77
+ async quantize(data: ImageData, options: any): Promise<ImageData> {
78
+ // 实际加载 codecs/imagequant/imagequant.js
79
+ throw new Error('Quantize not implemented in worker');
80
+ },
81
+ async resize(data: ImageData, options: any): Promise<ImageData> {
82
+ // 实际加载 codecs/resize/resize.js
83
+ throw new Error('Resize not implemented in worker');
84
+ },
85
+ };
86
+
87
+ // 消息处理
88
+ ctx.addEventListener('message', async (event) => {
89
+ const { id, method, args } = event.data;
90
+
91
+ try {
92
+ const fn = (workerApi as any)[method];
93
+ if (!fn) {
94
+ throw new Error(`Unknown method: ${method}`);
95
+ }
96
+ const result = await fn.apply(workerApi, args);
97
+ ctx.postMessage({ id, result, error: null });
98
+ } catch (err) {
99
+ ctx.postMessage({
100
+ id,
101
+ result: null,
102
+ error: err instanceof Error ? err.message : String(err),
103
+ });
104
+ }
105
+ });
106
+
107
+ export default workerApi;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Emscripten WASM 模块初始化工具
3
+ * 对标 Squoosh: src/features/worker-utils/index.ts
4
+ */
5
+
6
+ export function initEmscriptenModule<T extends EmscriptenWasm.Module>(
7
+ moduleFactory: EmscriptenWasm.ModuleFactory<T>
8
+ ): Promise<T> {
9
+ return moduleFactory({
10
+ noInitialRun: true,
11
+ });
12
+ }
13
+
14
+ export function blobToArrayBuffer(blob: Blob): Promise<ArrayBuffer> {
15
+ return new Response(blob).arrayBuffer();
16
+ }