滚动时缩小标题

Web 功能: 滚动条驱动的动画

滚动时缩小的标题是一种常见的界面模式,当用户向下滚动时,页面顶部的固定标题元素会平滑过渡到较小的尺寸。这种效果通常用于最大限度地扩大主要内容的屏幕空间,同时让用户能够访问重要的导航或品牌元素。借助 CSS 滚动驱动的动画,您可以通过将动画关联到文档的滚动位置,以声明式且高效的方式实现此效果。

如何实现

以下是创建滚动时缩小的标题的方法:

  1. 创建固定页眉:首先创建一个固定在页面顶部且具有预定义高度的页眉元素。

    <header>HEADER</header>
    
    header {
      position: fixed;
      height: 200px;
      top: 0;
      left: 0;
      right: 0;
    }
    
  2. 定义缩小动画:创建一个可更改标题高度的 CSS 动画。

    @keyframes shrink {
      to {
        height: 50px;
      }
    }
    
  3. 应用动画和滚动时间轴:将动画附加到标题,并使用 scroll() 函数将其与文档的滚动位置相关联。

    header {
      animation: shrink auto linear both;
      animation-timeline: scroll(block root);
    }
    
  4. 设置 animation-range:使用 animation-range 属性指定动画应发生的滚动距离。例如,如需在滚动前 150 像素时缩小标题,您可以使用 animation-range: 0px 150px;。

    header {
      animation-range: 0px 150px;
    }
    

提示:为防止页眉遮挡其后的内容,请向 body(或主要内容容器)添加一个 padding-top,其值等于页眉的初始高度。

提示:为确保页面内容与缩小的标题同步滚动,请将 animation-range-end 设置为起始大小和结束大小之间的差值。这样可以确保动画在标题达到最终尺寸时精确完成。在此演示中,标题从 200px 缩小到 50px,因此 animation-range-end 设置为 150px。

示例代码

@keyframes shrink {
  to {
    height: 50px;
  }
}

header {
  animation: shrink auto linear both;
  animation-timeline: scroll(block root);
  animation-range: 0px 150px;
}

最佳做法

使用滚动驱动动画时,请务必遵循一些最佳实践,以确保提供流畅且无障碍的体验:

  • 务必添加功能检测:并非所有浏览器都支持滚动驱动的动画。使用 @supports ((animation-timeline: scroll()) and (animation-range: 0% 100%)) 检查是否支持,并为不支持的浏览器提供后备方案。
    • 此处必须包含 (animation-range: 0% 100%) 检查,以滤除仅提供部分支持的浏览器。
    • 请勿将 scroll-timeline-polyfill 软件包用于回退策略,因为它功能不完整,并且存在许多已知问题。
    • 如果动画仅被视为装饰性元素,请选择渐进增强,不要提供后备。
  • 务必尊重用户偏好:有些用户更喜欢在网页上看到较少的动画。使用 prefers-reduced-motion 媒体查询为这些用户停用或减少动画。
  • 务必尝试仅为高性能 CSS 属性添加动画效果:为了获得最流畅的动画效果,请坚持为可由浏览器的合成器线程处理的属性(例如 transform 和 opacity)添加动画效果。为其他属性(例如 width 或 height)添加动画效果可能会导致性能问题。
  • 务必使用正确的声明顺序:使用 animation 简写属性时,请在它之后声明 animation-timeline 和 animation-range,以防止简写属性重置时间轴。

使用 scroll() 函数创建滚动驱动的动画时:

  • 可选:明确指定滚动条。如果目标不是最近的祖先滚动条,请使用 scroll(root) 或 scroll(self) 明确指定要使用的滚动条。
    • 如果 root、nearest 或 self 不够用,请使用命名的 scroll-timeline。
  • 可选:明确指定要跟踪的轴。如果未定位到默认的 block 轴(例如在水平滚动条中),请使用 scroll(block) 或 scroll(inline) 明确指定要跟踪的轴。

具体到此应用场景:

  • 在 animation-range 中使用百分比时,您添加动画效果的元素不得是 position: static 或 position: relative。
    • 这是因为这些元素被视为“流内”元素。在滚动时缩小这些元素会缩短总滚动距离,从而影响滚动到 10% 等位置的计算值。

浏览器支持和后备策略

因此,通常需要采用后备策略。

对于不支持滚动驱动动画的浏览器,您可以使用后备方案来重现视觉效果。后备方案通常使用滚动监听器(用于 ScrollTimeline 效果)或 IntersectionObserver API(用于 ViewTimeline 效果)构建。

在内置支持滚动驱动动画的浏览器中,请始终使用原生 CSS 实现,因为这些实现的性能更高。

请注意,并非所有效果都可以通过后备方法重现。

具体而言,对于此用例,以下脚本会为不支持滚动驱动动画的浏览器应用回退。它使用滚动监听器来跟踪文档在 150px 距离内的滚动位置,并相应地更新标题的高度。

// Fallback for browsers that don't support scroll-driven animations
if (!CSS.supports('(animation-timeline: scroll()) and (animation-range: 0% 100%)')) {
  const header = document.querySelector('header');

  const initialHeight = 200;
  const finalHeight = 50;
  const scrollDistance = 150;

  window.addEventListener('scroll', () => {
    const scrollY = window.scrollY;
    const scrollPercent = Math.min(1, scrollY / scrollDistance);
    const newHeight = initialHeight - (initialHeight - finalHeight) * scrollPercent;

    header.style.height = `${newHeight}px`;
  });
}