@libshub/gif-tools 1.0.2 → 1.0.7
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/dist/components/GifLoadStatsDebug.d.ts +7 -0
- package/dist/components/GifPlayer.d.ts +5 -4
- package/dist/gif-tools.css +1 -1
- package/dist/gif-tools.es.js +341 -88
- package/dist/index.d.ts +3 -3
- package/dist/utils/gifController.d.ts +5 -2
- package/dist/utils/gifResourceManager.d.ts +3 -3
- package/dist/utils/index.d.ts +4 -1
- package/dist/utils/loadStats.d.ts +18 -0
- package/dist/utils/types.d.ts +16 -3
- package/package.json +1 -1
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
import type { GifLoadStats } from '../utils';
|
|
3
|
+
export declare function GifLoadStatsDebug({ stats, canvasRef, visible, }: {
|
|
4
|
+
stats: GifLoadStats;
|
|
5
|
+
canvasRef: RefObject<HTMLCanvasElement | null>;
|
|
6
|
+
visible: boolean;
|
|
7
|
+
}): import("react").JSX.Element;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type CSSProperties } from 'react';
|
|
1
|
+
import { type CSSProperties, type ForwardRefExoticComponent, type RefAttributes } from 'react';
|
|
2
|
+
import type { GifLoadStats } from '../utils';
|
|
2
3
|
import './GifPlayer.css';
|
|
3
4
|
export interface GifPlayerRef {
|
|
4
5
|
play: () => void;
|
|
@@ -17,12 +18,12 @@ export interface GifPlayerProps {
|
|
|
17
18
|
autoPlay?: boolean;
|
|
18
19
|
showControls?: boolean;
|
|
19
20
|
debug?: boolean;
|
|
20
|
-
/** 未设置则无限循环;1 播放一次;N 播放 N 次后触发 onEnd */
|
|
21
21
|
loopCount?: number;
|
|
22
22
|
onPlay?: () => void;
|
|
23
23
|
onPause?: () => void;
|
|
24
24
|
onEnd?: () => void;
|
|
25
|
-
onLoaded?: (
|
|
25
|
+
onLoaded?: (stats: GifLoadStats) => void;
|
|
26
26
|
onError?: (error: Error) => void;
|
|
27
27
|
}
|
|
28
|
-
export
|
|
28
|
+
export type GifPlayerComponent = ForwardRefExoticComponent<GifPlayerProps & RefAttributes<GifPlayerRef>>;
|
|
29
|
+
export declare const GifPlayer: GifPlayerComponent;
|
package/dist/gif-tools.css
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
.gif-player{line-height:0;display:inline-block;position:relative;overflow:
|
|
1
|
+
.gif-player{line-height:0;display:inline-block;position:relative;overflow:visible}.gif-player__media{max-width:100%;height:auto;display:block}.gif-player__controls{opacity:0;gap:6px;transition:opacity .2s;display:flex;position:absolute;bottom:8px;right:8px}.gif-player:hover .gif-player__controls,.gif-player--show-controls .gif-player__controls{opacity:1}.gif-player__btn{color:#fff;cursor:pointer;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);background:#0000008c;border:none;border-radius:50%;justify-content:center;align-items:center;width:32px;height:32px;padding:0;display:flex}.gif-player__btn:hover{background:#000000bf}.gif-player__btn svg{fill:currentColor;width:16px;height:16px}.gif-player__debug{z-index:1;pointer-events:none;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);font-variant-numeric:tabular-nums;color:#e8eaed;background:#0c0e12d1;font-family:ui-monospace,Cascadia Code,SF Mono,monospace;position:absolute;top:0;left:0;box-shadow:0 2px 8px #0003}.gif-player__debug--compact{white-space:nowrap;border-radius:0 0 4px;padding:2px 5px;font-size:8px;line-height:1.2}.gif-player__debug--medium{border-radius:0 0 5px;padding:3px 6px;font-size:9px;line-height:1.2}.gif-player__debug--collapsible{pointer-events:auto;cursor:pointer;-webkit-user-select:none;user-select:none}.gif-player__debug--collapsible:hover{background:#12161ceb}.gif-player__debug--collapsible:focus-visible{outline-offset:1px;outline:1px solid #7ec8ffbf}.gif-player__debug--full{border-radius:0 0 6px;min-width:96px;padding:5px 7px;font-size:10px;line-height:1.35}.gif-player__debug--expanded{z-index:2;min-width:108px;max-width:none!important}.gif-player__debug-head{justify-content:space-between;align-items:center;gap:8px;display:flex}.gif-player__debug-badge{letter-spacing:.04em;color:#fff;background:#ffffff24;border-radius:3px;padding:1px 4px;font-size:8px;font-weight:600;display:inline-block}.gif-player__debug--full .gif-player__debug-badge{margin-bottom:4px;padding:1px 5px;font-size:9px}.gif-player__debug[data-mode=fresh] .gif-player__debug-badge{background:#4a90e28c}.gif-player__debug[data-mode=pending] .gif-player__debug-badge{background:#e6a23c8c}.gif-player__debug[data-mode=cache] .gif-player__debug-badge{background:#52c4808c}.gif-player__debug-body{flex-direction:column;gap:2px;display:flex}.gif-player__debug-row,.gif-player__debug-total{justify-content:space-between;align-items:baseline;gap:8px;display:flex}.gif-player__debug-label{color:#e8eaedb8;white-space:nowrap}.gif-player__debug-value{color:#fff;white-space:nowrap}.gif-player__debug-value--total{color:#7ec8ff;font-weight:600}.gif-player__debug[data-mode=cache] .gif-player__debug-value--total{color:#82e0aa}.gif-player__debug-total{border-top:1px solid #ffffff1f;margin-top:4px;padding-top:4px}.gif-player__debug-total .gif-player__debug-label{color:#e8eaede6;font-weight:600}.gif-player__debug-total .gif-player__debug-value{color:#7ec8ff;font-weight:600}.gif-player__debug[data-mode=cache] .gif-player__debug-total .gif-player__debug-value{color:#82e0aa}
|
|
2
2
|
/*$vite$:1*/
|
package/dist/gif-tools.es.js
CHANGED
|
@@ -299,64 +299,122 @@ var c = (e, t) => () => (t || (e((t = { exports: {} }).exports, t), e = null), t
|
|
|
299
299
|
});
|
|
300
300
|
};
|
|
301
301
|
})))(), h = /* @__PURE__ */ new Map(), g = /* @__PURE__ */ new Map(), _ = /* @__PURE__ */ new Map();
|
|
302
|
-
|
|
303
|
-
|
|
302
|
+
function v(e, t, n) {
|
|
303
|
+
return n === null || e >= n ? {
|
|
304
|
+
pendingWaitFetchMs: 0,
|
|
305
|
+
pendingWaitDecodeMs: t - e
|
|
306
|
+
} : {
|
|
307
|
+
pendingWaitFetchMs: n - e,
|
|
308
|
+
pendingWaitDecodeMs: t - n
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
function y() {
|
|
312
|
+
return {
|
|
313
|
+
fetchTimeMs: 0,
|
|
314
|
+
decodeTimeMs: 0,
|
|
315
|
+
pendingWaitFetchMs: 0,
|
|
316
|
+
pendingWaitDecodeMs: 0,
|
|
317
|
+
fromCache: !0,
|
|
318
|
+
fromPending: !1
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
function b(e, t) {
|
|
322
|
+
return {
|
|
323
|
+
fetchTimeMs: e,
|
|
324
|
+
decodeTimeMs: t,
|
|
325
|
+
pendingWaitFetchMs: 0,
|
|
326
|
+
pendingWaitDecodeMs: 0,
|
|
327
|
+
fromCache: !1,
|
|
328
|
+
fromPending: !1
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
async function x(e, t) {
|
|
332
|
+
let n = performance.now(), r = await fetch(e, {
|
|
304
333
|
mode: "cors",
|
|
305
334
|
credentials: "omit"
|
|
306
335
|
});
|
|
307
|
-
if (!
|
|
308
|
-
let
|
|
309
|
-
|
|
336
|
+
if (!r.ok) throw Error(`Failed to load gif: ${r.status}`);
|
|
337
|
+
let i = await r.arrayBuffer(), a = performance.now() - n;
|
|
338
|
+
t && (t.fetchDoneAt = performance.now());
|
|
339
|
+
let o = performance.now(), s = (0, m.parseGIF)(i), c = (0, m.decompressFrames)(s, !0), l = performance.now() - o;
|
|
340
|
+
if (!c.length) throw Error("GIF has no frames");
|
|
310
341
|
return {
|
|
311
342
|
gif: {
|
|
312
|
-
frames:
|
|
313
|
-
width:
|
|
314
|
-
height:
|
|
343
|
+
frames: c,
|
|
344
|
+
width: s.lsd.width,
|
|
345
|
+
height: s.lsd.height
|
|
315
346
|
},
|
|
316
|
-
|
|
347
|
+
fetchTimeMs: a,
|
|
348
|
+
decodeTimeMs: l
|
|
317
349
|
};
|
|
318
350
|
}
|
|
319
|
-
async function
|
|
351
|
+
async function S(e, t) {
|
|
320
352
|
let n = h.get(e);
|
|
321
|
-
if (n) return
|
|
353
|
+
if (n) return {
|
|
322
354
|
gif: n.data,
|
|
323
|
-
|
|
355
|
+
stats: y()
|
|
324
356
|
};
|
|
325
357
|
if (!t?.skipPending && g.has(e)) {
|
|
326
|
-
let { gif:
|
|
327
|
-
return h.
|
|
328
|
-
|
|
329
|
-
|
|
358
|
+
let t = g.get(e), n = performance.now(), { gif: r, fetchTimeMs: i, decodeTimeMs: a } = await t.promise, { pendingWaitFetchMs: o, pendingWaitDecodeMs: s } = v(n, performance.now(), t.fetchDoneAt), c = h.get(e);
|
|
359
|
+
return c ||= (h.set(e, {
|
|
360
|
+
data: r,
|
|
361
|
+
refCount: 0,
|
|
362
|
+
fetchTimeMs: i,
|
|
363
|
+
decodeTimeMs: a
|
|
364
|
+
}), h.get(e)), {
|
|
365
|
+
gif: c.data,
|
|
366
|
+
stats: {
|
|
367
|
+
fetchTimeMs: 0,
|
|
368
|
+
decodeTimeMs: 0,
|
|
369
|
+
pendingWaitFetchMs: o,
|
|
370
|
+
pendingWaitDecodeMs: s,
|
|
371
|
+
fromCache: !1,
|
|
372
|
+
fromPending: !0
|
|
373
|
+
}
|
|
330
374
|
};
|
|
331
375
|
}
|
|
332
376
|
let r = (_.get(e) ?? 0) + 1;
|
|
333
377
|
_.set(e, r);
|
|
334
|
-
let i =
|
|
378
|
+
let i = {
|
|
379
|
+
fetchDoneAt: null,
|
|
380
|
+
promise: void 0
|
|
381
|
+
};
|
|
382
|
+
i.promise = x(e, i).then(({ gif: t, fetchTimeMs: n, decodeTimeMs: i }) => _.get(e) === r ? (g.delete(e), h.set(e, {
|
|
335
383
|
data: t,
|
|
336
|
-
refCount: 0
|
|
384
|
+
refCount: 0,
|
|
385
|
+
fetchTimeMs: n,
|
|
386
|
+
decodeTimeMs: i
|
|
337
387
|
}), {
|
|
338
388
|
gif: t,
|
|
339
|
-
|
|
389
|
+
fetchTimeMs: n,
|
|
390
|
+
decodeTimeMs: i
|
|
340
391
|
}) : {
|
|
341
392
|
gif: t,
|
|
342
|
-
|
|
393
|
+
fetchTimeMs: n,
|
|
394
|
+
decodeTimeMs: i
|
|
343
395
|
}).catch((t) => {
|
|
344
396
|
throw _.get(e) === r && g.delete(e), t;
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
return _.get(e) === r && (h.get(e).refCount += 1), {
|
|
397
|
+
}), g.set(e, i);
|
|
398
|
+
let { gif: a, fetchTimeMs: o, decodeTimeMs: s } = await i.promise;
|
|
399
|
+
return {
|
|
349
400
|
gif: a,
|
|
350
|
-
|
|
401
|
+
stats: b(o, s)
|
|
351
402
|
};
|
|
352
403
|
}
|
|
353
|
-
function
|
|
404
|
+
function C(e) {
|
|
405
|
+
let t = h.get(e);
|
|
406
|
+
t && (t.refCount += 1);
|
|
407
|
+
}
|
|
408
|
+
function w(e) {
|
|
354
409
|
let t = h.get(e);
|
|
355
410
|
t && (--t.refCount, t.refCount <= 0 && h.delete(e));
|
|
356
411
|
}
|
|
412
|
+
function T() {
|
|
413
|
+
h.clear(), g.clear(), _.clear();
|
|
414
|
+
}
|
|
357
415
|
//#endregion
|
|
358
416
|
//#region src/utils/gifController.ts
|
|
359
|
-
function
|
|
417
|
+
function E(e, t, n, r, i = {}) {
|
|
360
418
|
let a = e.getContext("2d");
|
|
361
419
|
if (!a) throw Error("Canvas 2d context unavailable");
|
|
362
420
|
let o = a;
|
|
@@ -400,8 +458,8 @@ function x(e, t, n, r, i = {}) {
|
|
|
400
458
|
function x() {
|
|
401
459
|
b(), u = 0, d = 0, o.clearRect(0, 0, n, r), g(0);
|
|
402
460
|
}
|
|
403
|
-
function S() {
|
|
404
|
-
b(), p = null, s.width = 0, s.height = 0, e.width = 0, e.height = 0;
|
|
461
|
+
function S(t) {
|
|
462
|
+
b(), p = null, s.width = 0, s.height = 0, t?.clearCanvas !== !1 && (e.width = 0, e.height = 0);
|
|
405
463
|
}
|
|
406
464
|
return g(0), {
|
|
407
465
|
play: y,
|
|
@@ -412,58 +470,252 @@ function x(e, t, n, r, i = {}) {
|
|
|
412
470
|
getCompletedLoops: () => d
|
|
413
471
|
};
|
|
414
472
|
}
|
|
415
|
-
async function
|
|
416
|
-
let { skipPending: r, onLoaded: i, ...a } = n, { gif: o,
|
|
417
|
-
i?.(s)
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
473
|
+
async function D(e, t, n = {}) {
|
|
474
|
+
let { skipPending: r, onLoaded: i, ...a } = n, { gif: o, stats: s } = await S(t, { skipPending: r });
|
|
475
|
+
return i?.(s), {
|
|
476
|
+
controller: E(e, o.frames, o.width, o.height, a),
|
|
477
|
+
stats: s
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
//#endregion
|
|
481
|
+
//#region src/utils/loadStats.ts
|
|
482
|
+
function O(e) {
|
|
483
|
+
return e.fromCache ? 0 : e.fromPending ? e.pendingWaitFetchMs + e.pendingWaitDecodeMs : e.fetchTimeMs + e.decodeTimeMs;
|
|
484
|
+
}
|
|
485
|
+
function k(e) {
|
|
486
|
+
return e.fromCache ? {
|
|
487
|
+
mode: "cache",
|
|
488
|
+
lines: [{
|
|
489
|
+
label: "wait fetch",
|
|
490
|
+
valueMs: 0
|
|
491
|
+
}, {
|
|
492
|
+
label: "wait decode",
|
|
493
|
+
valueMs: 0
|
|
494
|
+
}],
|
|
495
|
+
totalMs: 0
|
|
496
|
+
} : e.fromPending ? {
|
|
497
|
+
mode: "pending",
|
|
498
|
+
lines: [{
|
|
499
|
+
label: "wait fetch",
|
|
500
|
+
valueMs: e.pendingWaitFetchMs
|
|
501
|
+
}, {
|
|
502
|
+
label: "wait decode",
|
|
503
|
+
valueMs: e.pendingWaitDecodeMs
|
|
504
|
+
}],
|
|
505
|
+
totalMs: e.pendingWaitFetchMs + e.pendingWaitDecodeMs
|
|
506
|
+
} : {
|
|
507
|
+
mode: "fresh",
|
|
508
|
+
lines: [{
|
|
509
|
+
label: "fetch",
|
|
510
|
+
valueMs: e.fetchTimeMs
|
|
511
|
+
}, {
|
|
512
|
+
label: "decode",
|
|
513
|
+
valueMs: e.decodeTimeMs
|
|
514
|
+
}],
|
|
515
|
+
totalMs: e.fetchTimeMs + e.decodeTimeMs
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
function A(e) {
|
|
519
|
+
let t = k(e), n = t.lines.map((e) => `${e.label} ${e.valueMs.toFixed(1)}ms`).join("\n");
|
|
520
|
+
return `${t.mode}\n${n}\ntotal ${t.totalMs.toFixed(1)}ms`;
|
|
521
|
+
}
|
|
522
|
+
function j(e) {
|
|
523
|
+
return `${e.toFixed(1)}ms`;
|
|
524
|
+
}
|
|
525
|
+
function M(e, t) {
|
|
526
|
+
let n = Math.min(e, t);
|
|
527
|
+
return n < 130 ? "compact" : n < 220 ? "medium" : "full";
|
|
528
|
+
}
|
|
529
|
+
var N = {
|
|
530
|
+
fresh: "F",
|
|
531
|
+
pending: "P",
|
|
532
|
+
cache: "C"
|
|
533
|
+
};
|
|
534
|
+
function P(e) {
|
|
535
|
+
let t = k(e), n = N[t.mode];
|
|
536
|
+
return t.mode === "cache" ? `${n} · 0` : `${n} · ${j(t.totalMs)}`;
|
|
537
|
+
}
|
|
538
|
+
function F(e, t) {
|
|
539
|
+
return t === "fresh" ? e.label === "fetch" ? "f" : "d" : e.label === "wait fetch" ? "wf" : "wd";
|
|
540
|
+
}
|
|
541
|
+
//#endregion
|
|
542
|
+
//#region src/components/GifLoadStatsDebug.tsx
|
|
543
|
+
var I = {
|
|
544
|
+
fresh: "FRESH",
|
|
545
|
+
pending: "PND",
|
|
546
|
+
cache: "CACHE"
|
|
547
|
+
};
|
|
548
|
+
function L(e, t) {
|
|
549
|
+
let [r, i] = a({
|
|
550
|
+
width: 0,
|
|
551
|
+
height: 0
|
|
552
|
+
});
|
|
553
|
+
return n(() => {
|
|
554
|
+
let n = e.current;
|
|
555
|
+
if (!n || !t) return;
|
|
556
|
+
let r = () => {
|
|
557
|
+
i({
|
|
558
|
+
width: n.clientWidth,
|
|
559
|
+
height: n.clientHeight
|
|
560
|
+
});
|
|
561
|
+
};
|
|
562
|
+
r();
|
|
563
|
+
let a = new ResizeObserver(r);
|
|
564
|
+
return a.observe(n), () => a.disconnect();
|
|
565
|
+
}, [e, t]), r;
|
|
566
|
+
}
|
|
567
|
+
function R({ label: e, valueMs: t }) {
|
|
568
|
+
return /* @__PURE__ */ s("div", {
|
|
569
|
+
className: "gif-player__debug-row",
|
|
570
|
+
children: [/* @__PURE__ */ o("span", {
|
|
571
|
+
className: "gif-player__debug-label",
|
|
572
|
+
children: e
|
|
573
|
+
}), /* @__PURE__ */ o("span", {
|
|
574
|
+
className: "gif-player__debug-value",
|
|
575
|
+
children: j(t)
|
|
576
|
+
})]
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
function z({ view: e, width: t, expanded: n, onToggle: r }) {
|
|
580
|
+
return /* @__PURE__ */ s("div", {
|
|
581
|
+
className: [
|
|
582
|
+
"gif-player__debug",
|
|
583
|
+
"gif-player__debug--full",
|
|
584
|
+
n && "gif-player__debug--expanded",
|
|
585
|
+
r && "gif-player__debug--collapsible"
|
|
586
|
+
].filter(Boolean).join(" "),
|
|
587
|
+
"data-mode": e.mode,
|
|
588
|
+
style: { maxWidth: !n && t > 0 ? Math.min(152, Math.round(t * .52)) : void 0 },
|
|
589
|
+
role: r ? "button" : void 0,
|
|
590
|
+
tabIndex: r ? 0 : void 0,
|
|
591
|
+
title: r ? n ? "点击收起" : "点击展开详情" : void 0,
|
|
592
|
+
onClick: r,
|
|
593
|
+
onKeyDown: r ? (e) => {
|
|
594
|
+
(e.key === "Enter" || e.key === " ") && (e.preventDefault(), r());
|
|
595
|
+
} : void 0,
|
|
596
|
+
children: [
|
|
597
|
+
/* @__PURE__ */ o("span", {
|
|
598
|
+
className: "gif-player__debug-badge",
|
|
599
|
+
children: I[e.mode]
|
|
600
|
+
}),
|
|
601
|
+
/* @__PURE__ */ o("div", {
|
|
602
|
+
className: "gif-player__debug-body",
|
|
603
|
+
children: e.lines.map((t) => /* @__PURE__ */ o(R, {
|
|
604
|
+
label: F(t, e.mode),
|
|
605
|
+
valueMs: t.valueMs
|
|
606
|
+
}, t.label))
|
|
607
|
+
}),
|
|
608
|
+
/* @__PURE__ */ s("div", {
|
|
609
|
+
className: "gif-player__debug-total",
|
|
610
|
+
children: [/* @__PURE__ */ o("span", {
|
|
611
|
+
className: "gif-player__debug-label",
|
|
612
|
+
children: "Σ"
|
|
613
|
+
}), /* @__PURE__ */ o("span", {
|
|
614
|
+
className: "gif-player__debug-value",
|
|
615
|
+
children: j(e.totalMs)
|
|
616
|
+
})]
|
|
617
|
+
})
|
|
618
|
+
]
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
function B({ stats: e, canvasRef: t, visible: r }) {
|
|
622
|
+
let { width: i, height: c } = L(t, r), l = k(e), u = M(i, c), [d, f] = a(!1), p = u !== "full";
|
|
623
|
+
n(() => {
|
|
624
|
+
f(!1);
|
|
625
|
+
}, [e]), n(() => {
|
|
626
|
+
p || f(!1);
|
|
627
|
+
}, [p]);
|
|
628
|
+
let m = () => {
|
|
629
|
+
p && f((e) => !e);
|
|
630
|
+
};
|
|
631
|
+
return p && d ? /* @__PURE__ */ o(z, {
|
|
632
|
+
view: l,
|
|
633
|
+
width: i,
|
|
634
|
+
expanded: !0,
|
|
635
|
+
onToggle: m
|
|
636
|
+
}) : u === "compact" ? /* @__PURE__ */ o("div", {
|
|
637
|
+
className: "gif-player__debug gif-player__debug--compact gif-player__debug--collapsible",
|
|
638
|
+
"data-mode": l.mode,
|
|
639
|
+
role: "button",
|
|
640
|
+
tabIndex: 0,
|
|
641
|
+
title: "点击展开详情",
|
|
642
|
+
onClick: m,
|
|
643
|
+
onKeyDown: (e) => {
|
|
644
|
+
(e.key === "Enter" || e.key === " ") && (e.preventDefault(), m());
|
|
645
|
+
},
|
|
646
|
+
children: P(e)
|
|
647
|
+
}) : u === "medium" ? /* @__PURE__ */ o("div", {
|
|
648
|
+
className: "gif-player__debug gif-player__debug--medium gif-player__debug--collapsible",
|
|
649
|
+
"data-mode": l.mode,
|
|
650
|
+
role: "button",
|
|
651
|
+
tabIndex: 0,
|
|
652
|
+
title: "点击展开详情",
|
|
653
|
+
onClick: m,
|
|
654
|
+
onKeyDown: (e) => {
|
|
655
|
+
(e.key === "Enter" || e.key === " ") && (e.preventDefault(), m());
|
|
656
|
+
},
|
|
657
|
+
children: /* @__PURE__ */ s("div", {
|
|
658
|
+
className: "gif-player__debug-head",
|
|
659
|
+
children: [/* @__PURE__ */ o("span", {
|
|
660
|
+
className: "gif-player__debug-badge",
|
|
661
|
+
children: I[l.mode]
|
|
662
|
+
}), /* @__PURE__ */ o("span", {
|
|
663
|
+
className: "gif-player__debug-value gif-player__debug-value--total",
|
|
664
|
+
children: j(l.totalMs)
|
|
665
|
+
})]
|
|
666
|
+
})
|
|
667
|
+
}) : /* @__PURE__ */ o(z, {
|
|
668
|
+
view: l,
|
|
669
|
+
width: i,
|
|
670
|
+
expanded: !1
|
|
671
|
+
});
|
|
422
672
|
}
|
|
423
673
|
//#endregion
|
|
424
674
|
//#region src/components/GifPlayer.tsx
|
|
425
|
-
var
|
|
426
|
-
let
|
|
427
|
-
|
|
428
|
-
let
|
|
429
|
-
|
|
430
|
-
}, []), B = t(() => {
|
|
431
|
-
w.current?.pause(), j(!1);
|
|
432
|
-
}, []), V = t(() => {
|
|
433
|
-
w.current?.isPlaying() ? B() : z();
|
|
434
|
-
}, [B, z]), H = t(() => {
|
|
435
|
-
w.current?.reset(), j(!1);
|
|
436
|
-
}, []), U = t(() => {
|
|
437
|
-
R.current = !0, L((e) => e + 1);
|
|
675
|
+
var V = e(({ src: e, autoPlay: c = !0, showControls: l = !1, debug: u = !1, loopCount: d, className: f, style: p, width: m, height: h, onPlay: g, onPause: _, onEnd: v, onLoaded: y, onError: b }, x) => {
|
|
676
|
+
let S = i(null), T = i(null), E = i(g), O = i(_), k = i(v), A = i(y), j = i(b), [M, N] = a(c), [P, F] = a(!1), [I, L] = a(null), [R, z] = a(0), V = i(!1), H = i(0);
|
|
677
|
+
E.current = g, O.current = _, k.current = v, A.current = y, j.current = b;
|
|
678
|
+
let U = t(() => {
|
|
679
|
+
T.current?.play(), N(!0);
|
|
438
680
|
}, []), W = t(() => {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
681
|
+
T.current?.pause(), N(!1);
|
|
682
|
+
}, []), G = t(() => {
|
|
683
|
+
T.current?.isPlaying() ? W() : U();
|
|
684
|
+
}, [W, U]), K = t(() => {
|
|
685
|
+
T.current?.reset(), N(!1);
|
|
686
|
+
}, []), q = t(() => {
|
|
687
|
+
V.current = !0, z((e) => e + 1);
|
|
688
|
+
}, []), J = t(() => {
|
|
689
|
+
if (!e) return () => {};
|
|
690
|
+
let t = ++H.current, n = !1, r = S.current;
|
|
691
|
+
if (!r) return () => {};
|
|
692
|
+
let i = V.current;
|
|
693
|
+
return V.current = !1, F(!1), N(!1), L(null), T.current?.destroy(), T.current = null, D(r, e, {
|
|
694
|
+
skipPending: i,
|
|
444
695
|
loopCount: d,
|
|
445
696
|
onPlay: () => {
|
|
446
|
-
|
|
697
|
+
t === H.current && N(!0), E.current?.();
|
|
447
698
|
},
|
|
448
699
|
onPause: () => {
|
|
449
|
-
|
|
700
|
+
t === H.current && N(!1), O.current?.();
|
|
450
701
|
},
|
|
451
702
|
onEnd: () => {
|
|
452
|
-
|
|
453
|
-
},
|
|
454
|
-
onLoaded: (e) => {
|
|
455
|
-
F(e), O.current?.(e);
|
|
703
|
+
t === H.current && N(!1), k.current?.();
|
|
456
704
|
}
|
|
457
|
-
}).then((
|
|
458
|
-
if (t) {
|
|
459
|
-
|
|
705
|
+
}).then(({ controller: r, stats: i }) => {
|
|
706
|
+
if (A.current?.(i), n || t !== H.current) {
|
|
707
|
+
r.destroy({ clearCanvas: !1 });
|
|
460
708
|
return;
|
|
461
709
|
}
|
|
462
|
-
|
|
710
|
+
C(e), T.current = r;
|
|
711
|
+
let a = r.destroy.bind(r);
|
|
712
|
+
r.destroy = (t) => {
|
|
713
|
+
a(t), w(e);
|
|
714
|
+
}, F(!0), L(i), c && (r.play(), N(!0));
|
|
463
715
|
}).catch((e) => {
|
|
464
|
-
t
|
|
716
|
+
t === H.current && L(null), j.current?.(e instanceof Error ? e : Error(String(e)));
|
|
465
717
|
}), () => {
|
|
466
|
-
|
|
718
|
+
n = !0, t === H.current && (T.current?.destroy(), T.current = null);
|
|
467
719
|
};
|
|
468
720
|
}, [
|
|
469
721
|
e,
|
|
@@ -471,19 +723,19 @@ var C = e(({ src: e, autoPlay: c = !0, showControls: l = !1, debug: u = !1, loop
|
|
|
471
723
|
c
|
|
472
724
|
]);
|
|
473
725
|
return r(x, () => ({
|
|
474
|
-
play:
|
|
475
|
-
pause:
|
|
476
|
-
toggle:
|
|
477
|
-
reset:
|
|
478
|
-
reload:
|
|
479
|
-
isPlaying: () =>
|
|
726
|
+
play: U,
|
|
727
|
+
pause: W,
|
|
728
|
+
toggle: G,
|
|
729
|
+
reset: K,
|
|
730
|
+
reload: q,
|
|
731
|
+
isPlaying: () => T.current?.isPlaying() ?? !1
|
|
480
732
|
}), [
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
]), n(() =>
|
|
733
|
+
U,
|
|
734
|
+
W,
|
|
735
|
+
G,
|
|
736
|
+
K,
|
|
737
|
+
q
|
|
738
|
+
]), n(() => J(), [J, R]), /* @__PURE__ */ s("div", {
|
|
487
739
|
className: [
|
|
488
740
|
"gif-player",
|
|
489
741
|
l && "gif-player--show-controls",
|
|
@@ -492,26 +744,27 @@ var C = e(({ src: e, autoPlay: c = !0, showControls: l = !1, debug: u = !1, loop
|
|
|
492
744
|
style: p,
|
|
493
745
|
children: [
|
|
494
746
|
/* @__PURE__ */ o("canvas", {
|
|
495
|
-
ref:
|
|
747
|
+
ref: S,
|
|
496
748
|
className: "gif-player__media",
|
|
497
749
|
role: "img",
|
|
498
750
|
style: {
|
|
499
751
|
...m === void 0 ? {} : { width: m },
|
|
500
752
|
...h === void 0 ? {} : { height: h },
|
|
501
|
-
...
|
|
753
|
+
...P ? {} : { visibility: "hidden" }
|
|
502
754
|
}
|
|
503
755
|
}),
|
|
504
|
-
u &&
|
|
505
|
-
|
|
506
|
-
|
|
756
|
+
u && I !== null && P && /* @__PURE__ */ o(B, {
|
|
757
|
+
stats: I,
|
|
758
|
+
canvasRef: S,
|
|
759
|
+
visible: P
|
|
507
760
|
}),
|
|
508
|
-
l &&
|
|
761
|
+
l && P && /* @__PURE__ */ o("div", {
|
|
509
762
|
className: "gif-player__controls",
|
|
510
763
|
children: /* @__PURE__ */ o("button", {
|
|
511
764
|
type: "button",
|
|
512
765
|
className: "gif-player__btn",
|
|
513
|
-
onClick:
|
|
514
|
-
children:
|
|
766
|
+
onClick: G,
|
|
767
|
+
children: M ? /* @__PURE__ */ s("svg", {
|
|
515
768
|
viewBox: "0 0 24 24",
|
|
516
769
|
"aria-hidden": "true",
|
|
517
770
|
children: [/* @__PURE__ */ o("rect", {
|
|
@@ -537,6 +790,6 @@ var C = e(({ src: e, autoPlay: c = !0, showControls: l = !1, debug: u = !1, loop
|
|
|
537
790
|
]
|
|
538
791
|
});
|
|
539
792
|
});
|
|
540
|
-
|
|
793
|
+
V.displayName = "GifPlayer";
|
|
541
794
|
//#endregion
|
|
542
|
-
export {
|
|
795
|
+
export { V as GifPlayer, T as clearGifResourceCache, D as createGifController, A as formatGifLoadStats, j as formatLoadTimeMs, k as getGifLoadStatsView, O as getTotalLoadTimeMs };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { GifPlayer } from './components/GifPlayer';
|
|
2
|
-
export type { GifPlayerProps, GifPlayerRef } from './components/GifPlayer';
|
|
3
|
-
export { createGifController } from './utils';
|
|
4
|
-
export type { CreateGifOptions, GifController } from './utils';
|
|
2
|
+
export type { GifPlayerComponent, GifPlayerProps, GifPlayerRef, } from './components/GifPlayer';
|
|
3
|
+
export { createGifController, clearGifResourceCache, formatGifLoadStats, formatLoadTimeMs, getGifLoadStatsView, getTotalLoadTimeMs } from './utils';
|
|
4
|
+
export type { CreateGifOptions, GifController, GifLoadStats, GifLoadStatsLine, GifLoadStatsMode, GifLoadStatsView } from './utils';
|
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
import type { CreateGifOptions, GifController } from './types';
|
|
2
|
-
export declare function createGifController(canvas: HTMLCanvasElement, src: string, options?: CreateGifOptions): Promise<
|
|
1
|
+
import type { CreateGifOptions, GifController, GifLoadStats } from './types';
|
|
2
|
+
export declare function createGifController(canvas: HTMLCanvasElement, src: string, options?: CreateGifOptions): Promise<{
|
|
3
|
+
controller: GifController;
|
|
4
|
+
stats: GifLoadStats;
|
|
5
|
+
}>;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import type { LoadedGif } from './types';
|
|
1
|
+
import type { LoadedGif, GifLoadStats } from './types';
|
|
2
2
|
interface LoadGifResourceOptions {
|
|
3
|
-
/** reload 等场景:不复用进行中的请求,直接发起新 fetch */
|
|
4
3
|
skipPending?: boolean;
|
|
5
4
|
}
|
|
6
5
|
interface LoadGifResult {
|
|
7
6
|
gif: LoadedGif;
|
|
8
|
-
|
|
7
|
+
stats: GifLoadStats;
|
|
9
8
|
}
|
|
10
9
|
export declare function loadGifResource(src: string, options?: LoadGifResourceOptions): Promise<LoadGifResult>;
|
|
10
|
+
export declare function acquireGifResource(src: string): void;
|
|
11
11
|
export declare function releaseGifResource(src: string): void;
|
|
12
12
|
export declare function clearGifResourceCache(): void;
|
|
13
13
|
export {};
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
1
|
export { createGifController } from './gifController';
|
|
2
|
-
export
|
|
2
|
+
export { acquireGifResource, clearGifResourceCache, releaseGifResource } from './gifResourceManager';
|
|
3
|
+
export { formatGifLoadStats, formatGifLoadStatsCompact, formatLoadTimeMs, getDebugDensity, getGifLoadStatsView, getTotalLoadTimeMs } from './loadStats';
|
|
4
|
+
export type { GifLoadStatsLine, GifLoadStatsMode, GifLoadStatsView } from './loadStats';
|
|
5
|
+
export type { CreateGifOptions, GifController, GifLoadStats } from './types';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { GifLoadStats } from './types';
|
|
2
|
+
export declare function getTotalLoadTimeMs(stats: GifLoadStats): number;
|
|
3
|
+
export type GifLoadStatsMode = 'fresh' | 'pending' | 'cache';
|
|
4
|
+
export interface GifLoadStatsLine {
|
|
5
|
+
label: string;
|
|
6
|
+
valueMs: number;
|
|
7
|
+
}
|
|
8
|
+
export interface GifLoadStatsView {
|
|
9
|
+
mode: GifLoadStatsMode;
|
|
10
|
+
lines: GifLoadStatsLine[];
|
|
11
|
+
totalMs: number;
|
|
12
|
+
}
|
|
13
|
+
export declare function getGifLoadStatsView(stats: GifLoadStats): GifLoadStatsView;
|
|
14
|
+
export declare function formatGifLoadStats(stats: GifLoadStats): string;
|
|
15
|
+
export declare function formatLoadTimeMs(ms: number): string;
|
|
16
|
+
export declare function getDebugDensity(width: number, height: number): 'compact' | 'medium' | 'full';
|
|
17
|
+
export declare function formatGifLoadStatsCompact(stats: GifLoadStats): string;
|
|
18
|
+
export declare function getGifLoadStatsLineLabel(line: GifLoadStatsLine, mode: GifLoadStatsMode): string;
|
package/dist/utils/types.d.ts
CHANGED
|
@@ -4,20 +4,33 @@ export interface LoadedGif {
|
|
|
4
4
|
width: number;
|
|
5
5
|
height: number;
|
|
6
6
|
}
|
|
7
|
+
export interface GifLoadStats {
|
|
8
|
+
/** 本次实际 fetch 耗时,仅 fresh 发起方有值 */
|
|
9
|
+
fetchTimeMs: number;
|
|
10
|
+
/** 本次实际 decode 耗时,仅 fresh 发起方有值 */
|
|
11
|
+
decodeTimeMs: number;
|
|
12
|
+
/** 等待进行中的 fetch 阶段,仅 pending */
|
|
13
|
+
pendingWaitFetchMs: number;
|
|
14
|
+
/** 等待进行中的 decode 阶段,仅 pending */
|
|
15
|
+
pendingWaitDecodeMs: number;
|
|
16
|
+
fromCache: boolean;
|
|
17
|
+
fromPending: boolean;
|
|
18
|
+
}
|
|
7
19
|
export interface CreateGifOptions {
|
|
8
20
|
loopCount?: number;
|
|
9
21
|
onEnd?: () => void;
|
|
10
22
|
onPlay?: () => void;
|
|
11
23
|
onPause?: () => void;
|
|
12
|
-
onLoaded?: (
|
|
13
|
-
/** reload 等场景:不复用进行中的请求,直接发起新 fetch */
|
|
24
|
+
onLoaded?: (stats: GifLoadStats) => void;
|
|
14
25
|
skipPending?: boolean;
|
|
15
26
|
}
|
|
16
27
|
export interface GifController {
|
|
17
28
|
play: () => void;
|
|
18
29
|
pause: () => void;
|
|
19
30
|
reset: () => void;
|
|
20
|
-
destroy: (
|
|
31
|
+
destroy: (options?: {
|
|
32
|
+
clearCanvas?: boolean;
|
|
33
|
+
}) => void;
|
|
21
34
|
isPlaying: () => boolean;
|
|
22
35
|
getCompletedLoops: () => number;
|
|
23
36
|
}
|