Skip to content

Instantly share code, notes, and snippets.

@productdevbook
Created November 13, 2024 12:52
Show Gist options
  • Save productdevbook/31fc135c0184e578ec9ec785d0c8aab1 to your computer and use it in GitHub Desktop.
Save productdevbook/31fc135c0184e578ec9ec785d0c8aab1 to your computer and use it in GitHub Desktop.
Motion Test
import { animate } from 'motion'
import type { AnimationPlaybackControls, ValueAnimationOptions } from 'motion/react'
import {
type VNode,
computed,
defineComponent,
h,
mergeProps,
reactive,
useAttrs,
useSlots,
} from 'vue'
export default defineComponent({
name: 'Motion',
inheritAttrs: false,
props: {
as: {
type: String,
default: 'div',
},
transition: {
type: Object,
required: false,
},
animate: {
type: [Object, Array],
required: false,
},
initial: {
type: [Object, Array],
required: false,
},
},
setup(props) {
const slots = useSlots()
const attrs = useAttrs()
const instances = reactive<{ [key: number]: any }>({})
const propsConfig = computed(() => ({
transition: {
...getDefaultTransition(props.animate, props.transition),
...props.transition,
},
animate: {
...props.animate,
},
initial: {
...props.initial,
},
}))
function setNodeInstance(node: VNode, index: number, style: any) {
let animationInstance: AnimationPlaybackControls | null = null
node.props ??= {}
node.props.style ??= {}
node.props.style = {
...node.props.style,
...style,
willChange: 'transform',
transform: 'translateZ(0)',
}
node.props.onVnodeMounted = ({ el }) => {
animationInstance = animate(el, propsConfig.value.animate, propsConfig.value.transition)
instances[index] = animationInstance
}
node.props.onVnodeUnmounted = () => {
if (animationInstance)
animationInstance.stop()
}
node.props.onVnodeUpdated = ({ el }) => {
animationInstance = animate(el, propsConfig.value.animate, propsConfig.value.transition)
instances[index] = animationInstance
}
return node
}
return () =>
setNodeInstance(
h(
props.as,
mergeProps({ style: propsConfig.value.initial }, attrs),
slots,
),
0,
attrs.style,
)
},
})
export const transformPropOrder = [
'transformPerspective',
'x',
'y',
'z',
'translateX',
'translateY',
'translateZ',
'scale',
'scaleX',
'scaleY',
'rotate',
'rotateX',
'rotateY',
'rotateZ',
'skew',
'skewX',
'skewY',
]
export const transformProps = new Set(transformPropOrder)
const underDampedSpring: Partial<ValueAnimationOptions> = {
type: 'spring',
stiffness: 500,
damping: 25,
restSpeed: 10,
}
function criticallyDampedSpring(target: unknown): Partial<ValueAnimationOptions> {
return {
type: 'spring',
stiffness: 550,
damping: target === 0 ? 2 * Math.sqrt(550) : 30,
restSpeed: 10,
}
}
const keyframesTransition: Partial<ValueAnimationOptions> = {
type: 'keyframes',
duration: 0.8,
}
const ease: Partial<ValueAnimationOptions> = {
type: 'keyframes',
ease: [0.25, 0.1, 0.35, 1],
duration: 0.3,
}
function getDefaultTransition(valuesKey: any, options: any): any {
// Optimize getDefaultTransition function
if (options?.keyframes?.length > 2) {
return keyframesTransition
}
else if (valuesKey) {
let value: Partial<ValueAnimationOptions<number>> = {}
for (const key of Object.keys(valuesKey)) {
if (transformProps.has(key)) {
value = key.startsWith('scale')
? options?.keyframes?.length > 2
? criticallyDampedSpring(options.keyframes[1])
: underDampedSpring
: underDampedSpring
}
}
if (Object.keys(value).length > 0)
return value
}
return ease
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment