22 <div ref =" container" :style =" { opacity: 0 }" class =" w-full h-full flex items-center justify-center -z-100" >
33 <slot />
44 </div >
5+ <div v-if =" showProgressBar" class =" fixed bottom-0 left-0 w-full h-2" >
6+ <motion .div
7+ class =" w-full h-2 bg-blue-200 transition-all"
8+ :style =" { width: progressPercent }"
9+ />
10+ </div >
511</template >
612
713<script setup lang="ts">
814import type { AnimationSequence } from ' motion-v' ;
9- import { animate } from ' motion-v' ;
15+ import { animate , motion } from ' motion-v' ;
1016
1117const props = defineProps <{
1218 initSequence: () => AnimationSequence ; // 初始化序列
1319 enterSequence: () => AnimationSequence ; // 进入动画序列
1420 exitSequence: () => AnimationSequence ; // 退出动画序列
1521 infinitySequence? : () => AnimationSequence ; // 无限循环动画序列
1622
17- enterDelay? : number ; // 进入动画延迟 毫秒
18- exitDelay? : number ; // 退出动画延迟 毫秒
23+ enterDelay? : number ; // 进入动画延迟 秒
24+ exitDelay? : number ; // 退出动画延迟 秒
25+
26+ showProgressBar? : boolean ; // 是否显示进度条
27+ enterDuration? : number ; // 进入动画持续时间(秒)
28+ exitDuration? : number ; // 退出动画持续时间(秒)
1929}>();
2030
2131const emit = defineEmits <{
@@ -36,6 +46,11 @@ const aniStatus = reactive<AnimateStatus>({
3646 stage: ' init' ,
3747 status: ' done' ,
3848});
49+ const progressValue = useMotionValue (0 );
50+ // useMotionValueEvent(progressValue, 'change', (latest) => {
51+ // console.log(`Progress: ${latest}`);
52+ // });
53+ const progressPercent = useTransform (progressValue , value => ` ${value * 100 }% ` );
3954
4055async function runSequence(seq : AnimationSequence , options ? : { duration? : number }) {
4156 if (! seq )
@@ -69,9 +84,12 @@ async function runEnterSequence() {
6984 const seq: AnimationSequence = [
7085 [container .value ! , { opacity: [0 , 1 ], y: [' 100vh' , 0 ] }, { at: ' 0' , duration: 0.5 }],
7186 ];
87+
7288 await runSequence (seq );
73- if (props .enterSequence )
89+ if (props .enterSequence ) {
90+ animate (progressValue , 0.5 , { duration: getSequenceDuration (props .enterSequence ()), ease: ' linear' });
7491 await runSequence (props .enterSequence (), {});
92+ }
7593 emit (' enterComplete' );
7694 aniStatus .stage = ' exit' ;
7795 aniStatus .status = ' done' ;
@@ -84,12 +102,15 @@ async function runExitSequence() {
84102 aniStatus .stage = ' exit' ;
85103 aniStatus .status = ' running' ;
86104 emit (' exitStart' );
105+
87106 if (props .exitSequence ) {
107+ animate (progressValue , 1 , { duration: getSequenceDuration (props .exitSequence ()), ease: ' linear' });
88108 await runSequence (props .exitSequence ());
89109 }
90110 const seq: AnimationSequence = [
91111 [container .value ! , { opacity: [1 , 0 ], y: [0 , ' -100vh' ] }, { at: ' +0' , duration: 1 }],
92112 ];
113+
93114 await runSequence (seq , {});
94115 emit (' exitComplete' );
95116 aniStatus .stage = ' exit' ;
@@ -99,15 +120,62 @@ async function runExitSequence() {
99120async function runFullSequence() {
100121 await runInitSequence ();
101122 // console.log('Init Sequence Completed');
102- if (props .enterDelay )
103- await new Promise (resolve => setTimeout (resolve , props .enterDelay ));
123+ if (typeof props .enterDelay )
124+ await new Promise (resolve => setTimeout (resolve , ( props .enterDelay ?? 0 ) * 1000 ));
104125 await runEnterSequence ();
105126 // console.log('Enter Sequence Completed');
106- if (props .exitDelay )
107- await new Promise (resolve => setTimeout (resolve , props .exitDelay ));
127+ if (typeof props .exitDelay )
128+ await new Promise (resolve => setTimeout (resolve , ( props .exitDelay ?? 0 ) * 1000 ));
108129 await runExitSequence ();
109130}
110131
132+ function getSequenceDuration(sequence : AnimationSequence ): number {
133+ let prevStart = 0 ;
134+ let prevEnd = 0 ;
135+ let totalDuration = 0 ;
136+ for (const item of sequence ) {
137+ const [, , options = {}] = item as any ;
138+ const { at, duration = 0.3 , delay = 0 } = options as any ;
139+
140+ // 处理 delay 可能是函数(例如 stagger)
141+ let delayNum = 0 ;
142+ if (typeof delay === ' function' ) {
143+ delayNum = 0.1 ;
144+ } else {
145+ delayNum = delay || 0 ;
146+ }
147+
148+ // 解析 at
149+ let start: number ;
150+ if (at === undefined ) {
151+ start = prevEnd ;
152+ } else if (typeof at === ' number' ) {
153+ start = at ;
154+ } else if (typeof at === ' string' ) {
155+ if (at === ' <' ) {
156+ start = prevStart ;
157+ } else if (at .startsWith (' +' )) {
158+ start = prevEnd + Number .parseFloat (at );
159+ } else if (at .startsWith (' -' )) {
160+ start = prevEnd - Number .parseFloat (at );
161+ } else {
162+ // 标签等忽略
163+ start = prevEnd ;
164+ }
165+ } else {
166+ start = prevEnd ;
167+ }
168+
169+ start += delayNum ;
170+ const end = start + duration ;
171+ totalDuration = Math .max (totalDuration , end );
172+ prevStart = start ;
173+ prevEnd = end ;
174+ }
175+ // console.log(`Calculated sequence duration: ${totalDuration}s`);
176+ return totalDuration ;
177+ }
178+
111179defineExpose ({
112180 runFullSequence ,
113181 runInitSequence ,
0 commit comments