自定义范围滑块

背景

原生的 HTML5 控件 range input,每个浏览器的样式不同,为了达到统一的样式效果,我们通常有两种方案可以考虑,直接修改 css 样式,或者模拟出一个滑块控件。

解决方案一

直接修改 css 样式。用-webkit-slider-thumb修改滑块样式,用-webkit-slider-runnable-track修改轨道样式,再结合:focus:hover等事件响应加上特效,让滑块更生动。

下面介绍下如何仅通过 css 来控制 input range 的样式。

做一个简单的演示

或者点击我们的 在线 Demo

兼容性

我们这里演示了webkit下的滑块样式,要想支持更多浏览器,还需要设置不同的伪类

  • ::-webkit-slider-thumb 针对 webkit/blink 设置滑块

  • ::-moz-range-thumb 针对火狐设置滑块

  • ::-ms-thumb 针对 IE 设置滑块

还有背景条的设置

  • ::-webkit-slider-runnable-track 针对 webkit/blink 设置背景条

  • ::-moz-range-track 针对火狐设置背景条

  • ::-ms-track/::-ms-fill-lower/::-ms-fill-upper 针对 IE 设置背景条

小技巧

我们在滑动条的中间位置放置了一条竖线,用于提示中间位置,竖线符号除了使用按键shift+\打出来之外,还有以下一些竖线的符号可以使用。

ǀǁ׀
ا

解决方案二

模拟出一个滑块控件。比如用div元素模拟 range input 样式,用pointdownpointmovepointup等事件等来模拟 input 的行为

代码如下

<div id="container">
  <div id="slider" class="slider">
    <div class="thumb"></div>
  </div>

  <div class="rate">50%</div>
</div>

整理下样式

#container {
  margin: 30% auto;
  width: 300px;
  text-align: center;
}
.slider {
  border-radius: 6px;
  background: #e0e0e0;
  height: 10px;
}

.thumb {
  touch-action: none;
  width: 20px;
  height: 20px;
  border-radius: 20px;
  position: relative;
  left: 140px;
  top: -5px;
  background: gray;
  cursor: pointer;
  transition: transform 0.3s;
}
.rate {
  margin-top: 20px;
}

编写事件

let thumb = slider.querySelector(".thumb");
let rate = container.querySelector(".rate");
let shiftX;
const total = slider.offsetWidth - thumb.offsetWidth;

function onThumbDown(event) {
  event.preventDefault(); // prevent selection start (browser action)

  shiftX = event.clientX - thumb.getBoundingClientRect().left;

  thumb.setPointerCapture(event.pointerId);

  thumb.style.transform = "scale(1)";

  thumb.onpointermove = onThumbMove;

  thumb.onpointerup = (event) => {
    // dragging finished, no need to track pointer any more
    // ...any other "drag end" logic here...
    thumb.onpointermove = null;
    thumb.onpointerup = null;

    thumb.style.transform = "scale(1.2)";
  };
}

function onThumbMove(event) {
  let newLeft = event.clientX - shiftX - slider.getBoundingClientRect().left;

  // if the pointer is out of slider => adjust left to be within the boundaries
  if (newLeft < 0) {
    newLeft = 0;
  }
  let rightEdge = slider.offsetWidth - thumb.offsetWidth;
  if (newLeft > rightEdge) {
    newLeft = rightEdge;
  }

  thumb.style.left = newLeft + "px";
  setRate(newLeft);
}

function onThumbEnter(event) {
  thumb.style.transform = "scale(1.2)";
}

function onThumbLeave(event) {
  thumb.style.transform = "scale(1)";
}

function setRate(left) {
  rate.innerText = ((left / total) * 100).toFixed(0) + "%";
}
thumb.onpointerdown = onThumbDown;
thumb.onpointerenter = onThumbEnter;
thumb.onpointerleave = onThumbLeave;

thumb.ondragstart = () => false;

看下实际效果

参考

更多的详细解读请参考以下博文

评论