CSS @starting-style与过渡动画零延迟初始化实战

元素首次出现时的过渡动画问题

CSS Transitions有一个长期痛点:元素从 display:none 切换到可见状态时,过渡动画无法生效。浏览器在元素进入文档流时没有”上一个状态”可供插值,导致opacity从0到1、transform从scale(0)到scale(1)等入场动画必须依赖@keyframes或JavaScript定时器hack。

传统解决方案:

/* 传统方案:两帧requestAnimationFrame */
const el = document.querySelector(".modal");
el.style.display = "block";
requestAnimationFrame(() => {
  requestAnimationFrame(() => {
    el.classList.add("visible");
  });
});

双重RAF不可靠,浏览器可能合并帧导致第一帧被跳过。CSS @starting-style提案正是为解决这个问题而生。

@starting-style语法与工作原理

@starting-style 规则为元素定义”进入文档时的初始样式”,浏览器将其作为过渡的起始状态进行插值。Chrome 117+和Safari 17.4+已支持该特性:

.card {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 0.3s ease, transform 0.3s ease;
}

.card.visible {
  opacity: 1;
  transform: translateY(0);
}

@starting-style {
  .card.visible {
    opacity: 0;
    transform: translateY(20px);
  }
}

工作流程:当 .card 获得 .visible 类时,浏览器检测到transition属性有变化,查找 @starting-style.card.visible 的样式作为起始值,计算从 opacity:0; translateY(20px)opacity:1; translateY(0) 的插值动画。无需JavaScript介入。

display:none切换场景的完整方案

配合 transition-behavior: allow-discrete@starting-style,实现display:none到可见的完整过渡:

.overlay {
  display: none;
  opacity: 0;
  transition: opacity 0.3s ease, display 0.3s ease allow-discrete;
}

.overlay.open {
  display: block;
  opacity: 1;
}

@starting-style {
  .overlay.open {
    opacity: 0;
  }
}

allow-discrete 允许discrete属性(display、visibility)参与过渡。display从none到block的变化时机被延迟到过渡完成后,实现”淡入后显示、隐藏后消失”的完整闭环。

反向退出动画同样生效——移除 .open 类时,opacity从1过渡到0,display在过渡结束后才切换回none。这是以往需要复杂JS才能实现的效果。

Popover和Dialog的原生过渡方案

HTML popover属性和dialog元素天生支持显示/隐藏切换,配合@starting-style可实现零JS的弹窗过渡:

[popover] {
  opacity: 0;
  transform: scale(0.9);
  transition: opacity 0.2s ease, transform 0.2s ease,
              display 0.2s ease allow-discrete,
              overlay 0.2s ease allow-discrete;
  inset: 0;
  margin: auto;
}

[popover]:popover-open {
  opacity: 1;
  transform: scale(1);
}

@starting-style {
  [popover]:popover-open {
    opacity: 0;
    transform: scale(0.9);
  }
}

overlay 属性控制顶层覆盖层,配合 allow-discrete 可实现backdrop的淡入淡出。这种方式比 ::backdrop 伪元素动画更灵活,且无需额外JavaScript。

Vue3/React组件中的集成实践

Vue3中使用@starting-style替代Transition组件:

<template>
  <div v-if="show" class="panel" :class="{ active: animated }">
    <slot />
  </div>
</template>

<script setup>
import { ref, watch, nextTick } from "vue";

const show = ref(false);
const animated = ref(false);

watch(show, async (val) => {
  if (val) {
    await nextTick();
    animated.value = true;
  } else {
    animated.value = false;
  }
});
</script>

更简洁的做法是直接用CSS驱动,通过 :popover-open 或自定义属性选择器触发,省去 animated 状态变量。React同理,只需在条件渲染时添加类名,@starting-style自动处理入场动画。

浏览器兼容与降级方案

截至2026年8月,@starting-style在Chrome 117+、Edge 117+、Safari 17.4+中可用,Firefox尚未支持。降级策略:

@supports (transition-behavior: allow-discrete) {
  .modal {
    transition: opacity 0.3s, display 0.3s allow-discrete;
  }
  @starting-style {
    .modal.open { opacity: 0; }
  }
}

@supports not (transition-behavior: allow-discrete) {
  .modal {
    animation: fadeIn 0.3s ease forwards;
  }
  @keyframes fadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
  }
}

渐进增强方案确保在不支持@starting-style的浏览器中仍能呈现入场效果,仅退出动画降级为即时隐藏。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/cssstartingstyle-yu-guo-du-dong-hua-ling-yan-chi-chu-shi/

(0)
小编小编
上一篇 2小时前
下一篇 2小时前

相关推荐