js手写slider组件

142 阅读2分钟

主要内容有以下几点:

  • 精确定位计算:

    • 考虑了滑块本身的宽度(20px)
    • 计算实际可滑动的宽度时减去了滑块宽度
    • 添加了滑块中心点位置的计算
    • 考虑了wrapper的padding值(10px)
  • 进度条调整:

    • 添加了left: 10px确保进度条从正确位置开始
    • 宽度计算也考虑了实际可滑动范围
  • 性能优化:

    • 使用 requestAnimationFrame 优化动画性能
    • 保持代码在高频率更新时的流畅性
  • 实时响应:

    • 确保使用 input 事件进行实时更新
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Custom Slider</title>
    <style>
      .slider-wrapper {
        position: relative;
        margin-top: 40px;
        padding: 0 10px;
      }

      input[type='range'] {
        appearance: none;
        -webkit-appearance: none;
        width: 100%;
        height: 10px;
        border-radius: 5px;
        background: #ebecf1;
        outline: none;
        margin: 0;
      }

      input[type='range']::-webkit-slider-thumb {
        -webkit-appearance: none;
        width: 20px;
        height: 20px;
        border-radius: 50%;
        background: #2492fc;
        border: 3px solid #fff;
        box-shadow: 0 4px 8px rgba(36, 146, 252, 0.2);
        cursor: grab;
        position: relative;
        z-index: 2;
      }

      .slider-number {
        position: absolute;
        background: #2492fc;
        color: white;
        padding: 4px 8px;
        border-radius: 4px;
        font-size: 14px;
        transform: translateX(-50%);
        top: -35px;
      }

      .slider-number:after {
        content: '';
        position: absolute;
        bottom: -5px;
        left: 50%;
        transform: translateX(-50%);
        width: 0;
        height: 0;
        border-left: 5px solid transparent;
        border-right: 5px solid transparent;
        border-top: 5px solid #2492fc;
      }

      .slider-progress {
        position: absolute;
        height: 10px;
        background: #2492fc;
        border-radius: 5px;
        top: 50%;
        transform: translateY(-50%);
        z-index: 1;
        pointer-events: none;
        left: 10px;
      }
    </style>
  </head>
  <body>
    <div class="slider-wrapper">
      <div class="slider-number">0</div>
      <div class="slider-progress"></div>
      <input type="range" min="0" max="550" value="0" step="1" />
    </div>

    <script>
      const sliderWrapper = document.querySelector('.slider-wrapper')
      const sliderNum = document.querySelector('.slider-number')
      const input = document.querySelector('input')
      const progress = document.querySelector('.slider-progress')

      // 使用 requestAnimationFrame 优化性能
      function updateSlider(value) {
        requestAnimationFrame(() => {
          // 更新数字显示
          sliderNum.textContent = value

          // 计算滑块位置的百分比
          const percent = (value - input.min) / (input.max - input.min)

          // 计算实际可滑动的宽度(减去滑块的宽度)
          const thumbWidth = 20 // 滑块宽度
          const trackWidth = input.offsetWidth - thumbWidth

          // 计算滑块中心点位置
          const thumbPosition = percent * trackWidth + thumbWidth / 2 + 10

          // 更新数字标签位置,确保对齐滑块中心
          sliderNum.style.left = `${thumbPosition}px`

          // 更新进度条宽度
          progress.style.width = `${percent * trackWidth}px`
        })
      }

      // 使用 input 事件而不是 change 事件,确保实时更新
      input.addEventListener('input', (e) => {
        updateSlider(e.target.value)
      })

      // 初始化
      updateSlider(input.value)

      // 处理窗口大小改变
      window.addEventListener('resize', () => {
        updateSlider(input.value)
      })
    </script>
  </body>
</html>