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.
- package/README.md +174 -0
- package/dist/assets/imageWorker-DyeUTFOy.js +67 -0
- package/dist/components/ImageCompressor.vue.d.ts +38 -0
- package/dist/composables/useCompression.d.ts +170 -0
- package/dist/composables/useEncoderRegistry.d.ts +17 -0
- package/dist/composables/useWorker.d.ts +11 -0
- package/dist/constants/encoders.d.ts +5 -0
- package/dist/constants/resizeMethods.d.ts +39 -0
- package/dist/index.d.ts +15 -0
- package/dist/style.css +1 -0
- package/dist/types/compression.d.ts +35 -0
- package/dist/types/encoder.d.ts +100 -0
- package/dist/types/processor.d.ts +30 -0
- package/dist/types/worker.d.ts +21 -0
- package/dist/utils/file.d.ts +23 -0
- package/dist/utils/image.d.ts +31 -0
- package/dist/vue-image-compressor.js +660 -0
- package/dist/vue-image-compressor.umd.cjs +1 -0
- package/dist/workers/imageWorker.d.ts +4 -0
- package/dist/workers/utils/emscripten.d.ts +6 -0
- package/package.json +49 -0
- package/src/components/ImageCompressor.vue +304 -0
- package/src/composables/useCompression.ts +314 -0
- package/src/composables/useEncoderRegistry.ts +70 -0
- package/src/composables/useWorker.ts +132 -0
- package/src/constants/encoders.ts +137 -0
- package/src/constants/resizeMethods.ts +23 -0
- package/src/index.ts +63 -0
- package/src/types/compression.ts +38 -0
- package/src/types/encoder.ts +144 -0
- package/src/types/processor.ts +36 -0
- package/src/types/worker.ts +29 -0
- package/src/utils/file.ts +48 -0
- package/src/utils/image.ts +90 -0
- package/src/workers/imageWorker.ts +107 -0
- package/src/workers/utils/emscripten.ts +16 -0
|
@@ -0,0 +1,660 @@
|
|
|
1
|
+
import { ref as b, onUnmounted as Q, computed as q, defineComponent as H, watch as K, openBlock as _, createElementBlock as k, createElementVNode as g, withModifiers as N, renderSlot as S, toDisplayString as T, unref as m, createCommentVNode as z, withDirectives as X, isRef as Y, Fragment as Z, renderList as ee, vModelSelect as te, createTextVNode as ne } from "vue";
|
|
2
|
+
const L = {
|
|
3
|
+
mozJPEG: {
|
|
4
|
+
label: "MozJPEG",
|
|
5
|
+
mimeType: "image/jpeg",
|
|
6
|
+
extension: "jpg"
|
|
7
|
+
},
|
|
8
|
+
webP: {
|
|
9
|
+
label: "WebP",
|
|
10
|
+
mimeType: "image/webp",
|
|
11
|
+
extension: "webp"
|
|
12
|
+
},
|
|
13
|
+
avif: {
|
|
14
|
+
label: "AVIF",
|
|
15
|
+
mimeType: "image/avif",
|
|
16
|
+
extension: "avif"
|
|
17
|
+
},
|
|
18
|
+
jxl: {
|
|
19
|
+
label: "JPEG XL",
|
|
20
|
+
mimeType: "image/jxl",
|
|
21
|
+
extension: "jxl"
|
|
22
|
+
},
|
|
23
|
+
oxiPNG: {
|
|
24
|
+
label: "OxiPNG",
|
|
25
|
+
mimeType: "image/png",
|
|
26
|
+
extension: "png"
|
|
27
|
+
},
|
|
28
|
+
browserJPEG: {
|
|
29
|
+
label: "Browser JPEG",
|
|
30
|
+
mimeType: "image/jpeg",
|
|
31
|
+
extension: "jpg"
|
|
32
|
+
},
|
|
33
|
+
browserPNG: {
|
|
34
|
+
label: "Browser PNG",
|
|
35
|
+
mimeType: "image/png",
|
|
36
|
+
extension: "png"
|
|
37
|
+
},
|
|
38
|
+
browserGIF: {
|
|
39
|
+
label: "Browser GIF",
|
|
40
|
+
mimeType: "image/gif",
|
|
41
|
+
extension: "gif"
|
|
42
|
+
},
|
|
43
|
+
qoi: {
|
|
44
|
+
label: "QOI",
|
|
45
|
+
mimeType: "image/qoi",
|
|
46
|
+
extension: "qoi"
|
|
47
|
+
},
|
|
48
|
+
wp2: {
|
|
49
|
+
label: "WebP2",
|
|
50
|
+
mimeType: "image/webp2",
|
|
51
|
+
extension: "wp2"
|
|
52
|
+
}
|
|
53
|
+
}, W = {
|
|
54
|
+
mozJPEG: {
|
|
55
|
+
quality: 75,
|
|
56
|
+
baseline: !1,
|
|
57
|
+
arithmetic: !1,
|
|
58
|
+
progressive: !0,
|
|
59
|
+
optimize_coding: !0,
|
|
60
|
+
smoothing: 0,
|
|
61
|
+
color_space: 3,
|
|
62
|
+
quant_table: 3,
|
|
63
|
+
trellis_multipass: !1,
|
|
64
|
+
trellis_opt_zero: !1,
|
|
65
|
+
trellis_opt_table: !1,
|
|
66
|
+
trellis_loops: 1,
|
|
67
|
+
auto_subsample: !0,
|
|
68
|
+
chroma_subsample: 2,
|
|
69
|
+
separate_chroma_quality: !1,
|
|
70
|
+
chroma_quality: 75
|
|
71
|
+
},
|
|
72
|
+
webP: {
|
|
73
|
+
quality: 75,
|
|
74
|
+
target_size: 0,
|
|
75
|
+
target_PSNR: 0,
|
|
76
|
+
method: 4,
|
|
77
|
+
sns_strength: 50,
|
|
78
|
+
filter_strength: 60,
|
|
79
|
+
filter_sharpness: 0,
|
|
80
|
+
filter_type: 1,
|
|
81
|
+
partitions: 0,
|
|
82
|
+
segments: 4,
|
|
83
|
+
pass: 1,
|
|
84
|
+
show_compressed: 0,
|
|
85
|
+
preprocessing: 0,
|
|
86
|
+
autofilter: 0,
|
|
87
|
+
partition_limit: 0,
|
|
88
|
+
alpha_compression: 1,
|
|
89
|
+
alpha_filtering: 1,
|
|
90
|
+
alpha_quality: 100,
|
|
91
|
+
lossless: 0,
|
|
92
|
+
exact: 0,
|
|
93
|
+
use_delta_palette: 0,
|
|
94
|
+
vlnr: 0,
|
|
95
|
+
near_lossless: 60
|
|
96
|
+
},
|
|
97
|
+
avif: {
|
|
98
|
+
cqLevel: 33,
|
|
99
|
+
denoiseLevel: 0,
|
|
100
|
+
cqAlphaLevel: -1,
|
|
101
|
+
tileRows: 0,
|
|
102
|
+
tileCols: 0,
|
|
103
|
+
speed: 6,
|
|
104
|
+
subsample: 1,
|
|
105
|
+
chromaDeltaQ: !1,
|
|
106
|
+
sharpness: 0,
|
|
107
|
+
tune: 0
|
|
108
|
+
},
|
|
109
|
+
jxl: {
|
|
110
|
+
effort: 7,
|
|
111
|
+
quality: 75,
|
|
112
|
+
progressive: !1,
|
|
113
|
+
targetPsize: 0
|
|
114
|
+
},
|
|
115
|
+
oxiPNG: {
|
|
116
|
+
level: 2
|
|
117
|
+
},
|
|
118
|
+
browserJPEG: {
|
|
119
|
+
quality: 0.75
|
|
120
|
+
},
|
|
121
|
+
browserPNG: {},
|
|
122
|
+
browserGIF: {},
|
|
123
|
+
qoi: {},
|
|
124
|
+
wp2: {
|
|
125
|
+
quality: 75
|
|
126
|
+
}
|
|
127
|
+
}, re = Object.keys(L);
|
|
128
|
+
async function oe(t) {
|
|
129
|
+
const e = await createImageBitmap(t), r = document.createElement("canvas");
|
|
130
|
+
r.width = e.width, r.height = e.height;
|
|
131
|
+
const n = r.getContext("2d");
|
|
132
|
+
return n.drawImage(e, 0, 0), n.getImageData(0, 0, r.width, r.height);
|
|
133
|
+
}
|
|
134
|
+
async function J(t, e, r) {
|
|
135
|
+
const n = document.createElement("canvas");
|
|
136
|
+
return n.width = t.width, n.height = t.height, n.getContext("2d").putImageData(t, 0, 0), new Promise((p) => {
|
|
137
|
+
n.toBlob(
|
|
138
|
+
(c) => p(c),
|
|
139
|
+
e,
|
|
140
|
+
r
|
|
141
|
+
);
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
function Ge(t) {
|
|
145
|
+
return new Promise((e, r) => {
|
|
146
|
+
const n = new Image();
|
|
147
|
+
n.onload = () => {
|
|
148
|
+
e({ width: n.width, height: n.height }), URL.revokeObjectURL(n.src);
|
|
149
|
+
}, n.onerror = r, n.src = URL.createObjectURL(t);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
async function se(t) {
|
|
153
|
+
const e = new Uint8Array(await t.slice(0, 4).arrayBuffer());
|
|
154
|
+
return e[0] === 137 && e[1] === 80 ? "image/png" : e[0] === 255 && e[1] === 216 ? "image/jpeg" : e[0] === 71 && e[1] === 73 ? "image/gif" : e[0] === 82 && e[1] === 73 ? "image/webp" : e[0] === 0 && e[1] === 0 ? "image/avif" : t.type || "image/jpeg";
|
|
155
|
+
}
|
|
156
|
+
function ae(t) {
|
|
157
|
+
return new Promise((e) => {
|
|
158
|
+
const r = new Image();
|
|
159
|
+
r.onload = () => e(!0), r.onerror = () => e(!1), r.src = `data:${t};base64,`;
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
function ie(t, e, r) {
|
|
163
|
+
return new File([t], e, { type: r });
|
|
164
|
+
}
|
|
165
|
+
function $(t, e = 2) {
|
|
166
|
+
if (t === 0) return "0 Bytes";
|
|
167
|
+
const r = 1024, n = e < 0 ? 0 : e, u = ["Bytes", "KB", "MB", "GB"], p = Math.floor(Math.log(t) / Math.log(r));
|
|
168
|
+
return parseFloat((t / Math.pow(r, p)).toFixed(n)) + " " + u[p];
|
|
169
|
+
}
|
|
170
|
+
function le(t, e) {
|
|
171
|
+
return t === 0 ? 0 : Math.round((t - e) / t * 100);
|
|
172
|
+
}
|
|
173
|
+
function ce(t, e) {
|
|
174
|
+
return `${t.replace(/\.[^.]+$/, "")}_compressed.${e}`;
|
|
175
|
+
}
|
|
176
|
+
function V(t) {
|
|
177
|
+
return URL.createObjectURL(t);
|
|
178
|
+
}
|
|
179
|
+
function ue(t) {
|
|
180
|
+
URL.revokeObjectURL(t);
|
|
181
|
+
}
|
|
182
|
+
const de = 1e4;
|
|
183
|
+
function me() {
|
|
184
|
+
const t = b(!1), e = b(!1), r = b(null);
|
|
185
|
+
let n = null, u = null;
|
|
186
|
+
function p() {
|
|
187
|
+
if (!n)
|
|
188
|
+
try {
|
|
189
|
+
e.value = !0, n = new Worker(
|
|
190
|
+
new URL(
|
|
191
|
+
/* @vite-ignore */
|
|
192
|
+
"/assets/imageWorker-DyeUTFOy.js",
|
|
193
|
+
import.meta.url
|
|
194
|
+
),
|
|
195
|
+
{ type: "module" }
|
|
196
|
+
), t.value = !0, r.value = null;
|
|
197
|
+
} catch (x) {
|
|
198
|
+
r.value = x instanceof Error ? x : new Error("Failed to start worker"), console.error("Worker start failed:", x);
|
|
199
|
+
} finally {
|
|
200
|
+
e.value = !1;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function c() {
|
|
204
|
+
u && (clearTimeout(u), u = null), n && (n.terminate(), n = null, t.value = !1);
|
|
205
|
+
}
|
|
206
|
+
function h() {
|
|
207
|
+
u && clearTimeout(u), u = window.setTimeout(() => {
|
|
208
|
+
c();
|
|
209
|
+
}, de);
|
|
210
|
+
}
|
|
211
|
+
async function w() {
|
|
212
|
+
if (n || p(), !n)
|
|
213
|
+
throw new Error("Worker failed to initialize");
|
|
214
|
+
return n;
|
|
215
|
+
}
|
|
216
|
+
async function P(x, C) {
|
|
217
|
+
if (x.aborted)
|
|
218
|
+
throw new DOMException("AbortError", "AbortError");
|
|
219
|
+
return clearTimeout(u), new Promise((G, R) => {
|
|
220
|
+
const O = () => {
|
|
221
|
+
c(), R(new DOMException("AbortError", "AbortError"));
|
|
222
|
+
};
|
|
223
|
+
x.addEventListener("abort", O), C().then(G).catch(R).finally(() => {
|
|
224
|
+
x.removeEventListener("abort", O), h();
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
return Q(() => {
|
|
229
|
+
c();
|
|
230
|
+
}), {
|
|
231
|
+
isReady: t,
|
|
232
|
+
isLoading: e,
|
|
233
|
+
error: r,
|
|
234
|
+
worker: n,
|
|
235
|
+
getWorkerApi: w,
|
|
236
|
+
executeTask: P,
|
|
237
|
+
terminateWorker: c
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
function pe() {
|
|
241
|
+
const t = b(!1), e = b(0), r = b(null), n = b(null), u = b(null), { getWorkerApi: p, executeTask: c } = me(), h = q(() => t.value && !!u.value);
|
|
242
|
+
async function w(d, o) {
|
|
243
|
+
const a = await se(o), i = await ae(a), f = await p();
|
|
244
|
+
if (!i) {
|
|
245
|
+
if (a === "image/avif")
|
|
246
|
+
return c(d, () => f.avifDecode(o));
|
|
247
|
+
if (a === "image/webp")
|
|
248
|
+
return c(d, () => f.webpDecode(o));
|
|
249
|
+
if (a === "image/jxl")
|
|
250
|
+
return c(d, () => f.jxlDecode(o));
|
|
251
|
+
if (a === "image/webp2")
|
|
252
|
+
return c(d, () => f.wp2Decode(o));
|
|
253
|
+
if (a === "image/qoi")
|
|
254
|
+
return c(d, () => f.qoiDecode(o));
|
|
255
|
+
}
|
|
256
|
+
return oe(o);
|
|
257
|
+
}
|
|
258
|
+
async function P(d, o, a) {
|
|
259
|
+
if (a === 0) return o;
|
|
260
|
+
const i = await p();
|
|
261
|
+
return c(
|
|
262
|
+
d,
|
|
263
|
+
() => i.rotate(o, { rotate: a })
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
async function x(d, o, a) {
|
|
267
|
+
var v, y;
|
|
268
|
+
let i = o;
|
|
269
|
+
const f = await p();
|
|
270
|
+
return (v = a.resize) != null && v.enabled && (i = await c(
|
|
271
|
+
d,
|
|
272
|
+
() => f.resize(i, {
|
|
273
|
+
width: a.resize.width || i.width,
|
|
274
|
+
height: a.resize.height || i.height,
|
|
275
|
+
method: a.resize.method || "lanczos3",
|
|
276
|
+
fitMethod: a.resize.fitMethod || "stretch",
|
|
277
|
+
premultiply: !0,
|
|
278
|
+
linearRGB: !0
|
|
279
|
+
})
|
|
280
|
+
)), (y = a.quantize) != null && y.enabled && (i = await c(
|
|
281
|
+
d,
|
|
282
|
+
() => f.quantize(i, {
|
|
283
|
+
numColors: a.quantize.numColors || 256,
|
|
284
|
+
dither: a.quantize.dither || 1
|
|
285
|
+
})
|
|
286
|
+
)), i;
|
|
287
|
+
}
|
|
288
|
+
async function C(d, o, a) {
|
|
289
|
+
const i = await p(), f = a.encoder;
|
|
290
|
+
if (f === "browserJPEG")
|
|
291
|
+
return (await J(
|
|
292
|
+
o,
|
|
293
|
+
"image/jpeg",
|
|
294
|
+
a.encoderOptions.quality
|
|
295
|
+
)).arrayBuffer();
|
|
296
|
+
if (f === "browserPNG")
|
|
297
|
+
return (await J(o, "image/png")).arrayBuffer();
|
|
298
|
+
if (f === "browserGIF")
|
|
299
|
+
throw new Error("Browser GIF encoding not supported directly");
|
|
300
|
+
const y = {
|
|
301
|
+
mozJPEG: i.mozjpegEncode,
|
|
302
|
+
webP: i.webpEncode,
|
|
303
|
+
avif: i.avifEncode,
|
|
304
|
+
jxl: i.jxlEncode,
|
|
305
|
+
oxiPNG: i.oxipngEncode,
|
|
306
|
+
qoi: i.qoiEncode,
|
|
307
|
+
wp2: i.wp2Encode
|
|
308
|
+
}[f];
|
|
309
|
+
if (!y)
|
|
310
|
+
throw new Error(`Unknown encoder: ${f}`);
|
|
311
|
+
return (await c(
|
|
312
|
+
d,
|
|
313
|
+
() => y.call(i, o, a.encoderOptions)
|
|
314
|
+
)).buffer;
|
|
315
|
+
}
|
|
316
|
+
async function G(d, o) {
|
|
317
|
+
var f;
|
|
318
|
+
u.value = new AbortController();
|
|
319
|
+
const a = u.value.signal;
|
|
320
|
+
t.value = !0, e.value = 0, r.value = null;
|
|
321
|
+
const i = n.value;
|
|
322
|
+
n.value = null, (f = i == null ? void 0 : i.compressed) != null && f.blobUrl && ue(i.compressed.blobUrl);
|
|
323
|
+
try {
|
|
324
|
+
e.value = 10;
|
|
325
|
+
const v = {
|
|
326
|
+
file: d,
|
|
327
|
+
size: d.size,
|
|
328
|
+
width: 0,
|
|
329
|
+
height: 0,
|
|
330
|
+
blobUrl: V(d)
|
|
331
|
+
}, y = await w(a, d);
|
|
332
|
+
v.width = y.width, v.height = y.height, e.value = 25;
|
|
333
|
+
const j = await P(
|
|
334
|
+
a,
|
|
335
|
+
y,
|
|
336
|
+
o.rotate || 0
|
|
337
|
+
);
|
|
338
|
+
e.value = 35;
|
|
339
|
+
const U = await x(a, j, o);
|
|
340
|
+
e.value = 50;
|
|
341
|
+
const F = await C(a, U, o);
|
|
342
|
+
e.value = 80;
|
|
343
|
+
const I = L[o.encoder], M = ce(
|
|
344
|
+
d.name,
|
|
345
|
+
(I == null ? void 0 : I.extension) || "bin"
|
|
346
|
+
), D = ie(
|
|
347
|
+
F,
|
|
348
|
+
M,
|
|
349
|
+
(I == null ? void 0 : I.mimeType) || "application/octet-stream"
|
|
350
|
+
), B = {
|
|
351
|
+
file: D,
|
|
352
|
+
size: D.size,
|
|
353
|
+
width: U.width,
|
|
354
|
+
height: U.height,
|
|
355
|
+
blobUrl: V(D)
|
|
356
|
+
}, l = {
|
|
357
|
+
original: v,
|
|
358
|
+
compressed: B,
|
|
359
|
+
savingsBytes: v.size - B.size,
|
|
360
|
+
savingsPercent: le(v.size, B.size),
|
|
361
|
+
encoderType: o.encoder,
|
|
362
|
+
encoderOptions: o.encoderOptions
|
|
363
|
+
};
|
|
364
|
+
return n.value = l, e.value = 100, l;
|
|
365
|
+
} catch (v) {
|
|
366
|
+
if (v instanceof DOMException && v.name === "AbortError")
|
|
367
|
+
throw r.value = "已取消", v;
|
|
368
|
+
const y = v instanceof Error ? v.message : "压缩失败";
|
|
369
|
+
throw r.value = y, console.error("Compression error:", v), v;
|
|
370
|
+
} finally {
|
|
371
|
+
t.value = !1, u.value = null;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
function R() {
|
|
375
|
+
u.value && u.value.abort();
|
|
376
|
+
}
|
|
377
|
+
function O() {
|
|
378
|
+
if (!n.value) return;
|
|
379
|
+
const { compressed: d } = n.value, o = document.createElement("a");
|
|
380
|
+
o.href = d.blobUrl, o.download = d.file.name, document.body.appendChild(o), o.click(), document.body.removeChild(o);
|
|
381
|
+
}
|
|
382
|
+
return {
|
|
383
|
+
compress: G,
|
|
384
|
+
cancel: R,
|
|
385
|
+
downloadResult: O,
|
|
386
|
+
isCompressing: t,
|
|
387
|
+
progress: e,
|
|
388
|
+
error: r,
|
|
389
|
+
result: n,
|
|
390
|
+
canCancel: h
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
function fe() {
|
|
394
|
+
const t = b("mozJPEG"), e = b({}), r = q(() => L[t.value]), n = q(
|
|
395
|
+
() => W[t.value]
|
|
396
|
+
), u = q(
|
|
397
|
+
() => re.map((w) => ({
|
|
398
|
+
type: w,
|
|
399
|
+
label: L[w].label,
|
|
400
|
+
mimeType: L[w].mimeType,
|
|
401
|
+
extension: L[w].extension
|
|
402
|
+
}))
|
|
403
|
+
);
|
|
404
|
+
function p(w) {
|
|
405
|
+
t.value = w, e.value = { ...W[w] };
|
|
406
|
+
}
|
|
407
|
+
function c(w, P) {
|
|
408
|
+
e.value = {
|
|
409
|
+
...e.value,
|
|
410
|
+
[w]: P
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
function h() {
|
|
414
|
+
e.value = { ...n.value };
|
|
415
|
+
}
|
|
416
|
+
return h(), {
|
|
417
|
+
selectedEncoder: t,
|
|
418
|
+
encoderOptions: e,
|
|
419
|
+
currentMeta: r,
|
|
420
|
+
currentDefaults: n,
|
|
421
|
+
availableEncoders: u,
|
|
422
|
+
selectEncoder: p,
|
|
423
|
+
updateOption: c,
|
|
424
|
+
resetOptions: h
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
const ve = { class: "image-compressor" }, ge = {
|
|
428
|
+
key: 0,
|
|
429
|
+
class: "preview-area"
|
|
430
|
+
}, we = { class: "preview-item" }, be = ["src"], he = { class: "preview-item" }, ye = ["src"], Ee = {
|
|
431
|
+
key: 1,
|
|
432
|
+
class: "compressing"
|
|
433
|
+
}, _e = ["value"], ke = { key: 2 }, xe = {
|
|
434
|
+
key: 1,
|
|
435
|
+
class: "encoder-settings"
|
|
436
|
+
}, Oe = ["value"], Te = {
|
|
437
|
+
key: 0,
|
|
438
|
+
class: "quality-control"
|
|
439
|
+
}, ze = ["value"], Pe = {
|
|
440
|
+
key: 2,
|
|
441
|
+
class: "actions"
|
|
442
|
+
}, Ie = ["disabled"], Ce = {
|
|
443
|
+
key: 3,
|
|
444
|
+
class: "error"
|
|
445
|
+
}, Re = /* @__PURE__ */ H({
|
|
446
|
+
__name: "ImageCompressor",
|
|
447
|
+
props: {
|
|
448
|
+
defaultEncoder: {},
|
|
449
|
+
defaultOptions: {}
|
|
450
|
+
},
|
|
451
|
+
emits: ["success", "error", "cancel"],
|
|
452
|
+
setup(t, { emit: e }) {
|
|
453
|
+
const r = t, n = e, u = b(null), p = b(null), c = b(""), h = b(""), {
|
|
454
|
+
compress: w,
|
|
455
|
+
cancel: P,
|
|
456
|
+
downloadResult: x,
|
|
457
|
+
isCompressing: C,
|
|
458
|
+
progress: G,
|
|
459
|
+
error: R,
|
|
460
|
+
result: O,
|
|
461
|
+
canCancel: d
|
|
462
|
+
} = pe(), {
|
|
463
|
+
selectedEncoder: o,
|
|
464
|
+
encoderOptions: a,
|
|
465
|
+
availableEncoders: i,
|
|
466
|
+
selectEncoder: f,
|
|
467
|
+
updateOption: v
|
|
468
|
+
} = fe();
|
|
469
|
+
r.defaultEncoder && f(r.defaultEncoder), r.defaultOptions && Object.entries(r.defaultOptions).forEach(([l, s]) => v(l, s));
|
|
470
|
+
const y = q(
|
|
471
|
+
() => p.value ? $(p.value.size) : ""
|
|
472
|
+
), j = q(
|
|
473
|
+
() => O.value ? $(O.value.compressed.size) : ""
|
|
474
|
+
), U = q(
|
|
475
|
+
() => ["mozJPEG", "webP", "jxl", "browserJPEG", "wp2"].includes(o.value)
|
|
476
|
+
);
|
|
477
|
+
function F(l) {
|
|
478
|
+
const s = l.target;
|
|
479
|
+
s.files && s.files[0] && M(s.files[0]);
|
|
480
|
+
}
|
|
481
|
+
function I(l) {
|
|
482
|
+
var s;
|
|
483
|
+
(s = l.dataTransfer) != null && s.files[0] && M(l.dataTransfer.files[0]);
|
|
484
|
+
}
|
|
485
|
+
function M(l) {
|
|
486
|
+
l.type.startsWith("image/") && (p.value = l, c.value = URL.createObjectURL(l), h.value && (URL.revokeObjectURL(h.value), h.value = ""));
|
|
487
|
+
}
|
|
488
|
+
function D() {
|
|
489
|
+
f(o.value);
|
|
490
|
+
}
|
|
491
|
+
async function B() {
|
|
492
|
+
if (p.value)
|
|
493
|
+
try {
|
|
494
|
+
const l = {
|
|
495
|
+
encoder: o.value,
|
|
496
|
+
encoderOptions: { ...a.value }
|
|
497
|
+
}, s = await w(p.value, l);
|
|
498
|
+
h.value = s.compressed.blobUrl, n("success", s);
|
|
499
|
+
} catch (l) {
|
|
500
|
+
if (l instanceof DOMException && l.name === "AbortError") {
|
|
501
|
+
n("cancel");
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
n("error", l instanceof Error ? l : new Error(String(l)));
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
return K(
|
|
508
|
+
() => p.value,
|
|
509
|
+
(l, s) => {
|
|
510
|
+
l && l !== s && c.value && URL.revokeObjectURL(c.value);
|
|
511
|
+
}
|
|
512
|
+
), (l, s) => (_(), k("div", ve, [
|
|
513
|
+
g("div", {
|
|
514
|
+
class: "upload-zone",
|
|
515
|
+
onDrop: N(I, ["prevent"]),
|
|
516
|
+
onDragover: s[1] || (s[1] = N(() => {
|
|
517
|
+
}, ["prevent"]))
|
|
518
|
+
}, [
|
|
519
|
+
g("input", {
|
|
520
|
+
type: "file",
|
|
521
|
+
accept: "image/*",
|
|
522
|
+
onChange: F,
|
|
523
|
+
ref_key: "fileInput",
|
|
524
|
+
ref: u,
|
|
525
|
+
class: "file-input"
|
|
526
|
+
}, null, 544),
|
|
527
|
+
g("div", {
|
|
528
|
+
class: "upload-prompt",
|
|
529
|
+
onClick: s[0] || (s[0] = (E) => {
|
|
530
|
+
var A;
|
|
531
|
+
return (A = u.value) == null ? void 0 : A.click();
|
|
532
|
+
})
|
|
533
|
+
}, [
|
|
534
|
+
S(l.$slots, "upload-prompt", {}, () => [
|
|
535
|
+
s[6] || (s[6] = g("p", null, "拖拽图片到此处,或点击上传", -1))
|
|
536
|
+
], !0)
|
|
537
|
+
])
|
|
538
|
+
], 32),
|
|
539
|
+
c.value ? (_(), k("div", ge, [
|
|
540
|
+
g("div", we, [
|
|
541
|
+
s[7] || (s[7] = g("h4", null, "原始图片", -1)),
|
|
542
|
+
g("img", {
|
|
543
|
+
src: c.value,
|
|
544
|
+
alt: "Original"
|
|
545
|
+
}, null, 8, be),
|
|
546
|
+
g("p", null, T(y.value), 1)
|
|
547
|
+
]),
|
|
548
|
+
g("div", he, [
|
|
549
|
+
s[8] || (s[8] = g("h4", null, "压缩后", -1)),
|
|
550
|
+
h.value ? (_(), k("img", {
|
|
551
|
+
key: 0,
|
|
552
|
+
src: h.value,
|
|
553
|
+
alt: "Compressed"
|
|
554
|
+
}, null, 8, ye)) : m(C) ? (_(), k("div", Ee, [
|
|
555
|
+
g("progress", {
|
|
556
|
+
value: m(G),
|
|
557
|
+
max: "100"
|
|
558
|
+
}, null, 8, _e),
|
|
559
|
+
g("p", null, "压缩中... " + T(m(G)) + "%", 1)
|
|
560
|
+
])) : z("", !0),
|
|
561
|
+
j.value ? (_(), k("p", ke, T(j.value), 1)) : z("", !0)
|
|
562
|
+
])
|
|
563
|
+
])) : z("", !0),
|
|
564
|
+
c.value ? (_(), k("div", xe, [
|
|
565
|
+
s[9] || (s[9] = g("label", null, "编码器:", -1)),
|
|
566
|
+
X(g("select", {
|
|
567
|
+
"onUpdate:modelValue": s[2] || (s[2] = (E) => Y(o) ? o.value = E : null),
|
|
568
|
+
onChange: D
|
|
569
|
+
}, [
|
|
570
|
+
(_(!0), k(Z, null, ee(m(i), (E) => (_(), k("option", {
|
|
571
|
+
key: E.type,
|
|
572
|
+
value: E.type
|
|
573
|
+
}, T(E.label) + " (" + T(E.extension) + ") ", 9, Oe))), 128))
|
|
574
|
+
], 544), [
|
|
575
|
+
[te, m(o)]
|
|
576
|
+
]),
|
|
577
|
+
U.value ? (_(), k("div", Te, [
|
|
578
|
+
g("label", null, [
|
|
579
|
+
ne(" 质量: " + T(m(a).quality ?? 75) + " ", 1),
|
|
580
|
+
g("input", {
|
|
581
|
+
type: "range",
|
|
582
|
+
min: "0",
|
|
583
|
+
max: "100",
|
|
584
|
+
value: m(a).quality ?? 75,
|
|
585
|
+
onInput: s[3] || (s[3] = (E) => m(v)("quality", Number(E.target.value))),
|
|
586
|
+
"-------": "",
|
|
587
|
+
REPLACE: ""
|
|
588
|
+
}, null, 40, ze)
|
|
589
|
+
])
|
|
590
|
+
])) : z("", !0),
|
|
591
|
+
S(l.$slots, "encoder-settings", {
|
|
592
|
+
encoder: m(o),
|
|
593
|
+
options: m(a)
|
|
594
|
+
}, void 0, !0)
|
|
595
|
+
])) : z("", !0),
|
|
596
|
+
c.value ? (_(), k("div", Pe, [
|
|
597
|
+
g("button", {
|
|
598
|
+
onClick: B,
|
|
599
|
+
disabled: m(C),
|
|
600
|
+
class: "btn-primary"
|
|
601
|
+
}, T(m(C) ? "压缩中..." : "开始压缩"), 9, Ie),
|
|
602
|
+
m(d) ? (_(), k("button", {
|
|
603
|
+
key: 0,
|
|
604
|
+
onClick: s[4] || (s[4] = //@ts-ignore
|
|
605
|
+
(...E) => m(P) && m(P)(...E)),
|
|
606
|
+
class: "btn-secondary"
|
|
607
|
+
}, " 取消 ")) : z("", !0),
|
|
608
|
+
m(O) ? (_(), k("button", {
|
|
609
|
+
key: 1,
|
|
610
|
+
onClick: s[5] || (s[5] = //@ts-ignore
|
|
611
|
+
(...E) => m(x) && m(x)(...E)),
|
|
612
|
+
class: "btn-success"
|
|
613
|
+
}, " 下载 (节省 " + T(m(O).savingsPercent) + "%) ", 1)) : z("", !0)
|
|
614
|
+
])) : z("", !0),
|
|
615
|
+
m(R) ? (_(), k("div", Ce, T(m(R)), 1)) : z("", !0)
|
|
616
|
+
]));
|
|
617
|
+
}
|
|
618
|
+
}), Ue = (t, e) => {
|
|
619
|
+
const r = t.__vccOpts || t;
|
|
620
|
+
for (const [n, u] of e)
|
|
621
|
+
r[n] = u;
|
|
622
|
+
return r;
|
|
623
|
+
}, Le = /* @__PURE__ */ Ue(Re, [["__scopeId", "data-v-86f2b482"]]), je = [
|
|
624
|
+
{ value: "lanczos3", label: "Lanczos3" },
|
|
625
|
+
{ value: "catrom", label: "Catrom" },
|
|
626
|
+
{ value: "mitchell", label: "Mitchell" },
|
|
627
|
+
{ value: "triangle", label: "Triangle" },
|
|
628
|
+
{ value: "vector", label: "Vector" }
|
|
629
|
+
], De = [
|
|
630
|
+
{ value: "stretch", label: "拉伸" },
|
|
631
|
+
{ value: "contain", label: "适应" }
|
|
632
|
+
], Be = [
|
|
633
|
+
{ value: 0, label: "不旋转" },
|
|
634
|
+
{ value: 90, label: "顺时针 90°" },
|
|
635
|
+
{ value: 180, label: "旋转 180°" },
|
|
636
|
+
{ value: 270, label: "逆时针 90°" }
|
|
637
|
+
];
|
|
638
|
+
export {
|
|
639
|
+
W as DEFAULT_ENCODER_OPTIONS,
|
|
640
|
+
re as ENCODER_LIST,
|
|
641
|
+
L as ENCODER_REGISTRY,
|
|
642
|
+
De as FIT_METHODS,
|
|
643
|
+
Le as ImageCompressor,
|
|
644
|
+
je as RESIZE_METHODS,
|
|
645
|
+
Be as ROTATE_OPTIONS,
|
|
646
|
+
ie as arrayBufferToFile,
|
|
647
|
+
oe as blobToImageData,
|
|
648
|
+
le as calculateSavings,
|
|
649
|
+
ae as canDecodeImageType,
|
|
650
|
+
V as createBlobUrl,
|
|
651
|
+
$ as formatBytes,
|
|
652
|
+
ce as generateCompressedFilename,
|
|
653
|
+
Ge as getImageDimensions,
|
|
654
|
+
J as imageDataToBlob,
|
|
655
|
+
ue as revokeBlobUrl,
|
|
656
|
+
se as sniffMimeType,
|
|
657
|
+
pe as useCompression,
|
|
658
|
+
fe as useEncoderRegistry,
|
|
659
|
+
me as useWorker
|
|
660
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(s,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],e):(s=typeof globalThis<"u"?globalThis:s||self,e(s.VueImageCompressor={},s.Vue))})(this,function(s,e){"use strict";var v=typeof document<"u"?document.currentScript:null;const B={mozJPEG:{label:"MozJPEG",mimeType:"image/jpeg",extension:"jpg"},webP:{label:"WebP",mimeType:"image/webp",extension:"webp"},avif:{label:"AVIF",mimeType:"image/avif",extension:"avif"},jxl:{label:"JPEG XL",mimeType:"image/jxl",extension:"jxl"},oxiPNG:{label:"OxiPNG",mimeType:"image/png",extension:"png"},browserJPEG:{label:"Browser JPEG",mimeType:"image/jpeg",extension:"jpg"},browserPNG:{label:"Browser PNG",mimeType:"image/png",extension:"png"},browserGIF:{label:"Browser GIF",mimeType:"image/gif",extension:"gif"},qoi:{label:"QOI",mimeType:"image/qoi",extension:"qoi"},wp2:{label:"WebP2",mimeType:"image/webp2",extension:"wp2"}},S={mozJPEG:{quality:75,baseline:!1,arithmetic:!1,progressive:!0,optimize_coding:!0,smoothing:0,color_space:3,quant_table:3,trellis_multipass:!1,trellis_opt_zero:!1,trellis_opt_table:!1,trellis_loops:1,auto_subsample:!0,chroma_subsample:2,separate_chroma_quality:!1,chroma_quality:75},webP:{quality:75,target_size:0,target_PSNR:0,method:4,sns_strength:50,filter_strength:60,filter_sharpness:0,filter_type:1,partitions:0,segments:4,pass:1,show_compressed:0,preprocessing:0,autofilter:0,partition_limit:0,alpha_compression:1,alpha_filtering:1,alpha_quality:100,lossless:0,exact:0,use_delta_palette:0,vlnr:0,near_lossless:60},avif:{cqLevel:33,denoiseLevel:0,cqAlphaLevel:-1,tileRows:0,tileCols:0,speed:6,subsample:1,chromaDeltaQ:!1,sharpness:0,tune:0},jxl:{effort:7,quality:75,progressive:!1,targetPsize:0},oxiPNG:{level:2},browserJPEG:{quality:.75},browserPNG:{},browserGIF:{},qoi:{},wp2:{quality:75}},G=Object.keys(B);async function M(n){const t=await createImageBitmap(n),r=document.createElement("canvas");r.width=t.width,r.height=t.height;const o=r.getContext("2d");return o.drawImage(t,0,0),o.getImageData(0,0,r.width,r.height)}async function L(n,t,r){const o=document.createElement("canvas");return o.width=n.width,o.height=n.height,o.getContext("2d").putImageData(n,0,0),new Promise(p=>{o.toBlob(m=>p(m),t,r)})}function Z(n){return new Promise((t,r)=>{const o=new Image;o.onload=()=>{t({width:o.width,height:o.height}),URL.revokeObjectURL(o.src)},o.onerror=r,o.src=URL.createObjectURL(n)})}async function x(n){const t=new Uint8Array(await n.slice(0,4).arrayBuffer());return t[0]===137&&t[1]===80?"image/png":t[0]===255&&t[1]===216?"image/jpeg":t[0]===71&&t[1]===73?"image/gif":t[0]===82&&t[1]===73?"image/webp":t[0]===0&&t[1]===0?"image/avif":n.type||"image/jpeg"}function F(n){return new Promise(t=>{const r=new Image;r.onload=()=>t(!0),r.onerror=()=>t(!1),r.src=`data:${n};base64,`})}function A(n,t,r){return new File([n],t,{type:r})}function V(n,t=2){if(n===0)return"0 Bytes";const r=1024,o=t<0?0:t,u=["Bytes","KB","MB","GB"],p=Math.floor(Math.log(n)/Math.log(r));return parseFloat((n/Math.pow(r,p)).toFixed(o))+" "+u[p]}function W(n,t){return n===0?0:Math.round((n-t)/n*100)}function J(n,t){return`${n.replace(/\.[^.]+$/,"")}_compressed.${t}`}function q(n){return URL.createObjectURL(n)}function $(n){URL.revokeObjectURL(n)}const X=1e4;function H(){const n=e.ref(!1),t=e.ref(!1),r=e.ref(null);let o=null,u=null;function p(){if(!o)try{t.value=!0,o=new Worker(new URL("/assets/imageWorker-DyeUTFOy.js",typeof document>"u"&&typeof location>"u"?require("url").pathToFileURL(__filename).href:typeof document>"u"?location.href:v&&v.tagName.toUpperCase()==="SCRIPT"&&v.src||new URL("vue-image-compressor.umd.cjs",document.baseURI).href),{type:"module"}),n.value=!0,r.value=null}catch(_){r.value=_ instanceof Error?_:new Error("Failed to start worker"),console.error("Worker start failed:",_)}finally{t.value=!1}}function m(){u&&(clearTimeout(u),u=null),o&&(o.terminate(),o=null,n.value=!1)}function w(){u&&clearTimeout(u),u=window.setTimeout(()=>{m()},X)}async function E(){if(o||p(),!o)throw new Error("Worker failed to initialize");return o}async function T(_,C){if(_.aborted)throw new DOMException("AbortError","AbortError");return clearTimeout(u),new Promise((R,N)=>{const k=()=>{m(),N(new DOMException("AbortError","AbortError"))};_.addEventListener("abort",k),C().then(R).catch(N).finally(()=>{_.removeEventListener("abort",k),w()})})}return e.onUnmounted(()=>{m()}),{isReady:n,isLoading:t,error:r,worker:o,getWorkerApi:E,executeTask:T,terminateWorker:m}}function Q(){const n=e.ref(!1),t=e.ref(0),r=e.ref(null),o=e.ref(null),u=e.ref(null),{getWorkerApi:p,executeTask:m}=H(),w=e.computed(()=>n.value&&!!u.value);async function E(f,a){const i=await x(a),c=await F(i),g=await p();if(!c){if(i==="image/avif")return m(f,()=>g.avifDecode(a));if(i==="image/webp")return m(f,()=>g.webpDecode(a));if(i==="image/jxl")return m(f,()=>g.jxlDecode(a));if(i==="image/webp2")return m(f,()=>g.wp2Decode(a));if(i==="image/qoi")return m(f,()=>g.qoiDecode(a))}return M(a)}async function T(f,a,i){if(i===0)return a;const c=await p();return m(f,()=>c.rotate(a,{rotate:i}))}async function _(f,a,i){var b,h;let c=a;const g=await p();return(b=i.resize)!=null&&b.enabled&&(c=await m(f,()=>g.resize(c,{width:i.resize.width||c.width,height:i.resize.height||c.height,method:i.resize.method||"lanczos3",fitMethod:i.resize.fitMethod||"stretch",premultiply:!0,linearRGB:!0}))),(h=i.quantize)!=null&&h.enabled&&(c=await m(f,()=>g.quantize(c,{numColors:i.quantize.numColors||256,dither:i.quantize.dither||1}))),c}async function C(f,a,i){const c=await p(),g=i.encoder;if(g==="browserJPEG")return(await L(a,"image/jpeg",i.encoderOptions.quality)).arrayBuffer();if(g==="browserPNG")return(await L(a,"image/png")).arrayBuffer();if(g==="browserGIF")throw new Error("Browser GIF encoding not supported directly");const h={mozJPEG:c.mozjpegEncode,webP:c.webpEncode,avif:c.avifEncode,jxl:c.jxlEncode,oxiPNG:c.oxipngEncode,qoi:c.qoiEncode,wp2:c.wp2Encode}[g];if(!h)throw new Error(`Unknown encoder: ${g}`);return(await m(f,()=>h.call(c,a,i.encoderOptions))).buffer}async function R(f,a){var g;u.value=new AbortController;const i=u.value.signal;n.value=!0,t.value=0,r.value=null;const c=o.value;o.value=null,(g=c==null?void 0:c.compressed)!=null&&g.blobUrl&&$(c.compressed.blobUrl);try{t.value=10;const b={file:f,size:f.size,width:0,height:0,blobUrl:q(f)},h=await E(i,f);b.width=h.width,b.height=h.height,t.value=25;const D=await T(i,h,a.rotate||0);t.value=35;const I=await _(i,D,a);t.value=50;const j=await C(i,I,a);t.value=80;const O=B[a.encoder],z=J(f.name,(O==null?void 0:O.extension)||"bin"),P=A(j,z,(O==null?void 0:O.mimeType)||"application/octet-stream"),U={file:P,size:P.size,width:I.width,height:I.height,blobUrl:q(P)},d={original:b,compressed:U,savingsBytes:b.size-U.size,savingsPercent:W(b.size,U.size),encoderType:a.encoder,encoderOptions:a.encoderOptions};return o.value=d,t.value=100,d}catch(b){if(b instanceof DOMException&&b.name==="AbortError")throw r.value="已取消",b;const h=b instanceof Error?b.message:"压缩失败";throw r.value=h,console.error("Compression error:",b),b}finally{n.value=!1,u.value=null}}function N(){u.value&&u.value.abort()}function k(){if(!o.value)return;const{compressed:f}=o.value,a=document.createElement("a");a.href=f.blobUrl,a.download=f.file.name,document.body.appendChild(a),a.click(),document.body.removeChild(a)}return{compress:R,cancel:N,downloadResult:k,isCompressing:n,progress:t,error:r,result:o,canCancel:w}}function K(){const n=e.ref("mozJPEG"),t=e.ref({}),r=e.computed(()=>B[n.value]),o=e.computed(()=>S[n.value]),u=e.computed(()=>G.map(E=>({type:E,label:B[E].label,mimeType:B[E].mimeType,extension:B[E].extension})));function p(E){n.value=E,t.value={...S[E]}}function m(E,T){t.value={...t.value,[E]:T}}function w(){t.value={...o.value}}return w(),{selectedEncoder:n,encoderOptions:t,currentMeta:r,currentDefaults:o,availableEncoders:u,selectEncoder:p,updateOption:m,resetOptions:w}}const ee={class:"image-compressor"},te={key:0,class:"preview-area"},ne={class:"preview-item"},oe=["src"],re={class:"preview-item"},ae=["src"],le={key:1,class:"compressing"},ie=["value"],se={key:2},ce={key:1,class:"encoder-settings"},de=["value"],me={key:0,class:"quality-control"},ue=["value"],fe={key:2,class:"actions"},pe=["disabled"],ge={key:3,class:"error"},be=((n,t)=>{const r=n.__vccOpts||n;for(const[o,u]of t)r[o]=u;return r})(e.defineComponent({__name:"ImageCompressor",props:{defaultEncoder:{},defaultOptions:{}},emits:["success","error","cancel"],setup(n,{emit:t}){const r=n,o=t,u=e.ref(null),p=e.ref(null),m=e.ref(""),w=e.ref(""),{compress:E,cancel:T,downloadResult:_,isCompressing:C,progress:R,error:N,result:k,canCancel:f}=Q(),{selectedEncoder:a,encoderOptions:i,availableEncoders:c,selectEncoder:g,updateOption:b}=K();r.defaultEncoder&&g(r.defaultEncoder),r.defaultOptions&&Object.entries(r.defaultOptions).forEach(([d,l])=>b(d,l));const h=e.computed(()=>p.value?V(p.value.size):""),D=e.computed(()=>k.value?V(k.value.compressed.size):""),I=e.computed(()=>["mozJPEG","webP","jxl","browserJPEG","wp2"].includes(a.value));function j(d){const l=d.target;l.files&&l.files[0]&&z(l.files[0])}function O(d){var l;(l=d.dataTransfer)!=null&&l.files[0]&&z(d.dataTransfer.files[0])}function z(d){d.type.startsWith("image/")&&(p.value=d,m.value=URL.createObjectURL(d),w.value&&(URL.revokeObjectURL(w.value),w.value=""))}function P(){g(a.value)}async function U(){if(p.value)try{const d={encoder:a.value,encoderOptions:{...i.value}},l=await E(p.value,d);w.value=l.compressed.blobUrl,o("success",l)}catch(d){if(d instanceof DOMException&&d.name==="AbortError"){o("cancel");return}o("error",d instanceof Error?d:new Error(String(d)))}}return e.watch(()=>p.value,(d,l)=>{d&&d!==l&&m.value&&URL.revokeObjectURL(m.value)}),(d,l)=>(e.openBlock(),e.createElementBlock("div",ee,[e.createElementVNode("div",{class:"upload-zone",onDrop:e.withModifiers(O,["prevent"]),onDragover:l[1]||(l[1]=e.withModifiers(()=>{},["prevent"]))},[e.createElementVNode("input",{type:"file",accept:"image/*",onChange:j,ref_key:"fileInput",ref:u,class:"file-input"},null,544),e.createElementVNode("div",{class:"upload-prompt",onClick:l[0]||(l[0]=y=>{var Y;return(Y=u.value)==null?void 0:Y.click()})},[e.renderSlot(d.$slots,"upload-prompt",{},()=>[l[6]||(l[6]=e.createElementVNode("p",null,"拖拽图片到此处,或点击上传",-1))],!0)])],32),m.value?(e.openBlock(),e.createElementBlock("div",te,[e.createElementVNode("div",ne,[l[7]||(l[7]=e.createElementVNode("h4",null,"原始图片",-1)),e.createElementVNode("img",{src:m.value,alt:"Original"},null,8,oe),e.createElementVNode("p",null,e.toDisplayString(h.value),1)]),e.createElementVNode("div",re,[l[8]||(l[8]=e.createElementVNode("h4",null,"压缩后",-1)),w.value?(e.openBlock(),e.createElementBlock("img",{key:0,src:w.value,alt:"Compressed"},null,8,ae)):e.unref(C)?(e.openBlock(),e.createElementBlock("div",le,[e.createElementVNode("progress",{value:e.unref(R),max:"100"},null,8,ie),e.createElementVNode("p",null,"压缩中... "+e.toDisplayString(e.unref(R))+"%",1)])):e.createCommentVNode("",!0),D.value?(e.openBlock(),e.createElementBlock("p",se,e.toDisplayString(D.value),1)):e.createCommentVNode("",!0)])])):e.createCommentVNode("",!0),m.value?(e.openBlock(),e.createElementBlock("div",ce,[l[9]||(l[9]=e.createElementVNode("label",null,"编码器:",-1)),e.withDirectives(e.createElementVNode("select",{"onUpdate:modelValue":l[2]||(l[2]=y=>e.isRef(a)?a.value=y:null),onChange:P},[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(e.unref(c),y=>(e.openBlock(),e.createElementBlock("option",{key:y.type,value:y.type},e.toDisplayString(y.label)+" ("+e.toDisplayString(y.extension)+") ",9,de))),128))],544),[[e.vModelSelect,e.unref(a)]]),I.value?(e.openBlock(),e.createElementBlock("div",me,[e.createElementVNode("label",null,[e.createTextVNode(" 质量: "+e.toDisplayString(e.unref(i).quality??75)+" ",1),e.createElementVNode("input",{type:"range",min:"0",max:"100",value:e.unref(i).quality??75,onInput:l[3]||(l[3]=y=>e.unref(b)("quality",Number(y.target.value))),"-------":"",REPLACE:""},null,40,ue)])])):e.createCommentVNode("",!0),e.renderSlot(d.$slots,"encoder-settings",{encoder:e.unref(a),options:e.unref(i)},void 0,!0)])):e.createCommentVNode("",!0),m.value?(e.openBlock(),e.createElementBlock("div",fe,[e.createElementVNode("button",{onClick:U,disabled:e.unref(C),class:"btn-primary"},e.toDisplayString(e.unref(C)?"压缩中...":"开始压缩"),9,pe),e.unref(f)?(e.openBlock(),e.createElementBlock("button",{key:0,onClick:l[4]||(l[4]=(...y)=>e.unref(T)&&e.unref(T)(...y)),class:"btn-secondary"}," 取消 ")):e.createCommentVNode("",!0),e.unref(k)?(e.openBlock(),e.createElementBlock("button",{key:1,onClick:l[5]||(l[5]=(...y)=>e.unref(_)&&e.unref(_)(...y)),class:"btn-success"}," 下载 (节省 "+e.toDisplayString(e.unref(k).savingsPercent)+"%) ",1)):e.createCommentVNode("",!0)])):e.createCommentVNode("",!0),e.unref(N)?(e.openBlock(),e.createElementBlock("div",ge,e.toDisplayString(e.unref(N)),1)):e.createCommentVNode("",!0)]))}}),[["__scopeId","data-v-86f2b482"]]),Ee=[{value:"lanczos3",label:"Lanczos3"},{value:"catrom",label:"Catrom"},{value:"mitchell",label:"Mitchell"},{value:"triangle",label:"Triangle"},{value:"vector",label:"Vector"}],we=[{value:"stretch",label:"拉伸"},{value:"contain",label:"适应"}],he=[{value:0,label:"不旋转"},{value:90,label:"顺时针 90°"},{value:180,label:"旋转 180°"},{value:270,label:"逆时针 90°"}];s.DEFAULT_ENCODER_OPTIONS=S,s.ENCODER_LIST=G,s.ENCODER_REGISTRY=B,s.FIT_METHODS=we,s.ImageCompressor=be,s.RESIZE_METHODS=Ee,s.ROTATE_OPTIONS=he,s.arrayBufferToFile=A,s.blobToImageData=M,s.calculateSavings=W,s.canDecodeImageType=F,s.createBlobUrl=q,s.formatBytes=V,s.generateCompressedFilename=J,s.getImageDimensions=Z,s.imageDataToBlob=L,s.revokeBlobUrl=$,s.sniffMimeType=x,s.useCompression=Q,s.useEncoderRegistry=K,s.useWorker=H,Object.defineProperty(s,Symbol.toStringTag,{value:"Module"})});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Emscripten WASM 模块初始化工具
|
|
3
|
+
* 对标 Squoosh: src/features/worker-utils/index.ts
|
|
4
|
+
*/
|
|
5
|
+
export declare function initEmscriptenModule<T extends EmscriptenWasm.Module>(moduleFactory: EmscriptenWasm.ModuleFactory<T>): Promise<T>;
|
|
6
|
+
export declare function blobToArrayBuffer(blob: Blob): Promise<ArrayBuffer>;
|