# @lovince/motion 中文文档

规范地址：https://npmcn.com/package/%40lovince/motion/

版本：0\.4\.0
npm 元数据更新时间：2026-11-01T18:05:16.846Z
中文译文更新时间：2026-11-01T18:49:40.686Z
软件包许可：MIT

本文由上游 README 机器翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40lovince/motion
原文对照：https://npmcn.com/package/%40lovince/motion/?lang=original
项目主页：https://github.com/Mac254/Lovince_product_journey#readme
源码仓库：https://github.com/Mac254/Lovince_product_journey

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：134 kB，101 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=18
- 运行依赖：1 个
- peer 依赖：vue ^3\.3\.0，gsap ，lenis ，react \>=18\.0\.0
- 首次发布：2026-10-15T20:42:48.007Z
- 最新版本发布：2026-11-01T18:05:16.687Z
- 已发布版本数：5（近 12 个月 5 个）

## 中文简介

与框架无关的运动系统：原生CSS显示，管理GSAP时间线和滚动场景，以及Lenis平滑滚动-强制清理和减少运动处理。

## 安装

```sh
npm install @lovince/motion
```

也可以使用：`pnpm add @lovince/motion`、`yarn add @lovince/motion`、`bun add @lovince/motion`

## README

# @lovince /运动

与框架无关的运动系统。 **CSS** 处理轻的界面运动， **GSAP** 编排时间线和滚动场景，以及 **微弱的** 提供了 平滑滚动。每个动画都有一个所有者，一个清理和一个 reduced-motion政策。

> **状态: `0.4.0`、实验。** API之前可能会更改 `1.0`.

```bash
bun add @lovince/motion
bun add gsap lenis      # only for the entries that use them
```

我们的目标不是让所有东西都有动画效果。它是让一个网站做的动作 具有一致性、可中断性和可访问性。

## 条目

每个引擎都位于自己的条目后面，因此应用程序只需为其导入的内容付费 一个缺失的可选节点不会破坏其他任何东西。

| 进口 | 需要 | 给了 |
| --- | --- | --- |
| `@lovince/motion` | \- - - - - - | `createMotion`的偏好, `revealOnScroll`， `fadeIn`， `splitText`， `focusTarget`， `whenMotionAllowed`，动作配置和令牌 |
| `@lovince/motion/gsap` | `gsap` | `createTimeline`， `createStagger`， `createScrollReveal`， `createParallax`， `createHorizontalScroll`， `gsapScrollDriver` |
| `@lovince/motion/scroll` | `lenis` | `createSmoothScroll` |
| `@lovince/motion/presets` | `gsap` | `heroEntrance`， `revealText`， `revealImage`， `followState`， `createPageTransition` |
| `@lovince/motion/vue` | `vue` | `useMotion`， `useMotionEffect`， `v-reveal` |
| `@lovince/motion/primitives` | `gsap` | `initMotion` 以及每个数据属性原语 |
| `@lovince/motion/primitives/<name>` | `gsap` （`scroll` 也 `lenis`） | 一个原始的: `reveal`， `split`， `scrub`， `parallax`， `magnetic`， `marquee`， `liquid`， `ripple`， `progress`， `headroom`， `transition`， `scroll` |
| `@lovince/motion/adapters/vue` · `react` · `svelte` · `astro` | `gsap` +框架 | `vMotion`， `useMotion`, `motion` 行动, `bindAstro` |
| `@lovince/motion/styles.css` | \- - - - - - | 令牌+实用程序+预设+基本样式(或 `tokens.css`， `utilities.css`， `presets.css`， `primitives.css` 他们自己) |

`gsap`， `lenis`， `vue` 和 `react` 是可选的同伴，所以你的应用程序控制他们 版本，并且在运行时永远不会有第二个副本。

## 原则

