Skip to content

Commit

Permalink
Merge pull request #4 from rick-hup/feat/motion-value
Browse files Browse the repository at this point in the history
Feat/motion value
  • Loading branch information
rick-hup authored Nov 23, 2024
2 parents c1280c8 + 53d2860 commit 30e44ec
Show file tree
Hide file tree
Showing 36 changed files with 1,013 additions and 154 deletions.
2 changes: 1 addition & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "docs",
"type": "module",
"version": "0.8.7",
"version": "0.2.0",
"files": [
"dist"
],
Expand Down
9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"name": "vue-motion",
"version": "0.1.0",
"version": "0.2.0",
"private": true,
"description": "",
"author": "",
"license": "MIT",
"homepage": "https://motion.seacoly.me/",
"homepage": "https://github.com/rick-hup/motion-vue",
"keywords": [],
"main": "index.js",
"scripts": {
Expand All @@ -19,7 +19,9 @@
"docs:build": "pnpm --filter docs build",
"docs:gen": "pnpm --filter docs docs:gen",
"docs:contributors": "pnpm --filter docs docs:contributors",
"lint-staged": "lint-staged"
"lint-staged": "lint-staged",
"bumpp": "bumpp package.json packages/*/package.json docs/package.json",
"pub:release": "pnpm --filter './packages/motion' pub:release"
},
"dependencies": {
"@vueuse/core": "^11.1.0",
Expand All @@ -33,6 +35,7 @@
"@commitlint/cli": "^17.0.3",
"@commitlint/config-conventional": "^17.0.3",
"@vue/tsconfig": "^0.5.1",
"bumpp": "^9.8.1",
"eslint": "^9.9.1",
"lint-staged": "^15.2.9",
"simple-git-hooks": "^2.11.1",
Expand Down
10 changes: 8 additions & 2 deletions packages/motion/package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
{
"name": "motion-v",
"version": "0.1.1",
"version": "0.2.0",
"description": "",
"author": "",
"license": "MIT",
"homepage": "https://github.com/rick-hup/motion-vue",
"repository": {
"type": "git",
"url": "https://github.com/rick-hup/motion-vue.git"
},
"keywords": [],
"sideEffects": false,
"exports": {
Expand All @@ -24,7 +29,8 @@
"dev": "vite build --watch",
"build": "vite build",
"test": "vitest",
"coverage": "vitest run --coverage"
"coverage": "vitest run --coverage",
"pub:release": "pnpm publish --access public"
},
"dependencies": {
"framer-motion": "11.11.11"
Expand Down
52 changes: 14 additions & 38 deletions packages/motion/src/components/Motion.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<script lang="ts">
import type { CSSProperties, IntrinsicElementAttributes } from 'vue'
import type { IntrinsicElementAttributes } from 'vue'
import { Primitive } from './Primitive'
import { isSvgTag } from './utils'
// import { isSvgTag } from './utils'
import { MotionState } from '@/state/motion-state'
import { isSVGElement } from '@/state/utils'
type ElementType = keyof IntrinsicElementAttributes
</script>
Expand All @@ -17,15 +18,14 @@ import { createStyles, style } from '@/state/style'
export interface MotionProps<T extends ElementType = 'div'> extends Options {
as?: T
asChild?: boolean
style?: CSSProperties
hover?: Options['hover']
press?: Options['press']
inView?: Options['inView']
inViewOptions?: Options['inViewOptions']
}
type ComBindProps = /* @vue-ignore */ Omit<IntrinsicElementAttributes[T], keyof Options | 'style' | 'as' | 'asChild'>
defineOptions({
name: 'Motion',
inheritAttrs: true,
})
const props = withDefaults(defineProps<MotionProps<T> & ComBindProps>(), {
as: 'div' as T,
asChild: false,
Expand All @@ -48,9 +48,12 @@ provideMotion(state)
const { primitiveElement, currentElement } = usePrimitiveElement()
onMounted(() => {
state.mount(currentElement.value)
if (isSVGElement(props.as)) {
style.set(currentElement.value, 'opacity', '')
}
state.update({
...props,
style: { ...props.style, ...createStyles(state.getTarget()) },
style: { ...createStyles(state.getTarget()), ...props.style },
initial: presenceInitial.value === false
? presenceInitial.value
: (
Expand All @@ -63,27 +66,7 @@ onUnmounted(() => {
unmounted = true
})
let manuallyAppliedMotionStyles = false
onUpdated(() => {
/**
* Vue reapplies all styles every render, rather than diffing and
* only reapplying the ones that change. This means that initially
* calculated motion styles also get reapplied every render, leading
* to incorrect animation origins.
*
* To prevent this, once an element is mounted we hand over these
* styles to Motion. This will currently still lead to a jump if interrupting
* transforms in browsers where the number polyfill is used.
*/
if (!manuallyAppliedMotionStyles && currentElement.value) {
manuallyAppliedMotionStyles = true
const styles = createStyles(state.getTarget())
for (const key in styles) {
style.set(currentElement.value, key, styles[key])
}
}
state.update({
...props,
initial: presenceInitial.value === false
Expand All @@ -94,17 +77,11 @@ onUpdated(() => {
})
})
function getSVGProps() {
if (!state.isMounted() && isSvgTag(props.as)) {
return state.getTarget()
}
}
function getStyle() {
if (isSvgTag(props.as)) {
return props.style
if (isSVGElement(props.as) && !state.isMounted()) {
return { opacity: 0 }
}
return !state.isMounted() ? { ...props.style, ...createStyles(state.getTarget()) } : props.style
return !state.isMounted() ? { ...createStyles(props.style), ...createStyles(state.getTarget()) } : createStyles(props.style)
}
</script>

Expand All @@ -114,7 +91,6 @@ function getStyle() {
ref="primitiveElement"
:as="as"
:as-child="asChild"
v-bind="getSVGProps()"
:style="getStyle()"
>
<slot />
Expand Down
27 changes: 27 additions & 0 deletions packages/motion/src/components/type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// types.ts
interface SVGRenderState {
style: Record<string, any>
transform: Record<string, any>
transformOrigin: {
originX?: string | number
originY?: string | number
originZ?: string | number
}
attrs: Record<string, any>
dimensions?: {
x: number
y: number
width: number
height: number
}
}

interface SVGProps {
attrX?: number
attrY?: number
attrScale?: number
pathLength?: number
pathSpacing?: number
pathOffset?: number
[key: string]: any
}
41 changes: 0 additions & 41 deletions packages/motion/src/components/utils.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/motion/src/features/gestures/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export abstract class BaseGesture extends Feature {
}

export function createGestureEvent(
element: Element,
element: HTMLElement,
name: MotionEventNames,
action: VoidFunction,
) {
Expand Down
4 changes: 2 additions & 2 deletions packages/motion/src/features/gestures/drag/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { dispatchPointerEvent } from '@/utils/events'
import type { MotionState } from '@/state/motion-state'
import { BaseGesture } from '@/features/gestures'
import { animate } from 'framer-motion/dom'
import { animate } from 'animate'
import type { DragOptions } from '@/state/types'

export class DragGesture extends BaseGesture {
Expand All @@ -18,7 +18,7 @@ export class DragGesture extends BaseGesture {
super(state)
this.subscribeEvents = () => {
const element = this.state.getElement()
const options = this.state.getOptions().dragOptions || {}
const options = (this.state.getOptions() as any).dragOptions || {}
this.constraints = options.constraints

const onPointerMove = (event: PointerEvent) => {
Expand Down
20 changes: 20 additions & 0 deletions packages/motion/src/framer-motion.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,23 @@ declare module 'framer-motion/dist/es/render/store.mjs' {
declare module 'framer-motion/dist/es/animation/utils/create-visual-element.mjs' {
export const createDOMVisualElement: (element: Element) => any
}

declare module 'motion-value' {
import type { MotionValue } from 'framer-motion/dom'

export const collectMotionValues: { current: MotionValue[] | undefined } // 根据实际需要定义具体类型

}

declare module 'framer-main-animation' {
import type { animateValue as animateValueF } from 'framer-motion'

export const animateValue: typeof animateValueF
export type MainThreadAnimation = ReturnType<typeof animateValueF>
}

declare module 'animate' {
import type { animate as animateF } from 'framer-motion'

export const animate: typeof animateF
}
1 change: 1 addition & 0 deletions packages/motion/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from 'framer-motion/dom'
export { default as Motion, type MotionProps } from './components/Motion.vue'
export { default as AnimatePresence, type AnimatePresenceProps } from './components/AnimatePresence.vue'
export * from './value'
10 changes: 8 additions & 2 deletions packages/motion/src/state/motion-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { invariant } from 'hey-listen'
import { visualElementStore } from 'framer-motion/dist/es/render/store.mjs'
import { createDOMVisualElement } from 'framer-motion/dist/es/animation/utils/create-visual-element.mjs'
import { isDef } from '@vueuse/core'
import type { DOMKeyframesDefinition, DynamicAnimationOptions } from 'framer-motion'
import { animate } from 'framer-motion/dom'
import type { DOMKeyframesDefinition, DynamicAnimationOptions, VisualElement } from 'framer-motion'
import { animate } from 'animate'
import { getOptions, hasChanged, noop, resolveVariant } from '@/state/utils'
import { FeatureManager } from '@/features'
import { style } from '@/state/style'
Expand Down Expand Up @@ -33,6 +33,8 @@ export class MotionState {

private target: DOMKeyframesDefinition
private featureManager: FeatureManager

private visualElement: VisualElement
constructor(options: Options, parent?: MotionState) {
this.options = options
this.parent = parent
Expand Down Expand Up @@ -78,6 +80,8 @@ export class MotionState {
createDOMVisualElement(element)
}
const visualElement = visualElementStore.get(element)
this.visualElement = visualElement
visualElement.triggerBuild()
visualElement.update(this.options as any, this.parent?.context as any)
if (typeof this.initial === 'object') {
for (const key in this.initial) {
Expand All @@ -104,6 +108,8 @@ export class MotionState {

update(options: Options) {
this.options = options
this.visualElement.update(this.options as any, this.parent?.context as any)

// 更新特征
this.featureManager.update()
// 更新动画
Expand Down
10 changes: 6 additions & 4 deletions packages/motion/src/state/style.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { DOMKeyframesDefinition } from 'framer-motion'
import { isCssVar, isNumber } from './utils'
import { buildTransformTemplate, isTransform, transformAlias, transformDefinitions } from './transform'
import { isMotionValue } from '@/utils'
import type { MotionStyle } from '@/state/types'

type MotionStyleKey = Exclude<
keyof CSSStyleDeclaration,
Expand Down Expand Up @@ -29,12 +31,12 @@ export const style = {
},
}

export function createStyles(keyframes?: DOMKeyframesDefinition): any {
export function createStyles(keyframes?: MotionStyle | DOMKeyframesDefinition): any {
const initialKeyframes: any = {}
const transforms: [string, any][] = []
for (let key in keyframes) {
const value = keyframes[key as keyof typeof keyframes]

for (let key in keyframes as any) {
let value = keyframes[key]
value = isMotionValue(value) ? value.get() : value
if (isTransform(key)) {
if (key in transformAlias) {
key = transformAlias[key as keyof typeof transformAlias]
Expand Down
18 changes: 15 additions & 3 deletions packages/motion/src/state/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { TransformProperties } from '@/types'
import type { DOMKeyframesDefinition, DynamicAnimationOptions } from 'framer-motion'
import type { animate } from 'framer-motion/dom'
import type { CSSProperties } from 'vue'
import type { MotionValue } from 'framer-motion/dom'
import type { animate } from 'animate'
import type { IntrinsicElementAttributes } from 'vue'

type AnimationPlaybackControls = ReturnType<typeof animate>

Expand All @@ -25,7 +27,13 @@ export interface DragOptions {
constraints?: false | Partial<BoundingBox>
dragSnapToOrigin?: boolean
}
export type MotionStyle = Partial<{
[K in keyof Variant]: Variant[K] | MotionValue
}>
export type ElementType = keyof IntrinsicElementAttributes

export interface Options {
as?: ElementType
inViewOptions?: InViewOptions & { once?: boolean }
inView?: string | Variant
press?: string | Variant
Expand All @@ -36,8 +44,12 @@ export interface Options {
variants?: {
[k: string]: Variant
}
style?: MotionStyle
transformTemplate?: (
transform: TransformProperties,
generatedTransform: string
) => string
transition?: DynamicAnimationOptions
style?: CSSProperties
onMotionStart?: (target: DOMKeyframesDefinition) => void
onMotionComplete?: (target: DOMKeyframesDefinition) => void
onHoverStart?: (e: PointerEvent) => void
Expand Down
Loading

0 comments on commit 30e44ec

Please sign in to comment.