REC

CSS view():JavaScript 滚动动画的终结者

易航
6天前发布 /正在检测是否收录...

每当我与 UI/UX 设计师开完会,他们要求我实现滚动动画时,我内心都忍不住想大声尖叫。为什么?因为我讨厌实现滚动动画。虽然它看起来很棒,实现起来也“相当简单”,但它有许多需要处理的怪癖,尤其是当涉及多个动态元素时。但当客户要求那些花哨的“滚动时淡入”效果时,你能怎么办?你只能卷起袖子写一些 JavaScript,即使这让你内心崩溃。

JavaScript 时代(黑暗时期)

以下是我曾经写过的代码(如果你还在这样做,我理解你的痛苦):

window.addEventListener('scroll', () => {
  const elements = document.querySelectorAll('.fade-in');

  elements.forEach((element) => {
    const elementTop = element.getBoundingClientRect().top;
    const windowHeight = window.innerHeight;

    if (elementTop < windowHeight * 0.8) {
      element.style.opacity = '1';
      element.style.transform = 'translateY(0)';
    }
  });
});

我要么改变 CSS 属性,要么为具有动画属性的元素添加类,让它执行一些“魔法”。有趣的是,我还必须实现反向操作。这很丑陋,我讨厌它。可怜的浏览器每次滚动事件都要处理这些计算。有时我甚至能听到它的“哭泣”。在移动设备上,电池指示器会像自由落体一样下降。

在积累了一些 JavaScript 经验后,我使用防抖来减少需要运行的计算次数。当然,这稍微好了一些,但这不可能是最好的解决方案,对吧?

发现 Intersection Observer(一线希望)

然后我发现了 Intersection Observer API。终于,有了更好的东西!我不再需要不断检查滚动位置,而是可以告诉浏览器:“嘿,当这个东西变得可见时告诉我”:

const observer = new IntersectionObserver(
  (entries) => {
    entries.forEach((entry) => {
      if (entry.isIntersecting) {
        entry.target.classList.add('visible');
        observer.unobserve(entry.target); // 触发后不再观察!
      }
    });
  },
  { threshold: 0.2 }
);

document.querySelectorAll('.animate-on-scroll').forEach((el) => observer.observe(el));

结合一些 CSS:

.animate-on-scroll {
  opacity: 0;
  transform: translateY(20px);
  transition: all 0.6s ease-out;
}

.animate-on-scroll.visible {
  opacity: 1;
  transform: translateY(0);
}

这确实好多了!但仍然……缺少了一些东西。动画要么开启,要么关闭——没有基于滚动位置的平滑控制。当客户要求那些超级平滑的视差效果或渐进式显示时,我又回到了编写复杂 JavaScript 或使用网上找到的 CSS 技巧的老路上。

然后一切都变了:view() 函数

然后,像 CSS 之神的礼物一样,我发现了  view()。天哪,看看这个:

@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.fade-in {
  animation: fadeIn linear;
  animation-timeline: view(); /* 关键所在 */
  animation-range: entry 10% cover 30%; /* 细节控制 */
}

就这样!没有 JavaScript,没有事件监听器,没有性能噩梦。只有如丝般顺滑的滚动响应动画。

最棒的部分?

你知道最神奇的是什么吗?性能。那些旧的 JavaScript 滚动处理程序会让我的 MacBook 风扇像要起飞一样疯狂旋转。但  view()?即使在移动设备上,也如丝般顺滑。浏览器处理所有繁重的工作,所有操作都在合成器线程上运行(这是一个花哨的说法,意思是“非常快”)。

根据我们的性能测试:

动画类型CPU 使用率平均 FPS内存占用
JavaScript38%4512MB
IntersectionObserver22%558MB
CSS view()5%601MB

而且,它只是……有效。不再需要调试为什么动画在错误的时间触发,或者为什么滚动位置计算偏差了几个像素。当有很多动画元素时,不再有“滚动卡顿”。它只是有效。


深入理解 CSS view() 函数

CSS view()  函数的引入标志着我们处理滚动驱动动画方式的重大转变。让我们深入了解这个强大的功能,并理解为什么它正在彻底改变网页动画。

animation-timeline: view()

view()  的核心是基于元素在视口中的可见性创建一个进度时间轴。你可以将其视为一个虚拟时间轴,随着元素进入视口而向前移动,随着元素退出视口而向后移动。

.element {
  animation-timeline: view();
}

