Skip to content

Commit

Permalink
Merge pull request #28 from unovue/feat/animation-control
Browse files Browse the repository at this point in the history
Feat/animation
  • Loading branch information
rick-hup authored Jan 9, 2025
2 parents 683dd84 + e5a04dc commit 656bf0b
Show file tree
Hide file tree
Showing 11 changed files with 56 additions and 77 deletions.
10 changes: 10 additions & 0 deletions docs/nuxt.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
app: {
head: {
meta: [
{
name: 'viewport',
content: 'width=device-width, initial-scale=1, shrink-to-fit=no',
},
],
},
},
nitro: {
preset: 'cloudflare-pages',
},
Expand Down
7 changes: 1 addition & 6 deletions packages/motion/src/components/LayoutGroup.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
<script setup lang="ts">
import { onBeforeUpdate } from 'vue'
import { type LayoutGroupProps, useLayoutGroupProvider } from './use-layout-group'
const props = defineProps<LayoutGroupProps>()
const { forceRender, key, group } = useLayoutGroupProvider(props)
onBeforeUpdate(() => {
group.dirty()
})
const { forceRender, key } = useLayoutGroupProvider(props)
</script>

<template>
Expand Down
2 changes: 1 addition & 1 deletion packages/motion/src/components/Motion.vue
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ function getProps() {
v-bind="getProps()"
:as="as"
:as-child="asChild"
:data-motion-id="state.id"
:data-motion-group="layoutGroup.key?.value || undefined"
>
<slot />
</Primitive>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { mountedStates } from '@/state'
import { doneCallbacks, provideAnimatePresence, removeDoneCallback } from '@/components/presence'
import type { AnimatePresenceProps } from './types'
import { usePopLayout } from './use-pop-layout'
import { createStyles } from '@/state/style'
// 定义组件Props接口
// 定义组件选项
Expand Down Expand Up @@ -33,20 +32,15 @@ onMounted(() => {
// 处理元素进入动画
function enter(el: HTMLElement) {
const state = mountedStates.get(el)
const motionStateId = el.dataset.motionId
const motionState = mountedStates.get(motionStateId)
if (motionState) {
const baseStyle = createStyles(motionState.baseTarget)
Object.assign(el.style, baseStyle)
}
if (!state) {
return
}
state.isVShow = true
removeDoneCallback(el)
state.setActive('exit', false)
}
const { addPopStyle, removePopStyle } = usePopLayout(props)
const { addPopStyle, removePopStyle, styles } = usePopLayout(props)
const exitDom = new Map<Element, boolean>()
Expand All @@ -66,17 +60,20 @@ function exit(el: Element, done: VoidFunction) {
if (e?.detail?.isExit) {
const projection = state.visualElement.projection
// @ts-ignore
if ((projection?.animationProgress > 0 && !state.isSafeToRemove)) {
if ((projection?.animationProgress > 0 && !state.isSafeToRemove && !state.isVShow)) {
return
}
state.willUpdate('done')
removePopStyle(state)
removeDoneCallback(el)
exitDom.delete(el)
if (exitDom.size === 0) {
props.onExitComplete?.()
}
if (!styles.has(state)) {
state.willUpdate('done')
}
done()
removePopStyle(state)
if (!el?.isConnected) {
state.unmount(true)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,16 @@ export function usePopLayout(props: AnimatePresenceProps) {

function removePopStyle(state: MotionState) {
const style = styles.get(state)
if (style) {
styles.delete(state)
if (!style)
return
styles.delete(state)
requestIdleCallback(() => {
document.head.removeChild(style)
}
})
}
return {
addPopStyle,
removePopStyle,
styles,
}
}
27 changes: 15 additions & 12 deletions packages/motion/src/state/motion-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,27 @@ import { invariant } from 'hey-listen'
import { visualElementStore } from 'framer-motion/dist/es/render/store.mjs'
import { isDef } from '@vueuse/core'
import type { AnimationPlaybackControls, DOMKeyframesDefinition, DynamicAnimationOptions, VisualElement } from 'framer-motion'
import { animate, noop } from 'framer-motion/dom'
import { animate, frame, noop } from 'framer-motion/dom'
import { getOptions, hasChanged, resolveVariant } from '@/state/utils'
import { FeatureManager } from '@/features'
import { style } from '@/state/style'
import { transformResetValue } from '@/state/transform'
import { scheduleAnimation, unscheduleAnimation } from '@/state/schedule'
import { motionEvent } from '@/state/event'
import { createVisualElement } from '@/state/create-visual-element'
import { type ActiveVariant, animateVariantsChildren } from '@/state/animate-variants-children'
import { doneCallbacks } from '@/components/presence'

const STATE_TYPES = ['initial', 'animate', 'inView', 'hover', 'press', 'whileDrag', 'exit'] as const
type StateType = typeof STATE_TYPES[number]
export const mountedStates = new Map<Element | string, MotionState>()
export const mountedStates = new WeakMap<Element, MotionState>()
let id = 0
export class MotionState {
public readonly id: string
public element: HTMLElement | null = null
private parent?: MotionState
public options: Options
public isSafeToRemove = false
public isFirstAnimate = true

public isVShow = false
private children?: Set<MotionState> = new Set()
public activeStates: Partial<Record<StateType, boolean>> = {
// initial: true,
Expand All @@ -43,7 +41,6 @@ export class MotionState {

constructor(options: Options, parent?: MotionState) {
this.id = `motion-state-${id++}`
mountedStates.set(this.id, this)
this.options = options
this.parent = parent
parent?.children?.add(this)
Expand Down Expand Up @@ -160,17 +157,23 @@ export class MotionState {

unmount(unMountChildren = false) {
mountedStates.delete(this.element)
mountedStates.delete(this.id)
unscheduleAnimation(this as any)
this.featureManager.unmount()
this.visualElement?.unmount()
if (unMountChildren) {
frame.render(() => {
this.visualElement?.unmount()
})
}
else {
this.visualElement?.unmount()
}
if (unMountChildren) {
const unmountChild = (child: MotionState) => {
child.unmount(true)
child.children?.forEach(unmountChild)
}
this.children?.forEach(unmountChild)
Array.from(this.children).forEach(unmountChild)
}
this.parent?.children?.delete(this)
}

beforeUpdate() {
Expand All @@ -189,7 +192,7 @@ export class MotionState {
this.featureManager.update()

if (hasAnimateChange && !notAnimate) {
scheduleAnimation(this as any)
this.animateUpdates()
}
}

Expand All @@ -201,7 +204,7 @@ export class MotionState {
((child as any).state as MotionState).setActive(name, isActive, false)
})
if (isAnimate) {
scheduleAnimation(this)
this.animateUpdates()
}
}

Expand Down
39 changes: 0 additions & 39 deletions packages/motion/src/state/schedule.ts

This file was deleted.

11 changes: 10 additions & 1 deletion playground/nuxt/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,14 @@ export default defineNuxtConfig({
'@nuxtjs/tailwindcss',
'motion-v/nuxt',
],

app: {
head: {
meta: [
{
name: 'viewport',
content: 'width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cove',
},
],
},
},
})
2 changes: 1 addition & 1 deletion playground/nuxt/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"private": true,
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev --port 3001",
"dev": "nuxt dev --port 3001 --host",
"generate": "nuxt generate",
"preview": "nuxt preview"
},
Expand Down
1 change: 0 additions & 1 deletion playground/nuxt/pages/app-card/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ useEventListener('keydown', (event: KeyboardEvent) => {
v-show="activeCard"
:initial="{ opacity: 0 }"
:animate="{ opacity: 1 }"
layout
:exit="{ opacity: 0 }"
class=" overlay pointer-events-none"
/>
Expand Down
6 changes: 4 additions & 2 deletions playground/nuxt/pages/share-layout.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
<script setup lang="ts">
import { useEventListener } from '@vueuse/core'
import { LayoutGroup, Motion, MotionConfig } from 'motion-v'
import Child from './child.vue'
import CHild2 from './child2.vue'
const show = ref(false)
useEventListener('keydown', (event: KeyboardEvent) => {
Expand All @@ -21,6 +19,8 @@ useEventListener('keydown', (event: KeyboardEvent) => {
v-if="!show"
layout-id="test"
class="w-[100px] h-[100px] bg-white rounded-md"
:transition="{ duration: 3 }"
:crossfade="false"
@click="() => {
show = !show
forceRender()
Expand All @@ -31,7 +31,9 @@ useEventListener('keydown', (event: KeyboardEvent) => {
<Motion
v-if="show"
layout-id="test"
:transition="{ duration: 3 }"
class="w-[200px] h-[200px] fixed top-0 left-0 bg-white rounded-md"
:crossfade="false"
@click="show = !show"
/>
</AnimatePresence>
Expand Down

0 comments on commit 656bf0b

Please sign in to comment.