视差滚动效果实现

2024-08-24 11:06:51 浏览数 (1)

视差滚动是一种在网页设计和视频游戏中常见的视觉效果技术,它通过在不同速度上移动页面或屏幕上的多层图像,创造出深度感和动感。 这种效果通过前景、中景和背景以不同的速度移动来实现,使得近处的对象看起来移动得更快,而远处的对象移动得较慢。

在官网中适当的使用视差效果,可以增加视觉吸引力,提高用户的参与度,从而提升网站和品牌的形象。本文通过JavaScript、CSS多种方式并在React框架下进行了视差效果的实现,供你参考指正。

实现方式

1、background-attachment

通过配置该 CSS 属性值为fixed可以达到背景图像的位置相对于视口固定,其他元素正常滚动的效果。但该方法的视觉表现单一,没有纵深,缺少动感。

代码语言:css复制
.parallax-box {
  width: 100%;
  height: 100vh;
  background-image: url("https://picsum.photos/800");
  background-size: cover;
  background-attachment: fixed;

  display: flex;
  justify-content: center;
  align-items: center;
}

点击访问完整在线代码

2、Transform 3D

在 CSS 中使用 3D 变换效果,通过将元素划分至不同的纵深层级,在滚动时相对视口不同距离的元素,滚动所产生的位移在视觉上就会呈现越近的元素滚动速度越快,相反越远的元素滚动速度就越慢。

为方便理解,你可以想象正开车行驶在公路上,汽车向前移动,你转头看向窗外,近处的树木一闪而过,远方的群山和风景慢慢的渐行渐远,逐渐的在视野中消失,而天边的太阳却只会在很长的一段距离细微的移动。

点击访问完整在线代码

代码语言:css复制
.parallax {
  perspective: 1px; /* 设置透视效果,为3D变换创造深度感 */
  overflow-x: hidden;
  overflow-y: auto;
  height: 100vh;
}

.parallax__group {
  transform-style: preserve-3d; /* 保留子元素3D变换效果 */
  position: relative;
  height: 100vh;
}

.parallax__layer {
  position: absolute;
  inset: 0;
  display: flex;
  justify-content: center;
  align-items: center;
}

/* 背景层样式,设置为最远的层 */
.parallax__layer--back {
  transform: translateZ(-2px) scale(3);
  z-index: 1;
}

/* 中间层样式,设置为中等距离的层 */
.parallax__layer--base {
  transform: translateZ(-1px) scale(2);
  z-index: 2;
}

/* 前景层样式,设置为最近的层 */
.parallax__layer--front {
  transform: translateZ(0px);
  z-index: 3;
}

实现原理

通过设置 perspective 属性,为整个容器创建一个 3D 空间。

使用 transform-style: preserve-3d 保持子元素的 3D 变换效果。

将内容分为多个层(背景、中间、前景),使用 translateZ() 将它们放置在 3D 空间的不同深度。

对于较远的层(如背景层),使用 scale() 进行放大,以补偿由于距离产生的视觉缩小效果。

当用户滚动页面时,由于各层位于不同的 Z 轴位置,它们会以不同的速度移动,从而产生视差效果。

3、ReactScrollParallax

想得到更炫酷的滚动视差效果,纯 CSS 的实现方式就会有些吃力。

如下是在 React 中实现示例,通过监听滚动事件,封装统一的视差组件,来达到多样的动画效果。

点击访问完整在线代码

代码语言:javascript复制
const Parallax = ({ children, effects = [], speed = 1, style = {} }) => {
  // 状态hooks:用于存储动画效果的当前值
  const [transform, setTransform] = useState("");

  useEffect(() => {
    if (!Array.isArray(effects) || effects.length === 0) {
      console.warn("ParallaxElement: effects should be a non-empty array");
      return;
    }

    const handleScroll = () => {
      // 计算滚动进度
      const scrollProgress =
        (window.scrollY /
          (document.documentElement.scrollHeight - window.innerHeight)) *
        speed;

      let transformString = "";

      // 处理每个效果
      effects.forEach((effect) => {
        const { property, startValue, endValue, unit = "" } = effect;
        const value =
          startValue  
          (endValue - startValue) * Math.min(Math.max(scrollProgress, 0), 1);

        switch (property) {
          case "translateX":
          case "translateY":
            transformString  = `${property}(${value}${unit}) `;
            break;
          case "scale":
            transformString  = `scale(${value}) `;
            break;
          case "rotate":
            transformString  = `rotate(${value}${unit}) `;
            break;
          // 更多的动画效果...
          default:
            console.warn(`Unsupported effect property: ${property}`);
        }
      });

      // 更新状态
      setTransform(transformString);
    };

    window.addEventListener("scroll", handleScroll);
    // 初始化位置
    handleScroll();

    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  }, [effects, speed]);

  // 渲染带有计算样式的子元素
  return <div style={{ ...style, transform }}>{children}</div>;
};

在此基础上你可以添加缓动函数使动画效果更加平滑;以及使用requestAnimationFrame获得更高的动画性能。

requestAnimationFrame 带来的性能提升

同步浏览器渲染周期:requestAnimationFrame 会在浏览器下一次重绘之前调用指定的回调函数。这确保了动画更新与浏览器的渲染周期同步,从而产生更流畑的动画效果。提高性能:与使用 setInterval 或 setTimeout 相比,requestAnimationFrame 可以更高效地管理动画。它只在浏览器准备好进行下一次重绘时才会执行,避免了不必要的计算和重绘。优化电池使用:在不可见的标签页或最小化的窗口中,requestAnimationFrame 会自动暂停,这可以节省 CPU 周期和电池寿命。适应显示器刷新率:requestAnimationFrame 会自动适应显示器的刷新率。这意味着在 60Hz、120Hz 或其他刷新率的显示器上,动画都能保持流畑。避免丢帧:由于与浏览器的渲染周期同步,使用 requestAnimationFrame 可以减少丢帧现象,特别是在高负荷情况下。更精确的时间控制:requestAnimationFrame 提供了一个时间戳参数,允许更精确地控制动画的时间。

4、组件库方案

在当前成熟的前端生态中,想要获得精彩的视差动画效果,你可以通过现有的开源组件库来高效的完成开发。

以下是一些你可以尝试的主流组件库:

  • rellax (Lightweight, vanilla javascript parallax library)
  • react-scroll-parallax
  • framer-motion
  • ...

引用参考

MDN - background-attachment

MDN - transform-style

Pure CSS Parallax Websites

How to create parallax scrolling with CSS

视差滚动实践

原地址文

0 人点赞