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

React如何实现选中条带平滑滑动动画的Tab选项卡

Tab选中指示条平滑滑动改造方案

你当前的实现通过固定left/right类切换指示条位置,既没有过渡动画逻辑,也只能适配2个固定宽度的Tab,按以下步骤改造即可实现丝滑的滑动效果:

  • 给指示条添加CSS过渡属性
    动画的核心是给指示条加上状态变化的过渡规则,在.selector样式中添加以下代码:

    .selector {
      position: absolute;
      bottom: 0;
      height: 2px;
      background: #1677ff;
      z-index: 1;
      /* 控制滑动动画的时长、缓动曲线,可根据需求调整参数 */
      transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    }
    

    注意承载Tab和指示条的父容器必须设置position: relative,否则指示条绝对定位会出现偏移。

  • 替换硬编码类名逻辑,动态计算指示条位置
    去掉原来tab === "two" ? "right" : "left"的固定判断,通过DOM属性获取选中Tab的实际偏移量和宽度,动态给指示条赋值样式,不管Tab数量多少、文字长度是否一致都能适配。React环境下核心实现代码如下:

    import { useState, useRef, useEffect } from 'react';
    
    export default function Tabs() {
      const [activeTab, setActiveTab] = useState('one');
      // 存储每个Tab项的DOM引用
      const tabRefs = useRef({});
      // 存储指示条的动态样式
      const [indicatorStyle, setIndicatorStyle] = useState({ left: 0, width: 0 });
    
      // 选中Tab变化时,更新指示条位置
      useEffect(() => {
        const currentTabNode = tabRefs.current[activeTab];
        if (currentTabNode) {
          setIndicatorStyle({
            left: currentTabNode.offsetLeft,
            width: currentTabNode.offsetWidth
          });
        }
      }, [activeTab]);
    
      // 组件首次挂载时初始化指示条位置
      useEffect(() => {
        const initialTabNode = tabRefs.current[activeTab];
        if (initialTabNode) {
          setIndicatorStyle({
            left: initialTabNode.offsetLeft,
            width: initialTabNode.offsetWidth
          });
        }
      }, []);
    
      // 示例Tab配置,可按需扩展
      const tabConfig = [
        { key: 'one', label: '第一个Tab' },
        { key: 'two', label: '第二个Tab' },
        { key: 'three', label: '第三个长文本Tab' }
      ];
    
      return (
        <div className="tabs-wrapper" style={{ position: 'relative' }}>
          <div className="tab-nav">
            {tabConfig.map(tab => (
              <div
                key={tab.key}
                ref={node => (tabRefs.current[tab.key] = node)}
                className={`tab-item ${activeTab === tab.key ? 'active' : ''}`}
                onClick={() => setActiveTab(tab.key)}
                style={{ padding: '12px 16px', cursor: 'pointer' }}
              >
                {tab.label}
              </div>
            ))}
          </div>
          {/* 指示条直接绑定动态计算的样式,不再依赖固定类名 */}
          <div className="selector" style={indicatorStyle}></div>
        </div>
      );
    }
    
  • 补充响应式适配(可选)
    如果你的页面需要适配不同屏幕尺寸,在窗口大小变化时Tab宽度可能发生改变,可以加resize事件监听,触发时重新计算指示条位置,避免错位:

    useEffect(() => {
      const recalcIndicatorPos = () => {
        const currentTabNode = tabRefs.current[activeTab];
        if (currentTabNode) {
          setIndicatorStyle({
            left: currentTabNode.offsetLeft,
            width: currentTabNode.offsetWidth
          });
        }
      };
      window.addEventListener('resize', recalcIndicatorPos);
      return () => window.removeEventListener('resize', recalcIndicatorPos);
    }, [activeTab]);
    

如果需要调整动画手感,直接修改.selector上transition属性的时长和缓动函数即可:比如将时长改为0.2s动画会更干脆,改为0.5s会更柔和。


内容的提问来源于stack exchange,提问作者Jenny Le

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 19:33:23