You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CSS View Transition过渡异常:切换标签时出现额外高度问题

问题描述

我正在用CSS View Transition开发标签页,将激活态的下划线动画改成了背景色过渡,但切换不同宽度的标签时,过渡过程中会出现额外高度,宽度相同的标签切换则无此问题。试过移除LI的height属性,改用top/bottom/left/right定位,问题仍未解决。请问要修改哪些样式或View Transition相关代码,才能修复这个过渡时的高度异常?

标签页切换过渡异常效果

相关代码(React + styled-components)
import { useState, flushSync } from 'react';
import styled from 'styled-components';

// 假设标签数据定义如下
const items = [
  { ID: 1, value: '首页' },
  { ID: 2, value: '很长很长的标签名称' },
  { ID: 3, value: '设置' },
];

function App() {
  const [activeID, setActiveID] = useState(1);

  const handleClick = (ID) => {
    if (!document.startViewTransition) {
      setActiveID(ID);
      return;
    }

    document.startViewTransition(() => {
      flushSync(() => {
        setActiveID(ID);
      });
    });
  };

  return (
    <>
      <UL>
        {items.map((item) => (
          <LI
            key={item.ID}
            $active={activeID === item.ID}
            onClick={() => handleClick(item.ID)}
          >
            {item.value}
          </LI>
        ))}
      </UL>
    </>
  );
}

const UL = styled.ul`
  display: flex;
  list-style: none;
  position: relative;
`;
const LI = styled.li`
  padding: 0.5rem 1rem;
  position: relative;

  &:before {
    content: "";
    display: block;
    position: absolute;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    background-color: ${({ $active }) =>
      $active ? "hsl(0 0% 100% / .1)" : "transparent"};
    border: ${({ $active }) =>
      $active ? "1px solid hsl(0 0% 100% / .1)" : "1px solid transparent"};
    border-radius: 0.5rem;
    view-transition-name: ${({ $active }) => ($active ? "highlight" : "")};
  }
`;

export default App;
解决方案

这个问题的核心是:不同宽度的标签项(LI)因内容长度差异,实际高度存在细微区别,View Transition在插值过渡时会放大这种差异,导致出现额外高度。可以通过两种方式修复:


方式一:统一标签项高度(简单快速)

强制所有标签项高度一致,消除高度差异带来的过渡异常:

  1. 修改LI的样式,添加固定高度并确保文字垂直居中:
const LI = styled.li`
  padding: 0.5rem 1rem;
  position: relative;
  height: 2.5rem; /* 根据你的设计调整合适高度 */
  display: flex;
  align-items: center; /* 保证文字垂直居中 */
`;
  1. 全局添加View Transition样式约束,避免过渡时的尺寸拉伸:
/* 在全局样式文件或style标签中添加 */
::view-transition-new(highlight),
::view-transition-old(highlight) {
  height: 100%;
  transform-origin: center;
}

方式二:分离高亮元素(更稳定)

把高亮背景从伪元素改成独立的绝对定位元素,脱离标签项的尺寸依赖,彻底避免高度问题:

  1. 修改App组件,添加独立的高亮元素:
function App() {
  const [activeID, setActiveID] = useState(1);
  const activeRef = useRef(null);

  const handleClick = (ID) => {
    if (!document.startViewTransition) {
      setActiveID(ID);
      return;
    }

    document.startViewTransition(() => {
      flushSync(() => {
        setActiveID(ID);
      });
    });
  };

  return (
    <>
      <UL>
        {/* 独立的高亮背景元素 */}
        <Highlight ref={activeRef} />
        {items.map((item) => (
          <LI
            key={item.ID}
            $isActive={activeID === item.ID}
            onClick={() => handleClick(item.ID)}
            ref={el => activeID === item.ID && (activeRef.current = el)}
          >
            {item.value}
          </LI>
        ))}
      </UL>
    </>
  );
}
  1. 重新定义高亮和容器样式:
const UL = styled.ul`
  display: flex;
  list-style: none;
  position: relative;
  align-items: center;
`;

const LI = styled.li`
  padding: 0.5rem 1rem;
  position: relative;
`;

const Highlight = styled.div`
  position: absolute;
  top: 0;
  bottom: 0;
  left: ${({ current }) => current?.offsetLeft || 0}px;
  width: ${({ current }) => current?.offsetWidth || 0}px;
  background-color: hsl(0 0% 100% / .1);
  border: 1px solid hsl(0 0% 100% / .1);
  border-radius: 0.5rem;
  view-transition-name: highlight;
`;

这种方式让高亮元素直接复用激活标签的尺寸,过渡时只处理位置和宽度变化,完全规避高度差异的影响。

内容的提问来源于stack exchange,提问作者Clayton Ingalls

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 18:57:08