-   **克制。** 运动解释了变化；它从不为自己而装饰。12-48px旅行。
-   **进入时要放松，离开时要放松。** 出口运行于 `exitRatio` （0.65×）的入口。
-   **可中断。** 吐温类使用 `overwrite: 'auto'` 和 `quickTo`所以一个新的手势永远不会等着旧的手势。
-   **只有变换和不透明度**,再加上 `clip-path` 用于显示和液体指示。
-   **符号，而不是原始数字。** 持续时间，轻松度，距离和错开来自 `@lovince/tokens`.
-   **简化运动是一级模式**，而不是事后的想法：内容立即显示，循环保持不变。
-   **每个原语都返回一个清理操作。**

## 令牌

| 集团 | 值 | CSS |
| --- | --- | --- |
| 持续时间 | Xs 0.2·sm 0.4·md 0.7·lg 1.1·xl 1.6 (s) | `--motion-duration-{xs…xl}` |
| 缓解 | 出 `expo.out` ·inOut `power3.inOut` ·在 `power2.in` ·软 `power2.out` ·液体（弹簧） | `--motion-ease-{out,in-out,in,soft,glide,liquid}` |
| 距离 | Sm 12·md 24·lg 48（在16px根处，按间距比例） | `--motion-distance-{sm,md,lg}` |
| 错开 | 紧固0.04·底座0.06·松动0.1 (s) | `--motion-stagger-{tight,base,loose}` |
| exitRatio | 0.65 | `--motion-exit-ratio` |

```ts
import { motionTokens, exitDuration } from '@lovince/motion'
gsap.to(el, { y: 0, duration: motionTokens.duration.md, ease: motionTokens.ease.out })
gsap.to(el, { autoAlpha: 0, duration: exitDuration(motionTokens.duration.md), ease: motionTokens.ease.in })
```

测试让CSS镜像和TypeScript的值保持同步。

## 模式

Lovince的界面已经以几种可识别的方式移动。 `motionPatterns` 将它们命名为新的工作匹配：

| 模式 | 使用 | 时机 |
| --- | --- | --- |
| `state` | 颜色，边框，背景变化 | `micro`，标准ease |
| `move` | 指示器，拇指，药片的更换位置 | `morph`，强调轻松 |
| `enter` | 弹窗，祝酒，物品到货 | `enter`，强调轻松 |
| `exit` | 什么离开 | 0.65× `enter`，放松 |
| `liquid` | 分节拇指，TAB药丸，选择 | `overlay`，春沉降 |
| `scene` | 卷轴显示，英雄 | `scene.md`， `expo.out` |
| `loop` | 遮罩，旋转器，微光 | 线性，当离开屏幕或被询问时暂停 |

## 原语

标记页面，然后初始化一次：

```html
<script>document.documentElement.classList.add('js')</script>
<link rel="stylesheet" href="@lovince/motion/styles.css" />

<header data-headroom>…</header>
<div data-scroll-progress></div>
<h1 data-split="lines">Big things, gently</h1>
<p data-scrub>Words light up as you scroll past them</p>
<h2 data-kinetic="scramble">Signal acquired</h2>
<div data-reveal="lg" data-delay="0.1">…</div>
<li data-reveal="pop">…</li>
<img data-parallax="0.2" src="…" alt="" />
<a data-magnetic="0.3" href="#">Book now</a>
<button data-ripple>Pick seat</button>
<div data-marquee="40" data-marquee-velocity>
  <ul data-marquee-track>…</ul>
  <button data-marquee-toggle>Pause</button>
  <button data-marquee-nudge="4">Faster</button>
</div>
<div data-liquid role="tablist">
  <span data-liquid-indicator></span>
  <button data-liquid-item role="tab" aria-selected="true">Day</button>
  <button data-liquid-item role="tab" aria-selected="false">Week</button>
</div>
```

```ts
import { initMotion } from '@lovince/motion/primitives'
import { createScroll } from '@lovince/motion/primitives/scroll'

const motion = initMotion({ scroll: createScroll() })
motion.destroy()
```

