Skip to content

Commit

Permalink
refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
sheepbox8646 committed Nov 11, 2024
1 parent f2057c9 commit f68082f
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 131 deletions.
119 changes: 61 additions & 58 deletions packages/core/src/animation.ts
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)
}
}
}
103 changes: 30 additions & 73 deletions packages/core/src/widget.ts
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>
}

0 comments on commit f68082f

Please sign in to comment.