关于 add-mouse-driven-orbit
这项设计技能用于为 3D 场景添加克制的指针驱动相机与物体运动,适合希望营造电影感纵深、但不需要直接操控物体的创作者。
主要功能
- 01实现跨帧率的指针缓动
- 02分离相机与物体旋转
- 03应用按深度缩放的 CSS 视差
- 04适配不同视口并保持构图
- 05支持减少动态效果与键盘控制
适用场景
- 01为 3D 场景添加轻微指针响应
- 02相机移动时保持文字与卡片位置
- 03通过键盘控制环绕状态
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ mengto/ skills --skill add-mouse-driven-orbit安装后在 Claude Code 中输入/add-mouse-driven-orbit确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
agents/openai.yamldemo/index.htmlPROMPT.md详细介绍
添加鼠标驱动的环绕效果
将一个经过阻尼处理的指针目标转化为浅弧形相机运动和幅度更小的物体旋转。用户需要直接检查产品时,使用 threejs 和 OrbitControls;需要拖动、缩放、遮挡或游戏玩法相机时,使用 build-game-camera-controls。此技能用于被动营造电影感景深,而非直接操控。
内容摘自 inner-green-3d.html:其中的程序化苔藓根系需要朝向指针转动,同时不能偏离标题、卡片和固定剪影地标。
记录意图,而非布局
在指针处理程序中,只写入归一化坐标:
function recordPointer(event) {
if (event.pointerType === "touch") return;
target.x = (event.clientX / viewport.width) * 2 - 1;
target.y = (event.clientY / viewport.height) * 2 - 1;
}
使用 ResizeObserver 缓存交互区域。不要在每次 pointermove 时为每个图层调用 getBoundingClientRect();同步布局会让轻量效果产生帧耗时峰值。
在 pointerleave 时,将目标设回 0, 0。对于粗略指针,保持设计好的中心姿态。被动环绕效果不应要求用户拖动触屏才能查看内容。
在帧循环中添加阻尼
源代码使用每个 60 Hz 帧 0.055 的系数。为不同刷新率保留相同的感觉:
const alpha = 1 - Math.pow(1 - 0.055, dt * 60);
smooth.x += (target.x - smooth.x) * alpha;
smooth.y += (target.y - smooth.y) * alpha;
将 dt 限制为 1/30 s,并在恢复运行后重置时间基准。每帧使用固定的 lerp,在 30 Hz 下会显得迟钝,在 120 Hz 下则会显得跳动;不加限制的时间增量会在应用转入后台后引发跳变。
四舍五入后的值稳定后,停止写入样式或 uniform。三位小数的精度远高于最终移动距离所需的一个像素:
const x = Math.round(smooth.x * 1000) / 1000;
const y = Math.round(smooth.y * 1000) / 1000;
if (x !== lastX || y !== lastY) publish(x, y);
拆分运动
使用方向相反、幅度不同的图层,让场景产生枢转效果,而非整体平移:
camera.position.x = -smooth.x * 26;
camera.position.y = smooth.y * 16;
camera.lookAt(camera.position.x * 0.42, camera.position.y * 0.42, 0);
nearGroup.rotation.y = smooth.x * 0.055;
nearGroup.rotation.x = smooth.y * 0.026;
farGroup.rotation.y = smooth.x * 0.030;
以下是以舞台像素为世界单位构图时采用的最终值:
| 图层 | 水平 | 垂直 | 原因 |
|---|---|---|---|
| 相机平移 | -26 | +16 | 形成浅弧形运动 |
| 相机注视点补偿 | 42% | 42% | 让主体保持在固定构图附近 |
| 近处物体偏航 | 0.055 rad | — | 展现表面景深,而不露出侧面 |
| 近处物体俯仰 | — | 0.026 rad | 避免顶面变平 |
| 远处物体偏航 | 0.030 rad | — | 拉开平面层次,避免与前景同步 |
不要让所有物体以相同幅度旋转。相同幅度的运动看起来就像平面海报跟着光标移动。平移相机时,不要让它始终朝向固定原点;否则主体会明显偏离布局。
使用同一组 smooth 值应用 CSS 视差,但给文本和控件设置小于 3D 造型的景深系数。保留 transform 供视差使用;对于无关的显示效果,使用裁剪、透明度或子包装器,避免动画互相覆盖。
保持不同尺寸下的构图
先在每个断点下建立中心姿态。使用 ResizeObserver 重新计算相机宽高比以及任何舞台到世界的缩放值,防止根元素尺寸为零,然后再应用环绕偏移。检查指针能到达的两个极端角落:确保文案不冲突、卡片不被裁切,也不会露出空白边缘。
在演示或配置器中使用可通过键盘操作的 X 轴和 Y 轴范围控件。它们能证明环绕效果可参数化,也能让非指针用户访问相同的设计状态。保持焦点可见,并播报运动模式的变化。
尊重运动偏好和生命周期
- 在
prefers-reduced-motion: reduce下,将场景渲染为设计好的四分之三视角静止画面,位置约为x=.28, y=-.12;保持范围控件可用,选中的静止画面无需插值即可重绘。 - 页面隐藏或场景移出视口时暂停,并在恢复时重置时间基准。
- 将 DPR 上限设为 2,并将
dt限制为 1/30 s。 - 除非 3D 物体承载信息,否则将画布设为装饰性内容。使用语义化 HTML 保留所有标签和控件。
- 清理时,取消帧、断开观察器、移除指针和媒体监听器,并释放 Three.js 资源。
坦诚说明成本
轨道数学计算的开销微乎其微。昂贵的是场景本身的重绘,以及叠加在其上的任何 DOM 样式失效。共享一个帧循环,对稳定后的值进行取整,避免在指针处理程序中读取布局,并在移除交互前分析底层绘制性能。移动摄像机并不会让密集场景的开销降低。
验证
- 在 1440×900 下,将中心和四角位置与源文件进行比较。
- 确认摄像机和对象以不同的幅度朝相反方向移动。
- 快速移过视口;场景必须沿路径平滑过渡,而不是突然跳变。
- 离开视口;确认场景平滑回到中心。
- 测试 390×844 和粗略指针/触摸输入;确保中心构图完整。
- 使用 Tab 键切换 X/Y 控件,用方向键更改它们,并确认焦点清晰可见。
- 测试
?reduced=1:保持稳定的设计静态画面,不进行自主缓动,但保留实时控件。 - 隐藏后重新显示,并滚动离开后返回;确认没有跳变,且只有一个帧循环。
- 确认两种尺寸下的控制台均无错误。
使用 demo/index.html 作为实际验证依据,并使用 demo/PROMPT.md 重新创建或改编。