| 属性 | 价值 | 做 |
| --- | --- | --- |
| `data-reveal` | `sm` · `md` · `lg` · `pop` | 随着画面滚动而逐渐淡出； `pop` 弹簧在 |
| `data-split` | `lines` · `words` | 字体加载后分割；被蒙面的线条从自己的边缘升起 |
| `data-scrub` | 调暗不透明度，默认0.18 | 点亮文字，擦洗卷轴 |
| `data-kinetic` | `tilt` · `letters` · `flip` · `blur` · `scramble` · `wipe` · `fill` · `slide` · `marker` | 动态类型;填充和滑动跟随滚动，其余播放一次； `<mark>` 获得绘制的高亮(`--kinetic-marker`） |
| `data-parallax` | 速度，默认为0.15 | 飘浮在卷轴上 |
| `data-magnetic` | 强度，默认0.3 | 拉向指针，仅悬停设备 |
| `data-marquee` | Px /s，默认为40，反向为负 | 无缝循环;暂停悬停，聚焦，离开屏幕和打开 `data-marquee-toggle` (2.2.2 WCAG) |
| `data-liquid` | \- - - - - - | 一颗药丸横跨两种物质，挤压、过度并落在活性物质上 |
| `data-ripple` | \- - - - - - | 一个环从压点展开 |
| `data-scroll-progress` | \- - - - - - | `scaleX` 跟踪页面进度 |
| `data-headroom` | 偏移量px，默认值420 | 隐藏向下滚动，显示向上滚动或聚焦 |
| `data-delay` | 秒 | 添加到显露或分裂中 |
| `data-lenis-prevent` | \- - - - - - | 让内部滚动条本地滚动 |

每个原语也可以单独调用，如 `(root?, options?) => Cleanup`：

```ts
import { liquid } from '@lovince/motion/primitives/liquid'
const stop = liquid('#filters')
```

液体指示器跟随组件已经报告的内容： `aria-selected`， `aria-pressed`， `aria-checked`， `aria-current` 或者一个 `data-state` 的 `on`， `active` 或者 `checked`因此，它可以与Lovince标签、切换组和分段控件一起使用，而无需连接。

**FOUC警卫。** 与一个 `js` 课上 `<html>`，显示和分割目标开始隐藏在CSS中，所以它们不会在脚本运行之前闪烁。没有JavaScript，在减少运动和清理后，它们是可见的。 `initMotion` 如果缺少类，则添加该类。

## 页面转换

`transition()` 遵循一个顺序，所以滚动，触发器和焦点永远不会不一致：

停止滚动→退出→销毁旧的原语→交换→滚动到顶部→init→刷新→开始滚动→进入

```ts
import { transition } from '@lovince/motion/primitives/transition'
const flow = transition({ scroll, init: (container) => initMotion({ root: container }).destroy, focus: 'h1' })
await flow.navigate(async () => { document.querySelector('main')!.replaceWith(await fetchPage(url)) })
```

## 适配器

```vue
<section v-motion="['reveal', 'liquid']">…</section>
```

```tsx
const ref = useMotion<HTMLDivElement>({ primitives: ['reveal'] })
return <div ref={ref}>…</div>
```

```svelte
<section use:motion={{ primitives: ['reveal'] }}>…</section>
```

```astro
<script>import { bindAstro } from '@lovince/motion/adapters/astro'; bindAstro()</script>
```

## 谁拥有什么

| 引擎 | 拥有 |
| --- | --- |
| CSS | 悬停，焦点和按压反馈，下划线，小过渡，装饰循环，简单的滚动显示 |
| GSAP | 英雄序列，交错和分割排版，滚动链接的场景，固定，水平画廊，页面过渡 |
| 微弱的 | 当两者同时使用时，在GSAP的时钟上平滑滚动，作为每个滚动条的一个服务 |

给每个动画一个所有者。永远不要用CSS和 马上GSAP。

## 快速启动

```ts
import '@lovince/motion/styles.css'
import { createMotion, revealOnScroll } from '@lovince/motion'
import { gsapScrollDriver } from '@lovince/motion/gsap'
import { heroEntrance } from '@lovince/motion/presets'
import { createSmoothScroll } from '@lovince/motion/scroll'

const motion = createMotion({ root: '#app' })

heroEntrance({ label: '.eyebrow', heading: 'h1', body: '.lede', actions: '.ctas', media: '.hero-image' }, { context: motion })
revealOnScroll('[data-reveal]', { context: motion })
const scroll = createSmoothScroll({ driver: gsapScrollDriver() })

// When this view goes away:
motion.destroy()   // reverts every tween, trigger, observer and split it owns
scroll.destroy()
```

## 该模型

-   **`createMotion()` 是所有者。** 它将选择器限定为根，保存每个 效果和清理，和 `destroy()` 将它们全部还原，首先是最新的。每个API 需要 `{ context }`． 如果没有上下文，调用将获得自己的上下文并返回 清理。
-   **当首选项改变时，效果会重新运行。** 效果设置并返回 其撤销。如果用户打开或关闭减少运动，而页面是 打开，效果撤消，再次运行 `scope.reduced` 更新。 进程会继续，所以一个完成的入口永远不会重播。
-   **结果，而不是暂停。** 任何结束的东西都会返回一个解决的承诺 有一次, `'finished'` 或者 `'cancelled'`，从不拒绝。摧毁了 拥有上下文将取消它。没有什么是等待计时器的。
-   **一个词汇。** 入口 `fade`， `fade-up`， `fade-down`， `slide-left`， `slide-right`， `scale` 和 `clip` 在CSS中是一样的 显示, `fadeIn` （Web Animations）和每个GSAP API。
-   **令牌决定。** 持续时间，曲线和行进距离来自 `@lovince/tokens`. `defaultMotionConfig` 使用 `duration.travel` 和 `ease.out` （`power3.out`)，以及CSS `--motion-*` 属性是别名 的 `--lovince-*` 所以CSS和GSAP的运行方式是一样的。

