返回 slidev
v-motion.ts
根目录 / packages / client / modules / v-motion.ts
1 import type { ClicksInfo } from '@slidev/types'
2 import type { App, ObjectDirective } from 'vue'
3 import { MotionDirective } from '@vueuse/motion'
4 import { watch } from 'vue'
5 import { useNav } from '../composables/useNav'
6 import { injectionClicksContext, injectionCurrentPage, injectionRenderContext } from '../constants'
7 import { makeId } from '../logic/utils'
8 import { directiveInject } from '../utils'
9 import { resolvedClickMap } from './v-click'
10
11 export function createVMotionDirectives() {
12 return {
13 install(app: App) {
14 const original = MotionDirective() as ObjectDirective
15 app.directive<HTMLElement | SVGElement, string>('motion', {
16 // @ts-expect-error extra prop
17 name: 'v-motion',
18 mounted(el, binding, node, prevNode) {
19 const clicksContext = directiveInject(binding, injectionClicksContext)
20 const thisPage = directiveInject(binding, injectionCurrentPage)
21 const renderContext = directiveInject(binding, injectionRenderContext)
22 const { currentPage, clicks: currentClicks, isPrintMode } = useNav()
23
24 const props = node.props = { ...node.props }
25
26 const variantInitial = { ...props.initial, ...props.variants?.['slidev-initial'] }
27 const variantEnter = { ...props.enter, ...props.variants?.['slidev-enter'] }
28 const variantLeave = { ...props.leave, ...props.variants?.['slidev-leave'] }
29 delete props.initial
30 delete props.enter
31 delete props.leave
32
33 const idPrefix = `${makeId()}-`
34 const clicks: {
35 id: string
36 at: number | [number, number]
37 variant: Record<string, unknown>
38 info: ClicksInfo | null | undefined
39 }[] = []
40
41 for (const k of Object.keys(props)) {
42 if (k.startsWith('click-')) {
43 const s = k.slice(6)
44 const at = s.includes('-') ? s.split('-').map(Number) as [number, number] : +s
45 const id = idPrefix + s
46 clicks.push({
47 id,
48 at,
49 variant: { ...props[k] },
50 info: clicksContext?.value.calculate(at),
51 })
52 delete props[k]
53 }
54 }
55
56 clicks.sort((a, b) => (Array.isArray(a.at) ? a.at[0] : a.at) - (Array.isArray(b.at) ? b.at[0] : b.at))
57
58 original.created!(el, binding, node, prevNode)
59 original.mounted!(el, binding, node, prevNode)
60
61 // @ts-expect-error extra prop
62 const motion = el.motionInstance
63 motion.clickIds = clicks.map(i => i.id)
64 motion.set(variantInitial)
65 motion.watchStopHandle = watch(
66 [thisPage, currentPage, currentClicks].filter(Boolean),
67 () => {
68 const visibility = resolvedClickMap.get(el)?.visibilityState.value ?? 'shown'
69 if (!clicksContext?.value || !['slide', 'presenter'].includes(renderContext?.value ?? '')) {
70 const mixedVariant: Record<string, unknown> = { ...variantInitial, ...variantEnter }
71 for (const { variant } of clicks)
72 Object.assign(mixedVariant, variant)
73
74 motion.set(mixedVariant)
75 }
76 else if (isPrintMode.value || thisPage?.value === currentPage.value) {
77 if (visibility === 'shown') {
78 const mixedVariant: Record<string, unknown> = { ...variantInitial, ...variantEnter }
79 for (const { variant, info } of clicks) {
80 if (!info || info.isActive.value)
81 Object.assign(mixedVariant, variant)
82 }
83 if (isPrintMode.value)
84 motion.set(mixedVariant) // print with clicks
85 else
86 motion.apply(mixedVariant)
87 }
88 else {
89 motion.apply(visibility === 'before' ? variantInitial : variantLeave)
90 }
91 }
92 else {
93 motion.apply((thisPage?.value ?? -1) > currentPage.value ? variantInitial : variantLeave)
94 }
95 },
96 {
97 immediate: true,
98 },
99 )
100 },
101 unmounted(el) {
102 // @ts-expect-error extra prop
103 const motion = el.motionInstance
104 motion.watchStopHandle()
105 },
106 })
107 },
108 }
109 }
110
110 lines TYPESCRIPT