diff --git a/packages/core/src/animation.ts b/packages/core/src/animation.ts index 70e1963..1a0ad5e 100644 --- a/packages/core/src/animation.ts +++ b/packages/core/src/animation.ts @@ -161,6 +161,7 @@ export function defineAnimation(setup: AnimationSetup) { export function registerAnimation( name: string, setup: (...args: any[]) => (animate: AnimationManager) => void, + relativeElapsed?: WatchSource, ): void { const current = getCurrentInstance(); const { widget } = current?.props as { @@ -168,8 +169,10 @@ export function registerAnimation( }; if (widget) { if (typeof widget.manager === "undefined") { - const { elapsed } = usePlayer(); - widget.manager = new AnimationManager(widget, elapsed); + widget.manager = new AnimationManager( + widget, + relativeElapsed ?? usePlayer().elapsed, + ); } (widget as Record)[name] = ( ...args: Parameters diff --git a/packages/core/src/player.ts b/packages/core/src/player.ts index bd8a55b..90e2984 100644 --- a/packages/core/src/player.ts +++ b/packages/core/src/player.ts @@ -1,5 +1,5 @@ import type { App, Ref } from "vue"; -import { inject, ref } from "vue"; +import { inject, ref, watch } from "vue"; import { AnimationManager } from "./animation"; const studioListenerAdded = { value: false }; @@ -63,7 +63,63 @@ export function usePlayer() { }, 1000); } - return { play, elapsed, useAnimation, setElapsed, pause, renderOnce }; + function useTimeline(start: number = 0) { + const relativeElapsed = ref(0); + const isPlaying = ref(false); + const localStart = ref(start); + + // 监听全局时间 + watch( + elapsed, + () => { + if (isPlaying.value) { + relativeElapsed.value = Math.max(0, elapsed.value - localStart.value); + } + }, + { immediate: true }, + ); + + function play() { + isPlaying.value = true; + } + + function pause() { + isPlaying.value = false; + } + + function seek(time: number) { + relativeElapsed.value = time; + localStart.value = elapsed.value - time; + } + + function reset() { + seek(0); + } + + function useAnimation(widget: T) { + return new AnimationManager(widget, relativeElapsed); + } + + return { + useAnimation, + elapsed: relativeElapsed, + isPlaying, + play, + pause, + seek, + reset, + }; + } + + return { + play, + elapsed, + useAnimation, + useTimeline, + setElapsed, + pause, + renderOnce, + }; } export type Player = ReturnType; diff --git a/test/src/App.vue b/test/src/App.vue index 70a91ca..667a305 100644 --- a/test/src/App.vue +++ b/test/src/App.vue @@ -9,61 +9,37 @@ import { Line, Arrow, ArrowIns, + grow, + move, + rotate, } from "@vue-motion/lib"; -import { onMounted } from "vue"; -import { NumberPlane } from "@vue-motion/extension-math"; +import { onMounted, watch } from "vue"; const rect = useWidget(); const line = useWidget(); const arrow = useWidget(); -const { play, elapsed } = usePlayer(); +const { play, elapsed, useAnimation, useTimeline } = usePlayer(); +const timeline = useTimeline(2); onMounted(() => { - rect.move(100, 100); - // rect.rotate(180) - // rect.rotateTo(200) - // rect.zoomTo(3, 3) - - rect.moveOnPath( - "M 100 100 Q 100 200 200 200 Q 300 200 300 100 Q 300 0 200 0 Q 100 0 100 100 Z", - { - by: easeInOutCirc, - }, - ); - rect.moveOnFunction( - (progress) => ({ - x: 100 + 200 * progress, - y: 100 + 200 * progress, - }), - { - by: easeInOutCirc, - }, - ); - rect.parallel( - (r) => r.discolorateFillTo("skyblue"), - (r) => r.discolorateBorderTo("blue"), - (r) => r.move(-200, -200), - ); - rect.exec(() => { - console.log("Hello, world!"); + watch(timeline.elapsed, () => { + console.log(timeline.elapsed.value); }); + useAnimation(arrow).animate(rotate, { duration: 1, offset: 200 }); + timeline + .useAnimation(rect) + .animate(move, { duration: 1, offsetX: 100, offsetY: 100 }); - line.grow(); - arrow.grow(); play(); - - document.addEventListener("click", () => { - console.log(elapsed.value); - }); + timeline.play(); });