```ts
const config: MotionConfig = { duration: 0.72, ease: 'power3.out', stagger: 0.08, respectReducedMotion: true }
```

## 减少运动

一个核心政策是：under `prefers-reduced-motion: reduce`，运动被取代 它的最终状态，不仅仅是缩短。

| API | 在减速运动下 |
| --- | --- |
| `revealOnScroll`， `createScrollReveal` | 显示内容；没有什么是隐藏的 |
| `fadeIn`， `createTimeline`,预设 | 跳到最终状态；承诺解决 `finished` |
| `createParallax`， `createHorizontalScroll` | 不创建;静态布局为 |
| `revealText` | 文本永远不会被分割 |
| `followState` | 各州立即适用 |
| `createSmoothScroll` | 微弱的;本机滚动，实时切换 |

样式表有相同的规则，所以只有css的动作也遵循它。集 `respectReducedMotion: false` 只为了有意义的动作。

## 渐进增强

内容永远不会被单独的CSS隐藏。显示只隐藏一个元素 设置 `data-motion-state="hidden"` 从脚本中删除它。如果 JavaScript、GSAP或样式表无法加载，页面只是静态的。 在服务器上没有效果运行，并且每个条目都安全导入。

## CSS层

```html
<a class="motion-link" href="/docs">Docs</a>
<section data-motion-reveal="fade-up">…</section>
<h1 class="motion-fade-up" style="--motion-delay: 0.1s">Hello</h1>
```

`revealOnScroll(targets, { effect, once, threshold, rootMargin, stagger })` 使用 IntersectionObserver，没有GSAP。一起进入的元素得到a `--motion-delay` 错开。 `fadeIn(targets, { effect, distance, duration, stagger })` 运行Web动画入口 `fill: backwards`，所以没有 之后徘徊。

公用事业: `.motion-link`， `.motion-lift`， `.motion-press`， `.motion-fade-up`， `.motion-fade-in`， `.motion-scale-in`， `.motion-spin`， `.motion-pulse`． 所有的 它们都是可选类，不会触及没有请求它的元素。

## GSAP层

```ts
const intro = createTimeline((tl, scope) => {
  tl.from(scope.select('h1'), { autoAlpha: 0, y: 28 })
    .from(scope.select('.lede'), { autoAlpha: 0, y: 18, duration: 0.5 }, '-=0.4')
}, { context: motion })

await intro.finished          // 'finished' | 'cancelled'
```

-   `createTimeline(build, { context, vars, scrollTrigger })`：青少年是 在毁灭时恢复。一个 `scrollTrigger` 在减速运动下下降。
-   `createStagger(targets, { effect, stagger, onScroll })`收藏，收藏，收藏 文档顺序。
-   `createScrollReveal(targets, { effect, start, repeat })`：批次 一起进入。
-   `createParallax(targets, { speed, axis })`：线性，擦洗，测量一次。 保持 `speed` 小。
-   `createHorizontalScroll(section, track, { minWidth })`：针在 `minWidth` 和更广泛;较窄的屏幕保持了简单的布局。选择屏幕外的项目可将其滚动到视图中。
-   `splitText(element, { by, mask, onResplit })`：单词、字符或行。它 保持内联标记，通过处理任何脚本 `Intl.Segmenter`，阅读全文 文字到屏幕阅读器，并在宽度改变时重新拆分行。 `revert()` 恢复原节点。

ScrollTrigger在第一次使用时注册，而不是在导入时注册。

## 平滑滚动

```ts
const scroll = createSmoothScroll({ driver: gsapScrollDriver(), lenis: { lerp: 0.1 } })
scroll.scrollTo('#pricing', { offset: -64 })
scroll.stop() // e.g. while a modal is open
```

每个卷轴有一个服务。第二个呼叫相同的卷轴 共享实例和最后一个实例 `destroy()` 把它拆了，所以是组件 每个人都可以安全地要求它。与 `gsapScrollDriver()`Lenis在GSAP上运行 并保持ScrollTrigger同步：一个帧循环，而不是两个。没有一个 驱动程序，Lenis运行自己的循环。更喜欢原生滚动应用程序，长表单 以及内容丰富的页面。

## 预设

-   `heroEntrance({ label, heading, body, actions, media })`：阅读顺序； 重叠成一个入口。 `actions` 孩子蹒跚。
-   `revealText(target, { by: 'words' | 'lines' | 'chars', onScroll })`:行 揭示从面具后面升起，并在调整大小的地方重建。
-   `revealImage(frames, { scale })`：画面向上打开 从一个轻微的变焦。只有剪辑路径和变换改变。
-   `followState(target, { attribute, states })`：动画到 组件报告(例如： `data-state` 从 `@lovince/ui-vue`)，所以菜单可以 门关着的时候，不要往外看。
-   `createPageTransition({ leave, enter, focus })`： `await leave(old)`交换, `enter(next)`． 每次通话结束，新的通话取消最后一个，然后集中注意力 移动到新的页面。

## 框架

**香草。** 在DOM存在后创建上下文，并调用 `destroy()` 当视图被移除时。

**Vue / next。**

```vue
<script setup lang="ts">
import { useMotion, useMotionEffect, vReveal } from '@lovince/motion/vue'
import { createSmoothScroll } from '@lovince/motion/scroll'
import { gsapScrollDriver } from '@lovince/motion/gsap'
import { heroEntrance } from '@lovince/motion/presets'

const root = ref<HTMLElement | null>(null)
useMotion((motion) => heroEntrance({ heading: 'h1', body: 'p' }, { context: motion }), { root })
const scroll = useMotionEffect(() => createSmoothScroll({ driver: gsapScrollDriver() }))
</script>

<template>
  <section ref="root"><h1>Hello</h1><p v-reveal="'fade-up'">…</p></section>
</template>
```

一切从mount开始，在unmount时恢复。期间没有任何运行 服务器呈现。

**反应。**

```tsx
useLayoutEffect(() => {
  const motion = createMotion({ root: ref.current })
  heroEntrance({ heading: 'h1' }, { context: motion })
  return motion.destroy // Strict Mode's double run is safe: destroy reverts fully
}, [])
```

**苗条的。**

```svelte
<script>
  import { onMount } from 'svelte'
  import { createMotion, revealOnScroll } from '@lovince/motion'
  let root
  onMount(() => {
    const motion = createMotion({ root })
    revealOnScroll('[data-reveal]', { context: motion })
    return motion.destroy
  })
</script>
```

## 表演

-   只有 `transform`， `opacity` 而且，对于揭示， `clip-path` 动画。
-   视差和固定测量通过ScrollTrigger的刷新，从来没有 滚动事件。只有当宽度改变时，才重新测量线分割。
-   没有永久的 `will-change`． GSAP每间隔设置一次。
-   一个时钟：当两个人都在场时，莱尼斯就会使用GSAP的计时器。
-   主条目小于6kb gzip，没有引擎。GSAP和Lenis只能加载 通过他们的条目。
-   每个原语小于1.5 KB gzip；他们都有 `initMotion` 保持在6 KB以下。

## 测试

-   **泄漏:** 后 `destroy()`， `ScrollTrigger.getAll().length` 回到原来的位置。
-   **减少运动:** `matchMedia` 是嘲笑;内容显示，没有分裂，循环保持不变，首选项更改重新运行。
-   **令牌同步:** CSS镜像会解析出与TypeScript令牌相同的值。

## 版本控制

Semver。更改令牌值是一个 **主要的** 改变，因为它改变了每个网站的感觉。新的标记、原语和属性是次要的。修复是补丁。在1.0之前，小版本可能会改变行为，发行说明也会这样说。

## 贡献

-   [ ] 使用令牌，而不是原始的持续时间、时间或距离
-   [ ] 动画转换和不透明度（或说明为什么不）
-   [ ] 返回使内容可见的清理操作
-   [ ] 处理减少的运动，并有一个测试
-   [ ] 有泄漏测试
-   [ ] 没有目标时从不扔
-   [ ] 保持在它的规模预算之内

## 故障排除

-   **内容保持隐藏：** 这个 `js` 类设置，但脚本失败。加载动作脚本之前，任何可能抛出，或放弃类。
-   **在错误的地方引发火灾：** 有什么东西在装载后改变了高度。呼叫 `motion.refresh()`； `createScroll` 一旦字体加载就会刷新。
-   **平滑滚动与内部滚动对抗：** 添加 `data-lenis-prevent` 到它。
-   **什么都不能动:** 检查操作系统的运动减少设置。这是预期的行为。
-   **Marquee仍然是：** 轨道还没有宽度，或者速度为0。给它内容 `data-marquee-track`.

## 许可证

麻省理工学院

## 常见问题

### @lovince/motion 是什么？

与框架无关的运动系统：原生CSS显示，管理GSAP时间线和滚动场景，以及Lenis平滑滚动-强制清理和减少运动处理。

### 如何安装 @lovince/motion？

在项目目录运行 npm install @lovince/motion，或使用 pnpm add / yarn add / bun add。

### @lovince/motion 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @lovince/motion 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @lovince/motion 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=18。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @lovince/motion 可以商用吗？

@lovince/motion 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @lovince/motion 还在维护吗？

最新版本 0\.4\.0 发布于 2026/11/02（1 天前）；至今共发布 5 个版本，其中过去 12 个月发布了 5 个；首次发布于 2026/10/16。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

