如何在多页面应用中为元素出现时添加向下滑动的动画?

前端开发 2026-07-10

我正在使用现代HTML和 CSS构建一个多页面应用(MPA),但不使用任何JavaScript框架。
我有一个表单,在提交时出现错误就会显示一个错误信息。
我想为错误信息添加向下滑动的动画,这样看起来就像把其余表单字段往下推移。
我正在使用跨文档视图转换来尝试实现这个效果。
问题在于,动画在开始过渡前就会显示一个与错误信息同等大小的空白区域。
我想让动画看起来像Svelte的 slide动画:

Svelte 滑动动画

但实际显示如下:

带有不需要的空白区域的动画示例

MRE的代码:

<!doctype html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>View transition MRE</title>
  </head>
  <body>
    <style>
      @view-transition {
        navigation: auto;
      }

      :root {
        interpolate-size: allow-keywords;
        color-scheme: dark light;
      }

      body {
        font-family: system-ui;
      }

      .incoming {
        view-transition-name: --incoming;
      }

      ::view-transition-image-pair(--incoming) {
        overflow-y: clip;
      }

      ::view-transition-new(--incoming) {
        animation-duration: 3s;

        @media (prefers-reduced-motion: no-preference) {
          animation-name: --slide-down-in;
        }
      }

      @keyframes --slide-down-in {
        from {
          block-size: 0;
        }

        to {
          block-size: auto;
        }
      }
    </style>
    <main>
      <h1>View transition MRE</h1>
      <p><a href="/mre/without.html">Toggle</a></p>
      <!-- Previous page does not have the .incoming element -->
      <p class="incoming">Slides in</p>
      <p>Lorem ipsum</p>
    </main>
  </body>
</html>

如何在不在动画开始前显示空白区域的前提下,让向下滑动的动画正常工作?

解决方案

你可以对 incoming 元素使用 clip-path 动画。
要让文本按你想要的方式向下移动,你需要给它一个 view-transition-name,使它从在 without.html 中的位置过渡到在 index.html 中的位置:

<!doctype html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>View transition MRE</title>
  </head>
  <body>
    <style>
      @view-transition {
        navigation: auto;
      }

      :root {
        interpolate-size: allow-keywords;
        color-scheme: dark light;
      }

      body {
        font-family: system-ui;
      }

      .incoming {
        view-transition-name: --incoming;
      }

      ::view-transition-group(--incoming) {
        animation-duration: 3s;
      }
      ::view-transition-new(--incoming) {
        animation-name: --slide-in;
      }
      ::view-transition-old(--incoming) {
        animation-name: --slide-out;
      }

      @keyframes --slide-in {
        from {
          clip-path: inset(0 0 100% 0);
        }
        to {
          clip-path: inset(0 0 0 0);
        }
      }

      @keyframes --slide-out {
        from {
          clip-path: inset(0 0 0 0);
        }
        to {
          clip-path: inset(0 0 100% 0);
        }
      }

      .text {
        view-transition-name: --text;
      } 

      ::view-transition-group(--text){
        animation-duration: 3s;
      }
    </style>
    <main>
      <h1>View transition MRE</h1>
      <p><a href="/without.html">Toggle</a></p>
      <div class="incoming"><p>Slides in</p></div>
      <p class="text">Lorem ipsum</p>
    </main>
  </body>
</html>

Stackblitz演示

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章