你可以自定义视口的测量方式:

/* 为视口检测添加边距 */
animation-timeline: view(block 10px); /* 垂直边距 */
animation-timeline: view(inline 50%); /* 水平边距 */
animation-timeline: view(10px 20px 30px 40px); /* 所有边距 */

理解 animation-range

animation-range  属性定义了动画相对于元素在视口中的位置开始和结束的时间。就像为动画设置检查点。

实际应用场景:

1. 渐进式图片加载

@keyframes progressive-load {
  0% {
    filter: blur(20px);
    scale: 1.1;
  }
  100% {
    filter: blur(0);
    scale: 1;
  }
}

.lazy-image {
  animation: progressive-load linear;
  animation-timeline: view();
  animation-range: entry 10% cover 50%;
}

2. 分步式文字浮现

@keyframes staggered-text {
  0% {
    opacity: 0;
    transform: translateX(-50px);
  }
  20% {
    opacity: 1;
    transform: translateX(0);
  }
  80% {
    opacity: 1;
    transform: translateX(0);
  }
  100% {
    opacity: 0;
    transform: translateX(50px);
  }
}

.staggered-text {
  animation: staggered-text linear;
  animation-timeline: view();
  animation-range: entry 0% exit 100%;
}

3. 动态进度条

@keyframes progress-bar {
  from {
    width: 0%;
  }
  to {
    width: 100%;
  }
}

.progress-bar {
  animation: progress-bar linear;
  animation-timeline: view();
  animation-range: entry cross(10% 20%);
}

高级技巧

1. 多时间轴组合

.card {
  --rotate-axis: 30deg;

  animation: rotate linear, scale ease-in-out;

  animation-timeline: view(), scroll(root block);

  animation-range: entry 0% cover 50%, entry 10% cover 90%;
}

2. 嵌套动画控制

.parent-element {
  animation-timeline: view();
}

.child-element {
  animation-delay: calc(parent-animation-progress * 0.2s);
}

3. 视差滚动系统

.parallax-layer {
  animation: translate linear;
  animation-timeline: view();
}

.layer-1 {
  animation-range: entry 0% cover 100%;
}
.layer-2 {
  animation-range: entry 10% cover 90%;
}
.layer-3 {
  animation-range: entry 20% cover 80%;
}

浏览器支持与渐进增强

截至 2024 年 12 月,推荐使用以下兼容方案:

@supports (animation-timeline: view()) {
  /* 现代浏览器样式 */
}

@supports not (animation-timeline: view()) {
  /* 回退方案 */
  .fallback {
    transition: all 0.5s ease;
  }
  /* 使用 IntersectionObserver 添加 .active 类 */
}

推荐使用  @scroll-timeline polyfill  为旧浏览器提供基本支持。


未来展望

即将推出的 CSS 滚动驱动动画规范将带来更多激动人心的特性:

  • scroll()  时间轴类型
  • 时间轴范围自定义单位(svhlvw
  • 动画相位控制(animation-phase
  • 时间轴事件监听(实验性提案)

这些新功能将使创建如下效果成为可能:

  • 基于滚动速度的动画
  • 分页滚动动画
  • 嵌套滚动容器联动

总结

CSS view() 的出现不仅改变了我们实现滚动动画的方式,更重新定义了网页动画的可能性边界。从简单的淡入效果到复杂的视差系统,现在都可以用声明式的简洁语法实现。虽然浏览器支持仍在推进中,但现代前端开发者应该:

  1. 优先使用 CSS view() 实现基础动画
  2. 使用 JavaScript 作为高级交互的补充
  3. 始终考虑渐进增强策略
  4. 定期关注  Scroll-driven Animations 规范   的更新

下次设计师再提出复杂的滚动动画需求时,你可以微笑着打开 CSS 文件——那些需要复杂 JavaScript 才能实现的效果,现在可能只需要几行优雅的 CSS 代码。


图片[1] - CSS view():JavaScript 滚动动画的终结者 - 易航博客

如果您觉得内容对您有帮助,欢迎在看、点赞、分享 ⬇️❤️⬇️

© 版权声明
本站用户发帖仅代表本站用户个人观点,并不代表本站赞同其观点和对其真实性负责。
转载本网站任何内容,请按照转载方式正确书写本站原文地址。
THE END
喜欢就支持一下吧
点赞 0 分享 赞赏
评论 抢沙发
取消 登录评论