通过 motion-anything 实现视差滚动的步骤

文章导读
视差滚动的核心不是某个库自带魔法,而是让不同层级的元素以不同速度移动。motion-anything 在这类需求里通常承担的是动画执行或插值部分,主动画数据仍需要由你计算。动手前先确认当前项目里 motion-anything 的安装方式:是 npm 包、局部组件还是 CDN 脚本,以及对外暴露的是 Hook、指令还是普通函数。这一步不确认,后面的写法很容易变成猜接口。
📋 目录
  1. Ⅰ 一、先确认 motion-anything 的接口形态
  2. Ⅱ 二、标记视差元素并定义速度因子
  3. Ⅲ 三、绑定滚动进度并更新 transform
  4. Ⅳ 四、清理监听与动画实例
A A

视差滚动的核心不是某个库自带魔法,而是让不同层级的元素以不同速度移动。motion-anything 在这类需求里通常承担的是动画执行或插值部分,主动画数据仍需要由你计算。动手前先确认当前项目里 motion-anything 的安装方式:是 npm 包、局部组件还是 CDN 脚本,以及对外暴露的是 Hook、指令还是普通函数。这一步不确认,后面的写法很容易变成猜接口。

利用 motion-anything 做视差滚动,通常要自己提供「滚动进度」这一数据:先标记元素,再把滚动位置换算成位移,最后调用库提供的动画方法更新。实现前先确认该库的导出名称与销毁方式;若 API 不明确,用原生 transform 也能达到同一效果,避免被库名称误导。

一、先确认 motion-anything 的接口形态

到项目 node_modules 或 GitHub 页面查看它提供的入口文件,重点确认三件事:包名和版本;可用的运动方法名称,例如 animate、scroll、progress 相关的导出;以及有没有销毁或取消实例的方法。如果文档里出现 scroll 进度、进度值、duration 这类词,说明你需要传入一个 0 到 1 的进度值。

如果它是 npm 包,可以用一行代码在控制台打印模块对象:

通过 motion-anything 实现视差滚动的步骤
import * as motionAnything from 'motion-anything';

console.log(motionAnything);

如果项目里无法这样导入,就说明它可能以其他方式注册,直接从全局变量或组件 props 查看。

二、标记视差元素并定义速度因子

给需要参与视差的元素增加 data-parallax-speed 属性,数值表示相对滚动速度。小于 1 表示比正常滚动慢,大于 1 表示更快,负数表示反向。先不要追求复杂的嵌套结构,从两层背景和一个前景开始调整。

<section class='scene'>
  <div class='layer' data-parallax-speed='0.2'>背景层</div>
  <div class='layer' data-parallax-speed='0.6'>中间层</div>
  <div class='layer' data-parallax-speed='-0.3'>反向层</div>
</section>

三、绑定滚动进度并更新 transform

在页面滚动时计算当前进度:progress = window.scrollY / (document.body.scrollHeight - window.innerHeight)。每个元素的位移量用进度乘以元素所在可视区域高度和速度因子得出。更新位置时优先使用 transform: translate3d(),并让滚动监听保持 passive。这一步是 motion-anything 与原生实现都可以共用的骨架。

通过 motion-anything 实现视差滚动的步骤
const layers = document.querySelectorAll('[data-parallax-speed]');

function updateParallax() {
  const maxScroll = document.body.scrollHeight - window.innerHeight;
  const progress = window.scrollY / maxScroll;

  layers.forEach((el) => {
    const speed = parseFloat(el.dataset.parallaxSpeed);
    const offset = progress * window.innerHeight * speed;

    // motion-anything 的动画入口写在这里,
    // 用它的进度或位移方法替换下面的原生 transform。
    el.style.transform = `translate3d(0, ${offset}px, 0)`;
  });
}

window.addEventListener('scroll', updateParallax, { passive: true });
updateParallax();

把 motion-anything 对应的调用放在注释位置。如果它接收 0 到 1 的进度,就把 progress 传进去;如果接收具体数值,就把 offset 传进去。无论哪种,页面滚动时都要触发一次读取。

四、清理监听与动画实例

使用 addEventListener 注册的匿名函数无法移除,第一次调试时就能看到这个问题。把 updateParallax 同名函数传给 removeEventListener,并在页面卸载时执行。如果 motion-anything 的调用返回了实例,例如 stop、cancel、dispose 这类方法,也要在移除监听时一并调用。

通过 motion-anything 实现视差滚动的步骤
function cleanup() {
  window.removeEventListener('scroll', updateParallax);
  // 如果 motion-anything 返回了动画实例,在这里调用它的销毁方法
}

最后,将页面滚动到不同位置做检查:背景层是否比正常内容移动慢,前景层是否更快,负速度层是否反向移动。如果没有任何反应,先确认 data-parallax-speed 属性是否拼写一致,再确认 motion-anything 的实际调用是否真的被触发。对于生产环境,可以用 requestAnimationFrame 对滚动回调做节流,减少重复计算;但这一步要结合页面复杂度来定,不能只看滚动事件本身。

从本质上说,视差滚动只是滚动监听与 transform 计算的组合;motion-anything 的价值在于提供更顺滑的插值或动画控制。如果某个版本连基本导出都不明确,完全可以用原生代码先把效果跑通,再决定是否保留这个库。