-
-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
f2057c9
commit f68082f
Showing
2 changed files
with
91 additions
and
131 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,111 +1,114 @@ | ||
import type { Ref } from 'vue' | ||
import type { WatchSource } from 'vue' | ||
import { watch } from 'vue' | ||
|
||
export type TimingFunction = (x: number) => number | ||
export const linear: TimingFunction = x => x | ||
|
||
export interface Context<T extends object> { | ||
target: T | ||
} | ||
|
||
export type Animation<A extends object, T extends object = object> = | ||
(context: Context<T> & A, progress: number) => void | ||
|
||
export function defineAnimation< | ||
A extends object, | ||
T extends object, | ||
>(animation: Animation<A, T>): Animation<A, T> { | ||
return animation | ||
} | ||
export type Animation<T extends object, A extends object = object> = | ||
(target: T, context: A, progress: number) => void | ||
|
||
export function exec<T extends object>(callback: (widget: T) => void) { | ||
return defineAnimation((context, progress) => { | ||
if (progress === 0) | ||
callback(context.target as T) | ||
}) | ||
} | ||
|
||
export const delay = defineAnimation((_context, _progress) => { | ||
// do nothing | ||
}) | ||
|
||
interface AnimationInstance<A extends object, T extends object> { | ||
context: Context<T> & A | ||
animation: Animation<A, T> | ||
interface AnimationInstance<T extends object, A extends object> { | ||
context: A | ||
animation: Animation<T, A> | ||
startAt?: number | ||
duration: number | ||
duration: number | 'once' | ||
by: TimingFunction | ||
} | ||
|
||
export class AnimationManager<T extends object> { | ||
animations: AnimationInstance<any, T>[] = [] | ||
index: number = 0 | ||
animations: AnimationInstance<T, any>[] = [] | ||
|
||
constructor(public target: T, elapsed: Ref<number>) { | ||
constructor(target: T, elapsed: WatchSource<number>) { | ||
watch(elapsed, <A extends object>(elapsed: number) => { | ||
if (this.animations.length === 0) | ||
if (this.animations.length === 0) { | ||
return | ||
const animation: AnimationInstance<A, T> = this.animations[0] | ||
if (typeof animation.startAt === 'undefined') { | ||
animation.startAt = elapsed | ||
animation.animation(animation.context, 0) | ||
} | ||
const { | ||
context, | ||
animation, | ||
duration, | ||
startAt, | ||
by, | ||
}: AnimationInstance<T, A> = this.animations[0] | ||
if (startAt === undefined) { | ||
this.animations[0].startAt = elapsed | ||
animation(target, context, 0) | ||
if (duration === 'once') { | ||
this.animations.shift() | ||
} | ||
} | ||
else { | ||
const progress = (elapsed - animation.startAt) / (animation.duration ?? (elapsed - animation.startAt)) | ||
animation.animation(animation.context, animation.by(Math.min(progress, 1))) | ||
const progress = (elapsed - startAt) / (duration as number) | ||
animation(target, context, by(Math.min(progress, 1))) | ||
if (progress >= 1) { | ||
this.animations.shift() | ||
// this.index++ | ||
} | ||
} | ||
}) | ||
} | ||
|
||
animate<A extends object>(animation: Animation<A, T>, context?: { | ||
duration?: number | ||
animate<A extends object>(animation: Animation<T, A>, context: { | ||
duration?: AnimationInstance<T, A>['duration'] | ||
by?: TimingFunction | ||
} & A) { | ||
context ??= {} as A | ||
const by = context.by ?? linear | ||
const duration = context.duration ?? 1 | ||
Object.assign(context, { target: this.target }) | ||
const by = context.by ?? linear | ||
this.animations.push({ context, animation, duration, by }) | ||
|
||
return this | ||
} | ||
|
||
exec(callback: (widget: T) => void) { | ||
this.animate(exec(callback), { duration: 0 }) | ||
once(animation: (target: T) => void) { | ||
this.animate(animation, { duration: 'once' }) | ||
|
||
return this | ||
} | ||
|
||
delay(duration: number) { | ||
this.animate(delay, { duration }) | ||
this.animate(() => { /* empty animation */ }, { duration }) | ||
|
||
return this | ||
} | ||
} | ||
|
||
export function withDefaults< | ||
A extends object, | ||
export function defineAnimation< | ||
T extends object, | ||
>(animation: Animation<A, T>, defaults: A): Animation<A, T> { | ||
return function (context: Context<T> & A, progress: number) { | ||
if (progress === 0) { | ||
Object.assign(context, defaults) | ||
A extends object = object, | ||
>(animation: Animation<T, A>): Animation<T, A> | ||
|
||
export function defineAnimation< | ||
T extends object, | ||
A extends object = object, | ||
>( | ||
setup: (target: T, context: A) => (progress: number) => void, | ||
dispose?: (target: T, context: A) => void | ||
): Animation<T, A> | ||
|
||
export function defineAnimation< | ||
T extends object, | ||
A extends object = object, | ||
>(...args: [Animation<T, A>] | Parameters<typeof defineAnimation>) { | ||
if (typeof args[0] === 'function') { | ||
return args[0] as Animation<T, A> | ||
} | ||
const [setup, dispose] = args as Parameters<typeof defineAnimation> | ||
let animation: (progress: number) => void | undefined | ||
return function (target: T, context: A, progress: number) { | ||
(animation ??= setup(target, context))(progress) | ||
if (dispose && progress === 1) { | ||
dispose(target, context) | ||
} | ||
animation(context, progress) | ||
} | ||
} | ||
|
||
export function parallel< | ||
A extends object, | ||
T extends object, | ||
>(...animations: Animation<A, T>[]): Animation<A, T> { | ||
return function (context: Context<T> & A, progress: number) { | ||
A extends object = object, | ||
>(...animations: Animation<T, A>[]): Animation<T, A> { | ||
return function (target: T, context: A, progress: number) { | ||
for (const animation of animations) { | ||
animation(context, progress) | ||
animation(target, context, progress) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,84 +1,41 @@ | ||
import type { Reactive } from 'vue' | ||
import { getCurrentInstance, inject, onMounted, provide, reactive, ref, useSlots, watch } from 'vue' | ||
import type { InjectionKey, Reactive, Ref, Slots } from 'vue' | ||
import { getCurrentInstance, inject, onMounted, provide, reactive, ref, useSlots, watchEffect } from 'vue' | ||
|
||
export interface Range { | ||
x: number | ||
y: number | ||
width: number | ||
height: number | ||
} | ||
const childWidgets: InjectionKey<Ref<Widget[]>> = Symbol('child-widgets') | ||
|
||
export interface Widget { | ||
wid?: string | ||
export interface Widget<T extends Widget = any> { | ||
ref?: string | InjectionKey<Reactive<T>> | ||
element?: SVGElement | ||
range?: Range | ||
slots?: string | ||
range?: DOMRect | ||
slots?: Slots | ||
children?: Widget[] | ||
} | ||
|
||
export type ReturnWidget<T extends Widget> = ReturnType<typeof defineWidget<T>> | ||
|
||
export function defineWidget<T extends Widget>(props: Reactive<T>, methods?: Record<string, () => any>, root?: SVGElement): Reactive<T> { | ||
let widget = inject<T>(props.wid as string) | ||
const widgets = inject('child-widgets') as T[] | ||
|
||
// const children = reactive([]) | ||
// provide('child-widgets', children) | ||
|
||
widget ??= {} as T | ||
// Object.assign(props, { | ||
// ...widget, | ||
// ...methods, | ||
// }) | ||
Object.assign(widget, { | ||
...props, | ||
...methods, | ||
}) | ||
|
||
// const propsProxy = reactive({ ...props }) | ||
// watch(props, (v) => { | ||
// for (const key in v) { | ||
// (propsProxy as Record<string, any>)[key] = (v as Record<string, any>)[key] | ||
// } | ||
// }) | ||
|
||
watch(props, (v) => { | ||
for (const key in v) { | ||
(widget as Record<string, any>)[key] = (v as Record<string, any>)[key] | ||
} | ||
}, { | ||
immediate: true, | ||
deep: true, | ||
}) | ||
|
||
onMounted(() => { | ||
widget.element = root ?? getCurrentInstance()!.proxy!.$el.parentElement | ||
widget.range = (root ?? getCurrentInstance()!.proxy!.$el.parentElement)!.getBoundingClientRect() | ||
const slots = useSlots() | ||
widget.slots = slots.default ? slots.default().map(v => v.children).join('') : '' | ||
if (widgets) { | ||
widgets.push(widget) | ||
} | ||
}) | ||
|
||
return widget | ||
export function useWidget<T extends Widget>(ref: string | InjectionKey<T>): T { | ||
const widget = reactive({}) | ||
provide(ref, widget) | ||
return widget as T | ||
} | ||
|
||
export function useWidget<T extends Widget>(wid: string) { | ||
const element = ref<SVGElement>() | ||
const range: Reactive<Range> = reactive({ x: 0, y: 0, width: 0, height: 0 }) | ||
const slots = ref<string>() | ||
const widget = reactive({ | ||
element, | ||
range, | ||
slots, | ||
}) | ||
provide(wid, widget) | ||
return widget as T | ||
export function useChildren(): Ref<Widget[]> { | ||
const children = ref([]) | ||
provide(childWidgets, children) | ||
return children | ||
} | ||
|
||
export function useChildren<T extends Widget>() { | ||
const widgets = reactive([]) as T[] | ||
provide('child-widgets', widgets) | ||
return widgets as Reactive<T[]> | ||
export function defineWidget<T extends Widget>(props: T, root?: SVGElement): T { | ||
if (props.ref) { | ||
const widget = inject<Reactive<T>>(props.ref) | ||
if (widget) { | ||
watchEffect(() => Object.assign(widget, props)) | ||
onMounted(() => { | ||
widget.element = root ?? getCurrentInstance()?.proxy?.$el.parentElement | ||
widget.range = widget.element?.getBoundingClientRect() | ||
widget.slots = useSlots() | ||
inject(childWidgets)?.value.push(widget) | ||
}) | ||
return widget | ||
} | ||
} | ||
return props as Reactive<T> | ||
} |