心态好好
摆脱懒惰和拖延(๑>◡<๑)

用一个“滑块”做出丝滑的 Tab 背景切换

在很多场景下,Tab 的“选中态”如果只靠文字变粗或颜色变化,给用户的反馈会比较弱,交互性一般。如果有一块会跟随选项移动的背景滑块,就可以显著提升可感知的交互质量,看起来更丝滑更爽。

 

所谓 Tab 背景切换“丝滑移动”,直觉上像是背景在从 A 选项滑到 B 选项;但实现层面最稳的思路不是改背景图,而是:在 Tab 容器里放一个“滑块元素”(选中背景)滑块做绝对定位,放在各个 Tab 文本的下方,点击/自动切换时,只更新滑块的 leftwidth并使用CSS 的 transition 属性做过渡动画。这样做有几个好处是:DOM 结构清晰,动的永远是同一个滑块、宽度可自适应内容(某些 Tab选项卡宽度更大,所以要注意滑块背景的宽度不要写死,我在这方面踩过很多次坑了o(╥﹏╥)o)。

 

需要注意的是:滑块必须在“同一个容器”里移动,html示例代码如下:

<div class="tab">
  <div class="tab-slide"></div>

  <div class="tab-item active">广告创建</div>
  <div class="tab-item">广告管理</div>
  <div class="tab-item">创意管理</div>
  <div class="tab-item">数据报表</div>
</div>

关键点有两条:tab 需要 position:relative,滑块才能用它当参照系;tab-slide 要放在 tab-item 前面或后面都行,但必须用 z-index 控制:滑块在下,文字在上.

接下来用css做过渡动画,css代码示例如下:

.tab {
  position: relative;
  display: flex;
  padding: 8px;
  border-radius: 999px;
  background: #f4faff;
}

.tab-item {
  position: relative;
  z-index: 2;
  padding: 14px 20px;
  border-radius: 999px;
  cursor: pointer;
}

.tab-slide {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  left: 10px;
  height: 46px;
  width: 100px;
  border-radius: 999px;
  z-index: 1;

  transition: all 0.3s ease-in-out;
}

注意的点:滑块用绝对定位让垂直居中更稳,不依赖具体行高。滑块默认 left/width 给一个初值,避免首次渲染闪动。

接下来用用js计算offsetLeft / offsetWidth 目标位置与宽度,示例代码如下:

const tabItems = document.querySelectorAll(".tab-item");
const slide = document.querySelector(".tab-slide");

let index = 0;

function moveSlideTo(item) {
  const targetLeft = item.offsetLeft;
  const targetWidth = item.offsetWidth;

  slide.style.left = `${targetLeft}px`;
  slide.style.width = `${targetWidth}px`;
}

tabItems.forEach((item, i) => {
  item.addEventListener("click", () => {
    index = i;

    tabItems.forEach((el) => el.classList.toggle("active", el === item));
    moveSlideTo(item);
  });
});

// 初始化:让滑块对齐默认 active
const initActive = document.querySelector(".tab-item.active") || tabItems[0];
moveSlideTo(initActive);

注意为什么这套写法“看起来很丝滑”?因为 JS 只是把滑块从旧位置改到新位置,本质是一次样式更新,真正的平滑感来自 CSS 的过渡。

 

接下来可以按需求加入轮播功能,实现自动轮播。

如果 Tab 对应的内容区也在自动轮播(例如每 3 秒切一个面板),滑块应该同步移动。常见做法:让一个 index 作为状态源记录当前的索引
setInterval 周期性 index++
点击时清掉定时器,执行切换,再重启定时器(避免用户点击后马上被自动轮播打断,小细节但是很关键)

let timer = null;
const interval = 3000;

function goTo(i) {
  index = (i + tabItems.length) % tabItems.length;
  const item = tabItems[index];

  tabItems.forEach((el) => el.classList.toggle("active", el === item));
  moveSlideTo(item);
}

function autoStart() {
  timer = setInterval(() => goTo(index + 1), interval);
}

tabItems.forEach((item, i) => {
  item.addEventListener("click", () => {
    clearInterval(timer);
    goTo(i);
    autoStart();
  });
});

goTo(0);
autoStart();

注意点击时清掉定时器,如果不这么做,用户很有可能在刚点击后又被“自动切走”,会产生“页面不听话”的感觉,用户体验大打折扣。

总结来说很多看似复杂的交互实际上并没有想象的难,比如这个滑块丝滑切换移动,动的只有一个元素,状态由一个索引统一驱动,获取容器的的宽高,让滑块根据容器宽高移动,动画交给 CSS,就这么简单,会了很容易就能写出来,所以说不要被某些看似复杂的或者没见过没写过的交互吓到,仔细想想其实也不是都是这么难哈哈。

赞(0) 打赏
未经允许不得转载:东东的小屋 » 用一个“滑块”做出丝滑的 Tab 背景切换

评论 1

  1. #1

    A really good blog and me back again.

觉得文章有用就打赏一下文章作者

非常感谢你的打赏,我们将继续提供更多优质内容,让我们一起创建更加美好的网络世界!

支付宝扫一扫

微信扫一扫

登录

找回密码

注册