Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

file structure change #3

Merged
merged 4 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ jobs:
- name: Install dependencies
run: pnpm i

- name: PNPM build
run: pnpm run build

- name: Build Docs
run: pnpm docs:build
Expand Down
2 changes: 1 addition & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@
"@vue/compiler-core": "^3.4.14",
"@vue/compiler-dom": "^3.4.14",
"@vue/tsconfig": "^0.5.1",
"motion-v": "workspace:*",
"autoprefixer": "^10.4.16",
"lodash.template": "^4.5.0",
"motion-v": "0.1.0",
"pathe": "^1.1.2",
"rimraf": "^5.0.5",
"shikiji": "^0.10.0-beta.9",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"main": "index.js",
"scripts": {
"dev:play": "pnpm --filter './playground/nuxt' dev",
"dev": "pnpm --filter './packages/vue' dev",
"dev": "pnpm --filter './packages/motion' dev",
"build": "pnpm --filter './packages/**' build",
"test": "pnpm --filter './packages/motion' test",
"prepare": "pnpm simple-git-hooks",
Expand Down
6 changes: 3 additions & 3 deletions packages/motion/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "motion-v",
"version": "0.1.0",
"version": "0.1.1",
"description": "",
"author": "",
"license": "MIT",
Expand All @@ -10,10 +10,10 @@
".": {
"types": "./dist/src/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.umd.cjs"
"require": "./dist/index.cjs"
}
},
"main": "./dist/index.umd.cjs",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/src/index.d.ts",
"files": [
Expand Down
4 changes: 2 additions & 2 deletions packages/motion/src/components/Motion.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
import type { CSSProperties, IntrinsicElementAttributes } from 'vue'
import { Primitive } from './Primitive'
import { isSvgTag } from './utils'
import { MotionState, mountedStates } from '@/state/motion-state'
import { MotionState } from '@/state/motion-state'

type ElementType = keyof IntrinsicElementAttributes
</script>

<script setup lang="ts" generic="T extends ElementType = 'div'">
import { nextTick, onMounted, onUnmounted, onUpdated, ref } from 'vue'
import { onMounted, onUnmounted, onUpdated, ref } from 'vue'
import type { Options } from '@/state/types'
import { usePrimitiveElement } from './usePrimitiveElement'
import { injectAnimatePresence, injectMotion, provideMotion } from './context'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import type { MotionState } from '../motion-state'
import type { MotionEventNames } from '@/types'
import { Feature } from './feature'
import type { MotionEventNames } from '../event'
import { motionEvent } from '../event'

export class EventFeature extends Feature {
private handlers: Partial<Record<MotionEventNames, (event: Event) => void>> = {}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import type { MotionState } from '@/state/motion-state'
import { EventFeature, HoverGesture, InViewGesture, PressGesture } from './'
import {
// DragGesture,
EventFeature,
HoverGesture,
InViewGesture,
PressGesture,
} from './'

export abstract class Feature {
state: MotionState
Expand All @@ -22,6 +28,7 @@ export class FeatureManager {
new HoverGesture(state),
new PressGesture(state),
new InViewGesture(state),
// new DragGesture(state),
new EventFeature(state),
]
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { dispatchPointerEvent } from '@/utils/events'
import { Feature } from '@/state/features/feature'
import { Feature } from '@/features/feature'
import type { MotionEventNames } from '@/types'

export abstract class BaseGesture extends Feature {
Expand Down
115 changes: 115 additions & 0 deletions packages/motion/src/features/gestures/drag/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { dispatchPointerEvent } from '@/utils/events'
import type { MotionState } from '@/state/motion-state'
import { BaseGesture } from '@/features/gestures'
import { animate } from 'framer-motion/dom'
import type { DragOptions } from '@/state/types'

export class DragGesture extends BaseGesture {
private isDragging = false
private currentPosition = { x: 0, y: 0 }
private startPosition = { x: 0, y: 0 }
private constraints?: DragOptions['constraints']

isActive() {
return Boolean(this.state.getOptions())
}

constructor(state: MotionState) {
super(state)
this.subscribeEvents = () => {
const element = this.state.getElement()
const options = this.state.getOptions().dragOptions || {}
this.constraints = options.constraints

const onPointerMove = (event: PointerEvent) => {
if (!this.isDragging)
return

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

// 应用约束
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)`

dispatchPointerEvent(element, 'drag', {
...event,
point: { x: newX, y: newY },
})
}

const onPointerUp = (event: PointerEvent) => {
if (!this.isDragging)
return

this.isDragging = false
this.currentPosition.x += event.clientX - this.startPosition.x
this.currentPosition.y += event.clientY - this.startPosition.y

this.state.setActive('drag', false)
dispatchPointerEvent(element, 'dragend', event)

window.removeEventListener('pointermove', onPointerMove)
window.removeEventListener('pointerup', onPointerUp)

// 处理拖拽结束后的动画
if (options.dragSnapToOrigin) {
animate(element, {
// eslint-disable-next-line ts/ban-ts-comment
// @ts-expect-error
x: 0,
y: 0,
transition: { type: 'spring', stiffness: 400, damping: 40 },
})
this.currentPosition = { x: 0, y: 0 }
}
}

const onPointerDown = (event: PointerEvent) => {
this.isDragging = true
this.startPosition = { x: event.clientX, y: event.clientY }

this.state.setActive('drag', true)
dispatchPointerEvent(element, 'dragstart', event)

window.addEventListener('pointermove', onPointerMove)
window.addEventListener('pointerup', onPointerUp)
}

// 设置初始样式
element.style.touchAction = 'none'
element.style.userSelect = 'none'
element.style.cursor = 'grab'

element.addEventListener('pointerdown', onPointerDown as EventListener)

return () => {
element.removeEventListener('pointerdown', onPointerDown as EventListener)
window.removeEventListener('pointermove', onPointerMove)
window.removeEventListener('pointerup', onPointerUp)
}
}
}

mount() {
this.updateGestureSubscriptions()
}

update() {
this.updateGestureSubscriptions()
}
}
150 changes: 150 additions & 0 deletions packages/motion/src/features/gestures/drag/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import type { VariantLabels } from '@/state/types'

export interface BoundingBox {
top: number
right: number
bottom: number
left: number
}
type DragElastic = boolean | number | Partial<BoundingBox>

export interface DragOptions {
/**
* Enable dragging for this element. Set to `false` by default.
* Set `true` to drag in both directions.
* Set `"x"` or `"y"` to only drag in a specific direction.
*
* ```template
* <Motion drag="x" />
* ```
*/
drag?: boolean | 'x' | 'y'
/**
* If `true`, the drag direction will be locked to the initial drag direction.
* Set to `false` by default.
*
* ```template
* <Motion dragOptions="{ directionLock: true, drag: 'x' }" />
* ```
*/
directionLock?: boolean

/**
* Allows drag gesture propagation to child components. Set to `false` by
* default.
*
* ```template
* <Motion dragOptions="{ propagation: true, drag: 'x' }" />
* ```
*/
propagation?: boolean
/**
* Applies constraints on the permitted draggable area.
*
* It can accept an object of optional `top`, `left`, `right`, and `bottom` values, measured in pixels.
* This will define a distance the named edge of the draggable component.
*
* Alternatively, it can accept a `ref` to another component created with React's `useRef` hook.
* This `ref` should be passed both to the draggable component's `dragConstraints` prop, and the `ref`
* of the component you want to use as constraints.
*
* ```template
* // In pixels
* <Motion dragOptions="{ constraints: { left: 0, right: 300 }, drag: 'x' }" />
*
* // As a ref to another component
* const MyComponent = () => {
* const constraintsRef = ref()
*
* return (
* <div ref={constraintsRef}>
* <Motion dragOptions="{ constraints: constraintsRef, drag: 'x' }" />
* </div>
* )
* }
* ```
*/
constraints?: false | Partial<BoundingBox> | HTMLElement

/**
* The degree of movement allowed outside constraints. 0 = no movement, 1 =
* full movement.
*
* Set to `0.5` by default. Can also be set as `false` to disable movement.
*
* By passing an object of `top`/`right`/`bottom`/`left`, individual values can be set
* per constraint. Any missing values will be set to `0`.
*
* ```template
* <Motion dragOptions="{ dragElastic: 0.2, constraints: { left: 0, right: 300 }, drag: 'x' }" />
* ```
*/
elastic?: DragElastic
/**
* Apply momentum from the pan gesture to the component when dragging
* finishes. Set to `true` by default.
*
* ```template
* <Motion dragOptions="{ dragMomentum: false, constraints: { left: 0, right: 300 }, drag: 'x' }" />
* ```
*/
momentum?: boolean
/**
* Allows you to change dragging inertia parameters.
* When releasing a draggable Frame, an animation with type `inertia` starts. The animation is based on your dragging velocity. This property allows you to customize it.
*
* ```jsx
* <motion.div
* drag
* dragTransition={{ bounceStiffness: 600, bounceDamping: 10 }}
* />
* ```
*/
// dragTransition?: InertiaOptions
/**
* Usually, dragging is initiated by pressing down on a component and moving it. For some
* use-cases, for instance clicking at an arbitrary point on a video scrubber, we
* might want to initiate dragging from a different component than the draggable one.
*
* By creating a `dragControls` using the `useDragControls` hook, we can pass this into
* the draggable component's `dragControls` prop. It exposes a `start` method
* that can start dragging from pointer events on other components.
*
* ```jsx
* const dragControls = useDragControls()
*
* function startDrag(event) {
* dragControls.start(event, { snapToCursor: true })
* }
*
* return (
* <>
* <div onPointerDown={startDrag} />
* <motion.div drag="x" dragControls={dragControls} />
* </>
* )
* ```
*/
// dragControls?: DragControls
/**
* If true, element will snap back to its origin when dragging ends.
*
* Enabling this is the equivalent of setting all `dragConstraints` axes to `0`
* with `dragElastic={1}`, but when used together `dragConstraints` can define
* a wider draggable area and `dragSnapToOrigin` will ensure the element
* animates back to its origin on release.
*/
snapToOrigin?: boolean
}

export interface DragProps {
/**
* Properties or variant label to animate to while the drag gesture is recognised.
*
* ```jsx
* <motion.div whileDrag={{ scale: 1.2 }} />
* ```
*/
drag?: VariantLabels
dragOptions?: DragOptions
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { dispatchPointerEvent } from '@/utils/events'
import type { Gesture } from './types'
import type { MotionState } from '@/state/motion-state'
import { BaseGesture } from '@/state/features/gestures'
import { BaseGesture } from '@/features'

function mouseEvent(element: Element, name: 'hoverstart' | 'hoverend', action: VoidFunction) {
function mouseEvent(element: HTMLElement, name: 'hoverstart' | 'hoverend', action: VoidFunction) {
return (event: PointerEvent) => {
if (event.pointerType && event.pointerType !== 'mouse')
return
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { dispatchPointerEvent } from '@/utils/events'
import type { MotionState } from '@/state/motion-state'
import { BaseGesture } from '@/state/features/gestures'
import { BaseGesture } from '@/features'
import { inView } from 'framer-motion/dom'

export class InViewGesture extends BaseGesture {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './base'
export * from './hover'
export * from './press'
export * from './in-view'
export * from './drag'
Loading
Loading