tabs

滚动的tabs

by BigFatK

HTML

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Tabs组件滚动动画(JS实现)</title>
</head>
<body>
  <div class="app">
    <div class="tabs">
       <div class="tab active">C</div>
      <div class="tab">C++</div>
      <div class="tab">JavaScript</div>
      <div class="tab">Java</div>
      <div class="tab">Python</div>
      <div class="tab">TypeScript</div>
      <div class="tab">很长的不知道什么语言哈哈哈哈</div>
      <div class="tab">Lisp</div>
      <div class="tab">C++</div>
      <div class="tab">JavaScript</div>
      <div class="tab">Java</div>
      <div class="tab">Python</div>
      <div class="tab">TypeScript</div>
      <div class="tab">很长的不知道什么语言哈哈哈哈</div>
      <div class="tab">Lisp</div>
      <div class="tab">C++</div>
      <div class="tab">JavaScript</div>
      <div class="tab">Java</div>
      <div class="tab">Python</div>
      <div class="tab">TypeScript</div>
      <div class="tab">很长的不知道什么语言哈哈哈哈</div>
      <div class="tab">Lisp</div>
    </div>
  </div>
</body>
</html>

CSS

* {
      margin: 0;
      padding: 0;
    }

    .tabs {
      overflow: auto;
      display: flex;
      flex-flow: row nowrap;
      background-color: yellow;
    }

    .tab {
      white-space: nowrap;
      padding: 10px 20px;
    }

    .tab.active {
      color: red;
    }

JavaScript

main()

function main () {
  document.querySelector('.tabs').addEventListener('click', event => {
    // 点击样式
    const oldTab = document.querySelector('.active')
    if (oldTab) oldTab.classList.remove('active')
    const newTab = event.target
    newTab.classList.add('active')

    // 滚动到中间,需要先计算要滚动的距离`offset`
    const rect = newTab.getBoundingClientRect()
    const offset = rect.left + rect.width / 2 - window.innerWidth / 2
    const tabs = document.querySelector('.tabs')
    animateScroll(tabs, offset, 200)
  })
}

// setInterval实现
// function animateScroll (el, offset, ms) {
//   const interval = 16 // 每段的毫秒数(貌似不低于16ms?)
//   const n = ms / interval // 分成的段数
//   let i = 0
//   const timerId = setInterval(() => {
//     el.scrollLeft += offset / n
//     ++i
//     if (i >= n) clearInterval(timerId)
//   }, interval)
// }

// requestAnimationFrame实现
function animateScroll (el, offset, ms) {
  const n = ms / 16 // 帧数
  let i = 0
  const acc = offset / n // 每帧偏移量

  function step () {
    ++i
    el.scrollLeft += acc
    if (i < n) {
      requestAnimationFrame(step) // 还不够,就继续递归
    }
  }

  requestAnimationFrame(step)
}