Skip to content

Commit

Permalink
fix: motion state unmount,svg elm initial props
Browse files Browse the repository at this point in the history
  • Loading branch information
rick-hup committed Nov 24, 2024
1 parent cb0cc7d commit 0283d5b
Show file tree
Hide file tree
Showing 14 changed files with 240 additions and 57 deletions.
53 changes: 29 additions & 24 deletions packages/motion/src/components/Motion.vue
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
<script lang="ts">
import type { IntrinsicElementAttributes } from 'vue'
import { Primitive } from './Primitive'
import { MotionState } from '@/state/motion-state'
import { isSVGElement } from '@/state/utils'
import { injectAnimatePresence } from './presence'
import { isMotionValue } from '@/utils'
import type { ElementType, Options, SVGAttributesWithMotionValues, SetMotionValueType } from '@/types'
</script>

<script setup lang="ts" generic="T extends ElementType = 'div'">
import { getCurrentInstance, onBeforeMount, onUnmounted, onUpdated, ref, useAttrs } from 'vue'
import type { ElementType, Options } from '@/state/types'
import { type IntrinsicElementAttributes, getCurrentInstance, onMounted, onUnmounted, onUpdated, ref, useAttrs } from 'vue'
import { injectMotion, provideMotion } from './context'
import { createStyles } from '@/state/style'
import { convertSvgStyleToAttributes, createStyles } from '@/state/style'
export interface MotionProps<T extends ElementType = 'div'> extends Options {
as?: T
asChild?: boolean
}
type IntrinsicElementAttributesAsMotionValues = SetMotionValueType<IntrinsicElementAttributes, keyof SVGAttributesWithMotionValues>
type ComBindProps = /* @vue-ignore */ Omit<IntrinsicElementAttributes[T], keyof Options | 'style' | 'as' | 'asChild'>
type ComBindProps = /* @vue-ignore */ Omit<IntrinsicElementAttributesAsMotionValues[T], keyof Options | 'style' | 'as' | 'asChild'>
defineOptions({
name: 'Motion',
inheritAttrs: true,
inheritAttrs: false,
})
const props = withDefaults(defineProps<MotionProps<T> & ComBindProps>(), {
const props = withDefaults(defineProps<ComBindProps & MotionProps<T>>(), {
as: 'div' as T,
asChild: false,
initial: undefined,
Expand All @@ -35,28 +35,23 @@ const props = withDefaults(defineProps<MotionProps<T> & ComBindProps>(), {
const { initial: presenceInitial, safeUnmount } = injectAnimatePresence({ initial: ref(undefined), safeUnmount: () => true })
const parentState = injectMotion(null)
const attrs = useAttrs()
const state = new MotionState(
{
...attrs,
...props,
},
parentState!,
)
provideMotion(state)
// const { primitiveElement, currentElement } = usePrimitiveElement()
const attrs = useAttrs()
const instance = getCurrentInstance()
onBeforeMount(() => {
onMounted(() => {
state.mount(instance?.vnode.el as HTMLElement)
state.update({
...attrs,
...props,
style: { ...createStyles(state.getTarget()), ...props.style },
initial: presenceInitial.value === false
? presenceInitial.value
: (
props.initial === true ? undefined : props.initial
),
})
})
Expand All @@ -77,13 +72,24 @@ onUpdated(() => {
})
})
function getStyle() {
if (!isSVGElement(props.as))
return !state.isMounted() ? { ...createStyles(props.style), ...createStyles(state.getTarget()) } : createStyles(props.style)
}
function getProps() {
const attrsProps = { ...attrs }
Object.keys(attrs).forEach((key) => {
if (isMotionValue(attrs[key]))
attrsProps[key] = attrs[key].get()
})
let styleProps: Record<string, any> = {}
function getSVGProps() {
return (!state.isMounted() && isSVGElement(props.as)) ? state.visualElement?.latestValues : undefined
if (!state.isMounted()) {
if (isSVGElement(props.as)) {
const { attributes, style } = convertSvgStyleToAttributes(state.getTarget())
Object.assign(attrsProps, attributes)
Object.assign(styleProps, style, props.style)
}
}
styleProps = createStyles(styleProps)
attrsProps.style = createStyles(styleProps)
return attrsProps
}
</script>

Expand All @@ -92,8 +98,7 @@ function getSVGProps() {
<Primitive
:as="as"
:as-child="asChild"
:style="getStyle()"
v-bind="getSVGProps()"
v-bind="getProps()"
>
<slot />
</Primitive>
Expand Down
1 change: 0 additions & 1 deletion packages/motion/src/components/Primitive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ export const Primitive = defineComponent({
},
setup(props, { attrs, slots }) {
const asTag = props.asChild ? 'template' : props.as

// For self closing tags, don't provide default slots because of hydration issue
const SELF_CLOSING_TAGS = ['area', 'img', 'input']
if (typeof asTag === 'string' && SELF_CLOSING_TAGS.includes(asTag))
Expand Down
26 changes: 13 additions & 13 deletions packages/motion/src/features/gestures/drag/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { dispatchPointerEvent } from '@/utils/events'
import type { MotionState } from '@/state/motion-state'
import { BaseGesture } from '@/features/gestures'
import { animate } from 'animate'
import type { DragOptions } from '@/state/types'
import type { DragOptions } from './types'

export class DragGesture extends BaseGesture {
private isDragging = false
Expand All @@ -28,20 +28,20 @@ export class DragGesture extends BaseGesture {
const deltaX = event.clientX - this.startPosition.x
const deltaY = event.clientY - this.startPosition.y

let newX = this.currentPosition.x + deltaX
let newY = this.currentPosition.y + deltaY
const newX = this.currentPosition.x + deltaX
const newY = this.currentPosition.y + deltaY

// 应用约束
if (this.constraints) {
if (this.constraints.left !== undefined)
newX = Math.max(this.constraints.left, newX)
if (this.constraints.right !== undefined)
newX = Math.min(this.constraints.right, newX)
if (this.constraints.top !== undefined)
newY = Math.max(this.constraints.top, newY)
if (this.constraints.bottom !== undefined)
newY = Math.min(this.constraints.bottom, newY)
}
// if (this.constraints) {
// if (this.constraints.left !== undefined)
// newX = Math.max(this.constraints.left, newX)
// if (this.constraints.right !== undefined)
// newX = Math.min(this.constraints.right, newX)
// if (this.constraints.top !== undefined)
// newY = Math.max(this.constraints.top, newY)
// if (this.constraints.bottom !== undefined)
// newY = Math.min(this.constraints.bottom, newY)
// }

// 更新元素位置
element.style.transform = `translate(${newX}px, ${newY}px)`
Expand Down
2 changes: 1 addition & 1 deletion packages/motion/src/features/gestures/drag/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { VariantLabels } from '@/state/types'
import type { VariantLabels } from '@/types'

export interface BoundingBox {
top: number
Expand Down
2 changes: 1 addition & 1 deletion packages/motion/src/features/gestures/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Options } from '@/state/types'
import type { Options } from '@/types'

export interface StateHandlers {
enable: VoidFunction
Expand Down
2 changes: 1 addition & 1 deletion packages/motion/src/state/event.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Variant } from './types'
import type { Variant } from '@/types'

export type MotionEventNames =
| 'motionstart'
Expand Down
4 changes: 2 additions & 2 deletions packages/motion/src/state/motion-state.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { AnimationFactory, MotionStateContext, Options } from '@/state/types'
import type { AnimationFactory, MotionStateContext, Options } from '@/types'
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'
Expand Down Expand Up @@ -81,7 +81,7 @@ export class MotionState {
}
const visualElement = visualElementStore.get(element)
this.visualElement = visualElement
visualElement.triggerBuild()
// 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 Down
72 changes: 71 additions & 1 deletion packages/motion/src/state/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ 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'
import type { MotionStyle } from '@/types'

type MotionStyleKey = Exclude<
keyof CSSStyleDeclaration,
Expand Down Expand Up @@ -67,3 +67,73 @@ export function createStyles(keyframes?: MotionStyle | DOMKeyframesDefinition):
}
return initialKeyframes
}

const SVG_STYLE_TO_ATTRIBUTES = {
fill: true,
stroke: true,
strokeWidth: true,
opacity: true,
fillOpacity: true,
strokeOpacity: true,
strokeLinecap: true,
strokeLinejoin: true,
strokeDasharray: true,
strokeDashoffset: true,
cx: true,
cy: true,
r: true,
d: true,
x: true,
y: true,
x1: true,
y1: true,
x2: true,
y2: true,
points: true,
pathLength: true,
transform: true,
viewBox: true,
width: true,
height: true,
preserveAspectRatio: true,
clipPath: true,
filter: true,
mask: true,
stopColor: true,
stopOpacity: true,
gradientTransform: true,
gradientUnits: true,
spreadMethod: true,
markerEnd: true,
markerMid: true,
markerStart: true,
textAnchor: true,
dominantBaseline: true,
fontFamily: true,
fontSize: true,
fontWeight: true,
letterSpacing: true,
vectorEffect: true,
} as const

export function convertSvgStyleToAttributes(keyframes?: MotionStyle | DOMKeyframesDefinition) {
const attributes: Record<string, any> = {}
const styleProps: Record<string, any> = {}

for (const key in keyframes as any) {
if (key in SVG_STYLE_TO_ATTRIBUTES) {
const attrKey = SVG_STYLE_TO_ATTRIBUTES[key as keyof typeof SVG_STYLE_TO_ATTRIBUTES]
const attrName = typeof attrKey === 'string' ? attrKey : key
const value = keyframes[key]
attributes[attrName] = isMotionValue(value) ? value.get() : value
}
else {
styleProps[key] = keyframes[key]
}
}

return {
attributes,
style: styleProps,
}
}
2 changes: 1 addition & 1 deletion packages/motion/src/state/transform.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { isDef } from '@vueuse/core'
import type { CssPropertyDefinition, CssPropertyDefinitionMap } from './types'
import type { CssPropertyDefinition, CssPropertyDefinitionMap } from '@/types'
import { noopReturn } from './utils'

const rotation: CssPropertyDefinition = {
Expand Down
2 changes: 1 addition & 1 deletion packages/motion/src/state/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Options } from '@/state/types'
import type { Options } from '@/types'
import type { DynamicAnimationOptions, Variant } from 'framer-motion'
import type { IntrinsicElementAttributes } from 'vue'

Expand Down
2 changes: 2 additions & 0 deletions packages/motion/src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export * from './transform'
export * from './framer-motion'
export * from './motion-values'
export * from './state'
export type MotionEventNames =
| 'motionstart'
| 'motioncomplete'
Expand Down
Loading

0 comments on commit 0283d5b

Please sign in to comment.