From 0d3aee12f87e19629981b2412ae15f0e2f82a1ac Mon Sep 17 00:00:00 2001 From: Acbox liu <850625057@qq.com> Date: Thu, 28 Nov 2024 23:06:27 +0800 Subject: [PATCH] feat: new grammar suggar of parallel --- packages/core/src/animation.ts | 58 +++++++++++++++++++++------ packages/core/src/index.ts | 1 - packages/core/src/symbols.ts | 1 - packages/lib/src/widgets/widget.ts | 63 +++++++++++++++++++++++++++--- test/src/App.vue | 9 +++-- 5 files changed, 110 insertions(+), 22 deletions(-) delete mode 100644 packages/core/src/symbols.ts diff --git a/packages/core/src/animation.ts b/packages/core/src/animation.ts index 543013e..0154cc7 100644 --- a/packages/core/src/animation.ts +++ b/packages/core/src/animation.ts @@ -8,7 +8,7 @@ export const linear: TimingFunction = (x) => x export type Animation = (target: T, context: A, progress: number) => void -interface AnimationInstance { +export interface AnimationInstance { context: A animation: Animation startAt?: number @@ -18,8 +18,10 @@ interface AnimationInstance { export class AnimationManager { animations: AnimationInstance[] = [] + private target: T constructor(target: T, elapsed: WatchSource) { + this.target = target watch(elapsed, (elapsed: number) => { if (this.animations.length === 0) { return @@ -71,6 +73,49 @@ export class AnimationManager { return this } + + parallel( + ...animations: ( + | (() => [Animation, A]) + | ((target: T & { manager: AnimationManager }) => void) + )[] + ) { + type ParallelResult = AnimationInstance | { + animation: Animation + context: A + duration?: number + by?: TimingFunction + } + + // Create a non-reactive temporary array + const tempAnimations: AnimationInstance[] = [] + + // Store the original push method + const originalAnimations = this.animations + this.animations = tempAnimations // Temporarily replace the animations array + + // Collect all animations + animations.forEach(fn => { + fn({ ...this.target, manager: this } as T & { manager: AnimationManager }) + + if (tempAnimations.length === 0) { + throw new Error('No animation was registered in parallel callback') + } + }) + + // Restore original animations array + this.animations = originalAnimations + + // Create the parallel animation + this.animate((target, _, progress) => { + // Execute all animations simultaneously with the same progress + tempAnimations.forEach((result: ParallelResult) => { + result.animation(target, result.context, progress) + }) + }, { duration: 1 }) + + return this + } } export type AnimationSetup = (target: T, context: A) => (progress: number) => void @@ -87,17 +132,6 @@ export function defineAnimation< } } -export function parallel< - T, - A = object, ->(...animations: Animation[]): Animation { - return function (target: T, context: A, progress: number) { - for (const animation of animations) { - animation(target, context, progress) - } - } -} - export function registerAnimation(name: string, setup: (...args: any[]) => (animate: AnimationManager) => void): void { const current = getCurrentInstance() const { widget } = current?.props as { widget: T & { manager?: AnimationManager } } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index d79b0d4..5c83d26 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -2,4 +2,3 @@ export * from './player' export * from './animation' export * from './widget' export * from './motion' -export * from './symbols' diff --git a/packages/core/src/symbols.ts b/packages/core/src/symbols.ts deleted file mode 100644 index c0b5261..0000000 --- a/packages/core/src/symbols.ts +++ /dev/null @@ -1 +0,0 @@ -export const ADDITION_ANIMATIONS = Symbol('ADDITION_ANIMATIONS') diff --git a/packages/lib/src/widgets/widget.ts b/packages/lib/src/widgets/widget.ts index 0879358..a55b40e 100644 --- a/packages/lib/src/widgets/widget.ts +++ b/packages/lib/src/widgets/widget.ts @@ -1,19 +1,40 @@ -import type { AnimationParams, Widget } from '@vue-motion/core' +import type { AnimationManager, AnimationParams, Widget } from '@vue-motion/core' import { registerAnimation } from '@vue-motion/core' import { inject } from 'vue' import { type HasOpacity, type HasOpacityMixin, type HasScale, type HasScaleMixin, type Positional, type PositionalMixin, type Rotatable, type RotatableMixin, type Scalable, type StrokableMixin, moveOnFunction, moveOnPath } from '../animations' import { fadeIn, fadeOut, fadeTo, move, moveTo, rotate, rotateTo, scale, scaleTo, zoomIn, zoomOut, zoomTo } from '../animations' import type { Colorable, colorableMixin } from '../animations/color' import { discolorate } from '../animations/color' +import type { Animation, AnimationInstance, TimingFunction } from '@vue-motion/core' -export type WidgetOptions = Widget & Positional & Scalable & Rotatable & HasOpacity & Colorable -export type WidgetMixin = WidgetOptions & PositionalMixin & RotatableMixin & HasScaleMixin & StrokableMixin & HasOpacityMixin & colorableMixin +export type WidgetOptions = Widget & Positional & Scalable & Rotatable & HasOpacity & Colorable & { + manager?: AnimationManager +} +export type WidgetMixin = WidgetOptions & PositionalMixin & RotatableMixin & HasScaleMixin & StrokableMixin & HasOpacityMixin & colorableMixin & { + parallel: (...animations: ((widget: WidgetMixin & { manager: AnimationManager }) => void)[]) => void + animate: (animation: Animation, context: { + duration?: AnimationInstance['duration'] + by?: TimingFunction + } & A) => void + once: (animation: (target: Widget) => void) => void + delay: (duration: number) => void +} +export interface Animatable { + animate: (animation: Animation, context: { + duration?: number + by?: TimingFunction + } & A) => void + once: (animation: (target: Widget) => void) => void + delay: (duration: number) => void + parallel: (...animations: ((widget: WidgetMixin) => void)[]) => void +} export function widget(options: WidgetOptions) { const props = {} as { transform?: string style?: string - } + } & WidgetMixin + const transform = [] if (options.x || options.y) transform.push(`translate(${options.x ?? 0},${options.y ?? 0})`) @@ -120,8 +141,40 @@ export function widget(options: WidgetOptions) { duration: params?.duration ?? defaultDuration, }) }) - const animations = inject<(() => void)[]>('ADDITION_ANIMATIONS') + registerAnimation('animate', (animation: Animation, context: { + duration?: AnimationInstance['duration'] + by?: TimingFunction + } & A) => { + return (manager) => manager.animate(animation, context) + }) + registerAnimation('once', (animation: (target: Widget) => void) => { + return (manager) => manager.once(animation) + }) + registerAnimation('delay', (duration: number) => { + return (manager) => manager.delay(duration) + }) + registerAnimation('parallel', + (...animations: ((widget: WidgetMixin & { manager: AnimationManager }) => void)[]) => { + return (manager: AnimationManager) => { + return manager.parallel(...animations) + } + } + ) + const animations = inject<(() => Animation)[]>('ADDITION_ANIMATIONS') animations?.forEach((animation) => animation()) return props } + +export interface MoveAnimationContext { + offsetX: number + offsetY: number + duration?: number + by?: TimingFunction +} + +export interface RotateAnimationContext { + offset: number + duration?: number + by?: TimingFunction +} diff --git a/test/src/App.vue b/test/src/App.vue index 9f3505d..5321596 100644 --- a/test/src/App.vue +++ b/test/src/App.vue @@ -1,6 +1,6 @@