返回效果库

磁吸按钮

Magnetic Button

指针靠近时,按钮会轻轻倾向你

完整版:适合从零新建组件,规格、契约、验收全都在。

实时预览

指针交互
把指针移进这片区域触屏设备:直接在演示区域操作检测到「减少动态效果」,动效已降级

参数

边调边看,提示词同步更新。

手感曲线
由当前刚度与阻尼实时计算。
超调
8.0%
稳定
0.36s

角色

你是一名资深前端工程师,工作在现有的项目中,技术栈是 React 19 + TypeScript + Tailwind CSS v4 + Motion v14(motion 包)。只实现下面这一个微交互:不要加额外功能,不要顺手重构周围代码。

任务

实现磁吸按钮:指针进入按钮中心 140px 的磁场范围后,按钮朝指针方向被拉动,最大偏移 16px;指针离开后由弹簧带回原位。按钮内文字以 35% 的比例跟随,形成轻微视差。

文件契约

- 只新建一个文件:components/effects/MagneticButton.tsx。

- 依赖:项目已安装 motion@14,从 motion/react 引入。不要新增任何其他包。

- 不要修改 package.json、全局 CSS、Tailwind 配置或任何其他已有文件。

实现要点

- 文件顶部加 "use client"。

- 用 useMotionValue + animate()(来自 motion/react)驱动 x / y 与 scale;用 useReducedMotion() 判断降级。

- 导出 MagneticButton,props 为 radius、pull、stiffness、damping、scale、glow、children、className。

- 布局用 Tailwind;精确数值用任意值(如 h-[56px] rounded-full);光晕层用 useMotionTemplate 生成 radial-gradient,CSS 变量 --mx / --my 跟随指针。

规格 —— 精确数值,不许四舍五入或“优化”

- 磁场半径:以按钮中心为圆心 140px。

- 吸附强度:strength = (1 − min(距离 / 140, 1)) ^ 1.6;偏移 = 方向 × strength × 16px。

- 弹簧,跟随与回弹共用:刚度 260、阻尼 20、质量 1。回弹必须由同一个弹簧驱动,不允许用 CSS transition 或缓动曲线替代。

- 悬停缩放:1.03(弹簧:刚度 300、阻尼 20、质量 1)。

- 文字视差:内部文字的偏移 = 按钮偏移 × 0.35,由同一个弹簧驱动。

- 光晕:按钮内叠加 220px 的径向渐变 rgba(201, 244, 90, 0.34),圆心跟随指针;opacity 在指针进入时弹到 1,离开时弹回 0。

- 按钮视觉:高 56px,左右内边距 32px,圆角 999px,背景 #F2F0E9,文字颜色 #0B0C0E,字号 16px,字重 500,字距 -0.01em,光标 pointer。

- 磁场容器:固定 280px × 280px,按钮在其中居中;按钮移动时容器尺寸不变。

- 只允许动画 transform 与 opacity。

行为

- 用 Pointer Events 绑定在磁场容器上(不要用 mouse 事件);在 pointerenter 和每次 pointermove 交互开始时读取按钮 rect——父容器可能滚动。

- pointerleave:立刻用弹簧回到偏移 0,按钮绝不允许停在偏移状态。

- event.pointerType === "touch" 时不吸附;按下缩放 0.98,抬起 / 取消时回 1。

- 容器不能吞掉点击:点击按钮仍然触发它的 onClick,按钮保留 type="button"。

- 键盘:按钮可聚焦;focus-visible 显示 2px #C9F45A、外扩 3px 的描边;聚焦时不产生位移。

- prefers-reduced-motion: reduce 时禁用吸附、缩放与光晕动画,但保留 hover / focus 样式。

- 卸载时清理动画与监听;不要在 document/window 上绑监听;不要每帧触发状态更新。

禁止

- 不要用动画改变 width / height / top / left / margin / box-shadow。

- 弹簧驱动 transform 的同时,不要再给它加 CSS transition。

- 不要留 TODO、不要写 // 其余代码、不要用 any、不要 console.log、不要留注释掉的实验代码。

- 不要把组件包进页面、布局、演示壳或 Provider。

- 不要改动全局 CSS、Tailwind 配置或上面文件之外的任何文件。

- 除 Motion 外不要引入任何动画库,更不要用 GSAP。

验收 —— 回答前逐项自查

1. 指针靠近时按钮朝指针方向吸附,约 0.5s 内稳定,无抖动。

2. 离开磁场后偏移精确回到 0,缩放回到 1。

3. 点击、键盘 Enter/Space、焦点环都仍然可用。

4. DevTools 里只有 transform 与 opacity 变化,零布局位移。

5. prefers-reduced-motion 下不吸附、不缩放,但按钮仍可用。

6. 没有 console 报错或警告;卸载后所有监听与动画都被清理。

7. 渲染出的数值与规格完全一致:磁场、吸附、刚度、阻尼、缩放。

输出

- 用一个代码块输出完整文件内容,不要 diff、不要省略号,代码块之前不要解释。

- 如果缺少必要依赖,先停下来问我,不要擅自安装。

- 代码块之后最后输出一行:self-check: 1 ✓ · 2 ✓ · 3 ✓ · 4 ✓ · 5 ✓ · 6 ✓ · 7 ✓,未通过的项用 ✗ 并附一句原因。

与演示同源生成2,424 字符 · 673 约 tokens · 58 行
技术规格

在按钮周围建立一个看不见的磁场:指针进入范围内,按钮被朝指针方向拉动,离开后由弹簧带回原位;按钮内文字以 35% 的比例跟随,形成轻微视差。

磁场半径
140 px
吸附距离
16 px
弹簧刚度
260
阻尼
20
悬停缩放
1.03 ×
指针光晕
开启
磁场容器
280 × 280 px
质量
1
文字视差
35%
吸附衰减
^1.6
光晕
220px · rgba(201, 244, 90, 0.34)
按钮
56px · 999px
验收清单

提示词要求 AI 在输出末尾逐条自检。

  1. 指针靠近时按钮朝指针方向吸附,约 0.5s 内稳定,无抖动。
  2. 离开磁场后偏移精确回到 0,缩放回到 1。
  3. 点击、键盘 Enter/Space、焦点环都仍然可用。
  4. DevTools 里只有 transform 与 opacity 变化,零布局位移。
  5. prefers-reduced-motion 下不吸附、不缩放,但按钮仍可用。
  6. 没有 console 报错或警告;卸载后所有监听与动画都被清理。
  7. 渲染出的数值与规格完全一致:磁场、吸附、刚度、阻尼、缩放。

待回归验证提示词与演示已同源生成;尚未在真实 Cursor / Claude Code 项目中做回归。

提示词如何还原这个效果

不是把效果描写一遍,而是把效果写成一份可执行的工程需求。

数字规格
每个像素、每个时间参数都来自同一个参数源——滑块改什么,提示词就写什么。
文件契约
只允许新建指定文件、指定依赖白名单,避免它顺手改你的全局样式或装一堆包。
验收清单
提示词末尾强制 AI 自检布局位移、键盘可用、reduced-motion 等 7 项。
完整版 / 中文

React + Tailwind + Motion