@chao-component/bag-animation-ui 1.0.0 → 1.0.1

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/index.d.mts CHANGED
@@ -1,2 +1,9 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
1
2
 
2
- export { }
3
+ declare function BagAnimation({ isAnimating, setIsAnimating, doneFunction }: {
4
+ isAnimating: boolean;
5
+ setIsAnimating: (isAnimating: boolean) => void;
6
+ doneFunction: () => void;
7
+ }): react_jsx_runtime.JSX.Element;
8
+
9
+ export { BagAnimation };
package/dist/index.d.ts CHANGED
@@ -1,2 +1,9 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
1
2
 
2
- export { }
3
+ declare function BagAnimation({ isAnimating, setIsAnimating, doneFunction }: {
4
+ isAnimating: boolean;
5
+ setIsAnimating: (isAnimating: boolean) => void;
6
+ doneFunction: () => void;
7
+ }): react_jsx_runtime.JSX.Element;
8
+
9
+ export { BagAnimation };
package/dist/index.js CHANGED
@@ -8,6 +8,10 @@ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
8
  var __commonJS = (cb, mod) => function __require() {
9
9
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
10
10
  };
11
+ var __export = (target, all) => {
12
+ for (var name in all)
13
+ __defProp(target, name, { get: all[name], enumerable: true });
14
+ };
11
15
  var __copyProps = (to, from, except, desc) => {
12
16
  if (from && typeof from === "object" || typeof from === "function") {
13
17
  for (let key of __getOwnPropNames(from))
@@ -770,9 +774,325 @@ var require_lib = __commonJS({
770
774
 
771
775
  // src/index.ts
772
776
  var index_exports = {};
777
+ __export(index_exports, {
778
+ BagAnimation: () => BagAnimation
779
+ });
773
780
  module.exports = __toCommonJS(index_exports);
774
781
 
775
782
  // src/BagAnimation.tsx
776
783
  var import_react = require("react");
777
784
  var import_apng_js = __toESM(require_lib());
778
785
  var import_jsx_runtime = require("react/jsx-runtime");
786
+ var frames = [
787
+ "/assets/main/bag/blind_box_spin.png",
788
+ "/assets/main/bag/blind_box_unopen.png",
789
+ "/assets/main/bag/blind_box_tear_off.png",
790
+ "/assets/main/bag/blind_box_open.png"
791
+ ];
792
+ function BagAnimation({ isAnimating, setIsAnimating, doneFunction }) {
793
+ const [frame, setFrame] = (0, import_react.useState)(0);
794
+ const [isPausedAtThird, setIsPausedAtThird] = (0, import_react.useState)(false);
795
+ const [thirdFrameProgress, setThirdFrameProgress] = (0, import_react.useState)(0);
796
+ const [showSwipeHint, setShowSwipeHint] = (0, import_react.useState)(true);
797
+ const canvasRef = (0, import_react.useRef)(null);
798
+ const apngRefs = (0, import_react.useRef)([]);
799
+ const animationRefs = (0, import_react.useRef)([]);
800
+ const thirdFramePlayerRef = (0, import_react.useRef)(null);
801
+ const thirdFrameCtxRef = (0, import_react.useRef)(null);
802
+ const thirdFrameApngRef = (0, import_react.useRef)(null);
803
+ const thirdFrameOriginalSizeRef = (0, import_react.useRef)(null);
804
+ const hasTriggeredFourthFrame = (0, import_react.useRef)(false);
805
+ const mysteryBoxImage = "/assets/main/bag.png";
806
+ const mysteryBoxAlt = "Weedza Mystery Box";
807
+ (0, import_react.useEffect)(() => {
808
+ if (isAnimating) {
809
+ setFrame(0);
810
+ setIsPausedAtThird(false);
811
+ setThirdFrameProgress(0);
812
+ setShowSwipeHint(true);
813
+ hasTriggeredFourthFrame.current = false;
814
+ } else {
815
+ animationRefs.current.forEach((anim) => {
816
+ if (anim) anim.stop();
817
+ });
818
+ }
819
+ }, [isAnimating]);
820
+ (0, import_react.useEffect)(() => {
821
+ if (!isAnimating || !canvasRef.current) return;
822
+ const canvas = canvasRef.current;
823
+ const ctx = canvas.getContext("2d");
824
+ if (!ctx) return;
825
+ if (animationRefs.current[frame]) {
826
+ animationRefs.current[frame].stop();
827
+ }
828
+ const loadAPNG = async () => {
829
+ try {
830
+ const response = await fetch(frames[frame]);
831
+ const arrayBuffer = await response.arrayBuffer();
832
+ const apng = await (0, import_apng_js.default)(arrayBuffer);
833
+ if (apng instanceof Error) {
834
+ console.error("Failed to parse APNG:", apng);
835
+ return;
836
+ }
837
+ canvas.width = apng.width;
838
+ canvas.height = apng.height;
839
+ apngRefs.current[frame] = apng;
840
+ await apng.createImages();
841
+ const player = await apng.getPlayer(ctx);
842
+ let isStopped = false;
843
+ let stopFunction = null;
844
+ let totalDuration = 0;
845
+ for (let i = 0; i < apng.frames.length; i++) {
846
+ const frameDelay = apng.frames[i].delay || 100;
847
+ totalDuration += frameDelay / 1e3 * 1e3;
848
+ }
849
+ player.play(1);
850
+ if (player.stop) {
851
+ stopFunction = player.stop.bind(player);
852
+ }
853
+ animationRefs.current[frame] = {
854
+ stop: () => {
855
+ isStopped = true;
856
+ if (stopFunction) {
857
+ stopFunction();
858
+ }
859
+ }
860
+ };
861
+ if (frame === 2) {
862
+ if (stopFunction) {
863
+ stopFunction();
864
+ }
865
+ thirdFramePlayerRef.current = player;
866
+ thirdFrameCtxRef.current = ctx;
867
+ thirdFrameApngRef.current = apng;
868
+ thirdFrameOriginalSizeRef.current = {
869
+ width: apng.width,
870
+ height: apng.height
871
+ };
872
+ setTimeout(() => {
873
+ setIsPausedAtThird(true);
874
+ setThirdFrameProgress(0);
875
+ }, 0);
876
+ const originalStop = animationRefs.current[frame].stop;
877
+ animationRefs.current[frame].stop = () => {
878
+ isStopped = true;
879
+ if (stopFunction) {
880
+ stopFunction();
881
+ }
882
+ originalStop();
883
+ };
884
+ } else {
885
+ const timeoutId = setTimeout(() => {
886
+ if (!isStopped) {
887
+ if (frame < frames.length - 1) {
888
+ setFrame(frame + 1);
889
+ } else {
890
+ setIsAnimating(false);
891
+ doneFunction();
892
+ setFrame(0);
893
+ }
894
+ }
895
+ }, totalDuration);
896
+ const originalStop = animationRefs.current[frame].stop;
897
+ animationRefs.current[frame].stop = () => {
898
+ isStopped = true;
899
+ clearTimeout(timeoutId);
900
+ if (stopFunction) {
901
+ stopFunction();
902
+ }
903
+ originalStop();
904
+ };
905
+ }
906
+ } catch (error) {
907
+ console.error("Error loading APNG:", error);
908
+ }
909
+ };
910
+ loadAPNG();
911
+ return () => {
912
+ if (animationRefs.current[frame]) {
913
+ animationRefs.current[frame].stop();
914
+ }
915
+ };
916
+ }, [frame, isAnimating, setIsAnimating, doneFunction]);
917
+ (0, import_react.useEffect)(() => {
918
+ if (isPausedAtThird && thirdFrameCtxRef.current && thirdFrameApngRef.current && thirdFramePlayerRef.current && canvasRef.current && thirdFrameOriginalSizeRef.current) {
919
+ const apng = thirdFrameApngRef.current;
920
+ const ctx = thirdFrameCtxRef.current;
921
+ const canvas = canvasRef.current;
922
+ const player = thirdFramePlayerRef.current;
923
+ const originalSize = thirdFrameOriginalSizeRef.current;
924
+ canvas.width = originalSize.width;
925
+ canvas.height = originalSize.height;
926
+ const totalFrames = apng.frames.length;
927
+ const targetFrameIndex = Math.floor(thirdFrameProgress / 100 * totalFrames);
928
+ const clampedFrameIndex = Math.min(Math.max(0, targetFrameIndex), totalFrames - 1);
929
+ if (apng.frames[clampedFrameIndex]) {
930
+ try {
931
+ ctx.save();
932
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
933
+ ctx.clearRect(0, 0, originalSize.width, originalSize.height);
934
+ if (player.render && typeof player.render === "function") {
935
+ if (canvas.width !== originalSize.width || canvas.height !== originalSize.height) {
936
+ canvas.width = originalSize.width;
937
+ canvas.height = originalSize.height;
938
+ }
939
+ player.render(apng.frames[clampedFrameIndex]);
940
+ } else {
941
+ const frame2 = apng.frames[clampedFrameIndex];
942
+ if (frame2.imageElement && frame2.imageElement instanceof HTMLImageElement) {
943
+ const img = frame2.imageElement;
944
+ ctxDrawSwipeFrames(ctx, img, clampedFrameIndex, originalSize);
945
+ } else if (frame2.image && frame2.image instanceof HTMLImageElement) {
946
+ const img = frame2.image;
947
+ ctxDrawSwipeFrames(ctx, img, clampedFrameIndex, originalSize);
948
+ } else {
949
+ if (canvas.width !== originalSize.width || canvas.height !== originalSize.height) {
950
+ canvas.width = originalSize.width;
951
+ canvas.height = originalSize.height;
952
+ }
953
+ player.render(apng.frames[clampedFrameIndex]);
954
+ }
955
+ }
956
+ ctx.restore();
957
+ } catch (error) {
958
+ console.error("Error rendering frame:", error);
959
+ }
960
+ }
961
+ }
962
+ }, [thirdFrameProgress, isPausedAtThird]);
963
+ const ctxDrawSwipeFrames = (ctx, img, clampedFrameIndex, originalSize) => {
964
+ let buttonGapPercentage = 0;
965
+ let startX = originalSize.width * 0.30685;
966
+ let startY = originalSize.height - (originalSize.height * buttonGapPercentage + img.height);
967
+ if (clampedFrameIndex == 0) {
968
+ ctx.drawImage(img, 0, 0, originalSize.width, originalSize.height);
969
+ } else if (clampedFrameIndex == 1 || clampedFrameIndex == 2) {
970
+ ctx.drawImage(img, startX, startY, img.width, img.height);
971
+ } else if (clampedFrameIndex == 3) {
972
+ ctx.drawImage(img, startX, startY, img.width, img.height);
973
+ } else if (clampedFrameIndex == 4) {
974
+ ctx.drawImage(img, startX, startY, img.width, img.height);
975
+ } else if (clampedFrameIndex == 5) {
976
+ ctx.drawImage(img, startX, startY, img.width, img.height);
977
+ }
978
+ };
979
+ const onMouseDown = (e) => {
980
+ if (isPausedAtThird && canvasRef.current) {
981
+ const rect = canvasRef.current.getBoundingClientRect();
982
+ const touchGap = e.clientX - rect.left;
983
+ const initialProgress = Math.max(0, Math.min(100, touchGap / rect.width * 100));
984
+ setThirdFrameProgress(initialProgress);
985
+ setShowSwipeHint(false);
986
+ }
987
+ };
988
+ const onMouseMove = (e) => {
989
+ if (!isPausedAtThird || !canvasRef.current || hasTriggeredFourthFrame.current) return;
990
+ const canvas = canvasRef.current;
991
+ const rect = canvas.getBoundingClientRect();
992
+ const currentX = e.clientX;
993
+ const relativeX = currentX - rect.left;
994
+ const canvasWidth = rect.width;
995
+ const progress = Math.max(0, Math.min(100, relativeX / canvasWidth * 100));
996
+ setThirdFrameProgress(progress);
997
+ if (progress >= 95 && !hasTriggeredFourthFrame.current) {
998
+ hasTriggeredFourthFrame.current = true;
999
+ setIsPausedAtThird(false);
1000
+ setThirdFrameProgress(0);
1001
+ setFrame(3);
1002
+ }
1003
+ };
1004
+ const onMouseUp = (e) => {
1005
+ if (isPausedAtThird) {
1006
+ setThirdFrameProgress(0);
1007
+ setShowSwipeHint(true);
1008
+ }
1009
+ };
1010
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1011
+ isAnimating && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "relative w-full md:w-[85%] mx-auto", children: [
1012
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1013
+ "canvas",
1014
+ {
1015
+ ref: canvasRef,
1016
+ onMouseDown,
1017
+ onMouseMove,
1018
+ onMouseUp,
1019
+ onMouseLeave: onMouseUp,
1020
+ onTouchStart: (e) => {
1021
+ if (isPausedAtThird && canvasRef.current) {
1022
+ const touch = e.touches[0];
1023
+ const rect = canvasRef.current.getBoundingClientRect();
1024
+ const touchGap = touch.clientX - rect.left;
1025
+ const initialProgress = Math.max(0, Math.min(100, touchGap / rect.width * 100));
1026
+ setThirdFrameProgress(initialProgress);
1027
+ setShowSwipeHint(false);
1028
+ }
1029
+ },
1030
+ onTouchMove: (e) => {
1031
+ if (!isPausedAtThird || !canvasRef.current || hasTriggeredFourthFrame.current) return;
1032
+ const touch = e.touches[0];
1033
+ const rect = canvasRef.current.getBoundingClientRect();
1034
+ const relativeX = touch.clientX - rect.left;
1035
+ const canvasWidth = rect.width;
1036
+ const progress = Math.max(0, Math.min(100, relativeX / canvasWidth * 100));
1037
+ setThirdFrameProgress(progress);
1038
+ if (progress >= 95 && !hasTriggeredFourthFrame.current) {
1039
+ hasTriggeredFourthFrame.current = true;
1040
+ setIsPausedAtThird(false);
1041
+ setThirdFrameProgress(0);
1042
+ setFrame(3);
1043
+ }
1044
+ },
1045
+ onTouchEnd: (e) => {
1046
+ if (isPausedAtThird) {
1047
+ e.preventDefault();
1048
+ setThirdFrameProgress(0);
1049
+ setShowSwipeHint(true);
1050
+ }
1051
+ },
1052
+ className: "select-none cursor-pointer w-full h-full object-contain",
1053
+ style: {
1054
+ imageRendering: "pixelated",
1055
+ cursor: isPausedAtThird ? "grab" : "pointer",
1056
+ touchAction: "none",
1057
+ // 防止默認觸摸行為
1058
+ userSelect: "none"
1059
+ // 防止選中
1060
+ }
1061
+ }
1062
+ ),
1063
+ isPausedAtThird && showSwipeHint && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "absolute bottom-8 left-1/2 transform -translate-x-1/2 bg-black/70 text-white px-6 py-3 rounded-full text-sm md:text-base font-medium animate-pulse pointer-events-none z-20", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-2", children: [
1064
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1065
+ "svg",
1066
+ {
1067
+ className: "w-5 h-5 md:w-6 md:h-6",
1068
+ fill: "none",
1069
+ stroke: "currentColor",
1070
+ viewBox: "0 0 24 24",
1071
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1072
+ "path",
1073
+ {
1074
+ strokeLinecap: "round",
1075
+ strokeLinejoin: "round",
1076
+ strokeWidth: 2,
1077
+ d: "M13 7l5 5m0 0l-5 5m5-5H6"
1078
+ }
1079
+ )
1080
+ }
1081
+ ),
1082
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Swipe to open" })
1083
+ ] }) })
1084
+ ] }),
1085
+ !isAnimating && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1086
+ "img",
1087
+ {
1088
+ src: mysteryBoxImage,
1089
+ alt: mysteryBoxAlt,
1090
+ className: "w-36 h-36 md:w-96 md:h-96 object-contain relative z-10"
1091
+ }
1092
+ )
1093
+ ] });
1094
+ }
1095
+ // Annotate the CommonJS export names for ESM import in node:
1096
+ 0 && (module.exports = {
1097
+ BagAnimation
1098
+ });
package/dist/index.mjs CHANGED
@@ -770,3 +770,315 @@ var require_lib = __commonJS({
770
770
  var import_apng_js = __toESM(require_lib());
771
771
  import { useEffect, useRef, useState } from "react";
772
772
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
773
+ var frames = [
774
+ "/assets/main/bag/blind_box_spin.png",
775
+ "/assets/main/bag/blind_box_unopen.png",
776
+ "/assets/main/bag/blind_box_tear_off.png",
777
+ "/assets/main/bag/blind_box_open.png"
778
+ ];
779
+ function BagAnimation({ isAnimating, setIsAnimating, doneFunction }) {
780
+ const [frame, setFrame] = useState(0);
781
+ const [isPausedAtThird, setIsPausedAtThird] = useState(false);
782
+ const [thirdFrameProgress, setThirdFrameProgress] = useState(0);
783
+ const [showSwipeHint, setShowSwipeHint] = useState(true);
784
+ const canvasRef = useRef(null);
785
+ const apngRefs = useRef([]);
786
+ const animationRefs = useRef([]);
787
+ const thirdFramePlayerRef = useRef(null);
788
+ const thirdFrameCtxRef = useRef(null);
789
+ const thirdFrameApngRef = useRef(null);
790
+ const thirdFrameOriginalSizeRef = useRef(null);
791
+ const hasTriggeredFourthFrame = useRef(false);
792
+ const mysteryBoxImage = "/assets/main/bag.png";
793
+ const mysteryBoxAlt = "Weedza Mystery Box";
794
+ useEffect(() => {
795
+ if (isAnimating) {
796
+ setFrame(0);
797
+ setIsPausedAtThird(false);
798
+ setThirdFrameProgress(0);
799
+ setShowSwipeHint(true);
800
+ hasTriggeredFourthFrame.current = false;
801
+ } else {
802
+ animationRefs.current.forEach((anim) => {
803
+ if (anim) anim.stop();
804
+ });
805
+ }
806
+ }, [isAnimating]);
807
+ useEffect(() => {
808
+ if (!isAnimating || !canvasRef.current) return;
809
+ const canvas = canvasRef.current;
810
+ const ctx = canvas.getContext("2d");
811
+ if (!ctx) return;
812
+ if (animationRefs.current[frame]) {
813
+ animationRefs.current[frame].stop();
814
+ }
815
+ const loadAPNG = async () => {
816
+ try {
817
+ const response = await fetch(frames[frame]);
818
+ const arrayBuffer = await response.arrayBuffer();
819
+ const apng = await (0, import_apng_js.default)(arrayBuffer);
820
+ if (apng instanceof Error) {
821
+ console.error("Failed to parse APNG:", apng);
822
+ return;
823
+ }
824
+ canvas.width = apng.width;
825
+ canvas.height = apng.height;
826
+ apngRefs.current[frame] = apng;
827
+ await apng.createImages();
828
+ const player = await apng.getPlayer(ctx);
829
+ let isStopped = false;
830
+ let stopFunction = null;
831
+ let totalDuration = 0;
832
+ for (let i = 0; i < apng.frames.length; i++) {
833
+ const frameDelay = apng.frames[i].delay || 100;
834
+ totalDuration += frameDelay / 1e3 * 1e3;
835
+ }
836
+ player.play(1);
837
+ if (player.stop) {
838
+ stopFunction = player.stop.bind(player);
839
+ }
840
+ animationRefs.current[frame] = {
841
+ stop: () => {
842
+ isStopped = true;
843
+ if (stopFunction) {
844
+ stopFunction();
845
+ }
846
+ }
847
+ };
848
+ if (frame === 2) {
849
+ if (stopFunction) {
850
+ stopFunction();
851
+ }
852
+ thirdFramePlayerRef.current = player;
853
+ thirdFrameCtxRef.current = ctx;
854
+ thirdFrameApngRef.current = apng;
855
+ thirdFrameOriginalSizeRef.current = {
856
+ width: apng.width,
857
+ height: apng.height
858
+ };
859
+ setTimeout(() => {
860
+ setIsPausedAtThird(true);
861
+ setThirdFrameProgress(0);
862
+ }, 0);
863
+ const originalStop = animationRefs.current[frame].stop;
864
+ animationRefs.current[frame].stop = () => {
865
+ isStopped = true;
866
+ if (stopFunction) {
867
+ stopFunction();
868
+ }
869
+ originalStop();
870
+ };
871
+ } else {
872
+ const timeoutId = setTimeout(() => {
873
+ if (!isStopped) {
874
+ if (frame < frames.length - 1) {
875
+ setFrame(frame + 1);
876
+ } else {
877
+ setIsAnimating(false);
878
+ doneFunction();
879
+ setFrame(0);
880
+ }
881
+ }
882
+ }, totalDuration);
883
+ const originalStop = animationRefs.current[frame].stop;
884
+ animationRefs.current[frame].stop = () => {
885
+ isStopped = true;
886
+ clearTimeout(timeoutId);
887
+ if (stopFunction) {
888
+ stopFunction();
889
+ }
890
+ originalStop();
891
+ };
892
+ }
893
+ } catch (error) {
894
+ console.error("Error loading APNG:", error);
895
+ }
896
+ };
897
+ loadAPNG();
898
+ return () => {
899
+ if (animationRefs.current[frame]) {
900
+ animationRefs.current[frame].stop();
901
+ }
902
+ };
903
+ }, [frame, isAnimating, setIsAnimating, doneFunction]);
904
+ useEffect(() => {
905
+ if (isPausedAtThird && thirdFrameCtxRef.current && thirdFrameApngRef.current && thirdFramePlayerRef.current && canvasRef.current && thirdFrameOriginalSizeRef.current) {
906
+ const apng = thirdFrameApngRef.current;
907
+ const ctx = thirdFrameCtxRef.current;
908
+ const canvas = canvasRef.current;
909
+ const player = thirdFramePlayerRef.current;
910
+ const originalSize = thirdFrameOriginalSizeRef.current;
911
+ canvas.width = originalSize.width;
912
+ canvas.height = originalSize.height;
913
+ const totalFrames = apng.frames.length;
914
+ const targetFrameIndex = Math.floor(thirdFrameProgress / 100 * totalFrames);
915
+ const clampedFrameIndex = Math.min(Math.max(0, targetFrameIndex), totalFrames - 1);
916
+ if (apng.frames[clampedFrameIndex]) {
917
+ try {
918
+ ctx.save();
919
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
920
+ ctx.clearRect(0, 0, originalSize.width, originalSize.height);
921
+ if (player.render && typeof player.render === "function") {
922
+ if (canvas.width !== originalSize.width || canvas.height !== originalSize.height) {
923
+ canvas.width = originalSize.width;
924
+ canvas.height = originalSize.height;
925
+ }
926
+ player.render(apng.frames[clampedFrameIndex]);
927
+ } else {
928
+ const frame2 = apng.frames[clampedFrameIndex];
929
+ if (frame2.imageElement && frame2.imageElement instanceof HTMLImageElement) {
930
+ const img = frame2.imageElement;
931
+ ctxDrawSwipeFrames(ctx, img, clampedFrameIndex, originalSize);
932
+ } else if (frame2.image && frame2.image instanceof HTMLImageElement) {
933
+ const img = frame2.image;
934
+ ctxDrawSwipeFrames(ctx, img, clampedFrameIndex, originalSize);
935
+ } else {
936
+ if (canvas.width !== originalSize.width || canvas.height !== originalSize.height) {
937
+ canvas.width = originalSize.width;
938
+ canvas.height = originalSize.height;
939
+ }
940
+ player.render(apng.frames[clampedFrameIndex]);
941
+ }
942
+ }
943
+ ctx.restore();
944
+ } catch (error) {
945
+ console.error("Error rendering frame:", error);
946
+ }
947
+ }
948
+ }
949
+ }, [thirdFrameProgress, isPausedAtThird]);
950
+ const ctxDrawSwipeFrames = (ctx, img, clampedFrameIndex, originalSize) => {
951
+ let buttonGapPercentage = 0;
952
+ let startX = originalSize.width * 0.30685;
953
+ let startY = originalSize.height - (originalSize.height * buttonGapPercentage + img.height);
954
+ if (clampedFrameIndex == 0) {
955
+ ctx.drawImage(img, 0, 0, originalSize.width, originalSize.height);
956
+ } else if (clampedFrameIndex == 1 || clampedFrameIndex == 2) {
957
+ ctx.drawImage(img, startX, startY, img.width, img.height);
958
+ } else if (clampedFrameIndex == 3) {
959
+ ctx.drawImage(img, startX, startY, img.width, img.height);
960
+ } else if (clampedFrameIndex == 4) {
961
+ ctx.drawImage(img, startX, startY, img.width, img.height);
962
+ } else if (clampedFrameIndex == 5) {
963
+ ctx.drawImage(img, startX, startY, img.width, img.height);
964
+ }
965
+ };
966
+ const onMouseDown = (e) => {
967
+ if (isPausedAtThird && canvasRef.current) {
968
+ const rect = canvasRef.current.getBoundingClientRect();
969
+ const touchGap = e.clientX - rect.left;
970
+ const initialProgress = Math.max(0, Math.min(100, touchGap / rect.width * 100));
971
+ setThirdFrameProgress(initialProgress);
972
+ setShowSwipeHint(false);
973
+ }
974
+ };
975
+ const onMouseMove = (e) => {
976
+ if (!isPausedAtThird || !canvasRef.current || hasTriggeredFourthFrame.current) return;
977
+ const canvas = canvasRef.current;
978
+ const rect = canvas.getBoundingClientRect();
979
+ const currentX = e.clientX;
980
+ const relativeX = currentX - rect.left;
981
+ const canvasWidth = rect.width;
982
+ const progress = Math.max(0, Math.min(100, relativeX / canvasWidth * 100));
983
+ setThirdFrameProgress(progress);
984
+ if (progress >= 95 && !hasTriggeredFourthFrame.current) {
985
+ hasTriggeredFourthFrame.current = true;
986
+ setIsPausedAtThird(false);
987
+ setThirdFrameProgress(0);
988
+ setFrame(3);
989
+ }
990
+ };
991
+ const onMouseUp = (e) => {
992
+ if (isPausedAtThird) {
993
+ setThirdFrameProgress(0);
994
+ setShowSwipeHint(true);
995
+ }
996
+ };
997
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
998
+ isAnimating && /* @__PURE__ */ jsxs("div", { className: "relative w-full md:w-[85%] mx-auto", children: [
999
+ /* @__PURE__ */ jsx(
1000
+ "canvas",
1001
+ {
1002
+ ref: canvasRef,
1003
+ onMouseDown,
1004
+ onMouseMove,
1005
+ onMouseUp,
1006
+ onMouseLeave: onMouseUp,
1007
+ onTouchStart: (e) => {
1008
+ if (isPausedAtThird && canvasRef.current) {
1009
+ const touch = e.touches[0];
1010
+ const rect = canvasRef.current.getBoundingClientRect();
1011
+ const touchGap = touch.clientX - rect.left;
1012
+ const initialProgress = Math.max(0, Math.min(100, touchGap / rect.width * 100));
1013
+ setThirdFrameProgress(initialProgress);
1014
+ setShowSwipeHint(false);
1015
+ }
1016
+ },
1017
+ onTouchMove: (e) => {
1018
+ if (!isPausedAtThird || !canvasRef.current || hasTriggeredFourthFrame.current) return;
1019
+ const touch = e.touches[0];
1020
+ const rect = canvasRef.current.getBoundingClientRect();
1021
+ const relativeX = touch.clientX - rect.left;
1022
+ const canvasWidth = rect.width;
1023
+ const progress = Math.max(0, Math.min(100, relativeX / canvasWidth * 100));
1024
+ setThirdFrameProgress(progress);
1025
+ if (progress >= 95 && !hasTriggeredFourthFrame.current) {
1026
+ hasTriggeredFourthFrame.current = true;
1027
+ setIsPausedAtThird(false);
1028
+ setThirdFrameProgress(0);
1029
+ setFrame(3);
1030
+ }
1031
+ },
1032
+ onTouchEnd: (e) => {
1033
+ if (isPausedAtThird) {
1034
+ e.preventDefault();
1035
+ setThirdFrameProgress(0);
1036
+ setShowSwipeHint(true);
1037
+ }
1038
+ },
1039
+ className: "select-none cursor-pointer w-full h-full object-contain",
1040
+ style: {
1041
+ imageRendering: "pixelated",
1042
+ cursor: isPausedAtThird ? "grab" : "pointer",
1043
+ touchAction: "none",
1044
+ // 防止默認觸摸行為
1045
+ userSelect: "none"
1046
+ // 防止選中
1047
+ }
1048
+ }
1049
+ ),
1050
+ isPausedAtThird && showSwipeHint && /* @__PURE__ */ jsx("div", { className: "absolute bottom-8 left-1/2 transform -translate-x-1/2 bg-black/70 text-white px-6 py-3 rounded-full text-sm md:text-base font-medium animate-pulse pointer-events-none z-20", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1051
+ /* @__PURE__ */ jsx(
1052
+ "svg",
1053
+ {
1054
+ className: "w-5 h-5 md:w-6 md:h-6",
1055
+ fill: "none",
1056
+ stroke: "currentColor",
1057
+ viewBox: "0 0 24 24",
1058
+ children: /* @__PURE__ */ jsx(
1059
+ "path",
1060
+ {
1061
+ strokeLinecap: "round",
1062
+ strokeLinejoin: "round",
1063
+ strokeWidth: 2,
1064
+ d: "M13 7l5 5m0 0l-5 5m5-5H6"
1065
+ }
1066
+ )
1067
+ }
1068
+ ),
1069
+ /* @__PURE__ */ jsx("span", { children: "Swipe to open" })
1070
+ ] }) })
1071
+ ] }),
1072
+ !isAnimating && /* @__PURE__ */ jsx(
1073
+ "img",
1074
+ {
1075
+ src: mysteryBoxImage,
1076
+ alt: mysteryBoxAlt,
1077
+ className: "w-36 h-36 md:w-96 md:h-96 object-contain relative z-10"
1078
+ }
1079
+ )
1080
+ ] });
1081
+ }
1082
+ export {
1083
+ BagAnimation
1084
+ };
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@chao-component/bag-animation-ui",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.mjs",
6
6
  "types": "dist/index.d.ts",
7
7
  "exports": {
8
8
  ".": {
9
+ "types": "./dist/index.d.ts",
9
10
  "import": "./dist/index.mjs",
10
- "require": "./dist/index.js",
11
- "types": "./dist/index.d.ts"
11
+ "require": "./dist/index.js"
12
12
  }
13
13
  },
14
14
  "files": ["dist"],