JavaScript 实现原生 Range 与自定义滑动特效详解

作者:像素猫时间:2026-10-30 12:34:54浏览:371

本文通过两个完整实例,分别演示了如何使用原生 input[type=range] 控件以及纯 CSS 和 JavaScript 自定义实现滑动条特效。重点解析了自定义特效中嵌套 Span 布局、定时器动画逻辑、getComputedStyle 兼容性处理及鼠标事件时序控制等关键技术点,提供可直接运行的代码参考。

原生 Range 与自定义特效概述

本文分享两个 JavaScript 特效的实现:一个是基于原生 input[type=range] 控件,另一个是完全自定义编写的效果。下方提供了完整的代码及演示示例。


<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title></title>
  <style>
    #tip{
      position: absolute;
      top: 30px;
      left: 0;
      right: 0;
      width: 200px;
      height: 160px;
      margin: auto;
      border: 1px solid gray;
      background-color: cornsilk;
    }
    #tip div{
      position: relative;
      width: 100%;
      height: 80px;
      border-bottom: 1px solid gray;
    }
    .out{
      position: relative;
      left: 16%;
      display: inline-block;
      border: 2px solid royalblue;
      margin-top: 20px;
      width: 130px;
      height: 20px;
      background-color: lightgoldenrodyellow;
    }
    .in{
      display: block;
      height: 20px;
      line-height: 20px;
      text-align: right;
      color: white;
      width: 50%;
      background-image: linear-gradient(to right,powderblue 0%,#336699 50%,red 100%);
      -webkit-user-select: none;
      -moz-user-select: none;
      -ms-user-select: none;
      user-select: none;
    }
    input[type="range"] {
      position: relative;
      left: 19%;
      top: 5px;
      box-shadow: 0 1px 0 0px #424242, 0 1px 0 #060607 inset, 0px 2px 10px 0px black inset, 1px 0px 2px rgba(0, 0, 0, 0.4) inset, 0 0px 1px rgba(0, 0, 0, 0.6) inset;
      background-color: lightskyblue;
      border-radius: 15px;
      width: 60%;
      -webkit-appearance: none;
      -moz-appearance: none;
      appearance: none;
      height:15px;
    }
    input[type="range"]::-webkit-slider-thumb {
      -webkit-appearance: none;
      -moz-appearance: none;
      appearance: none;
      height: 20px;
      width: 10px;
      background-color: coral;
      border-radius: 15px;
      -webkit-box-shadow: 0 -1px 1px black inset;
      -moz-box-shadow: 0 -1px 1px black inset;
      box-shadow: 0 -1px 1px black inset;
    }
    input[type="range"]:before{
      content: attr(value);
      color: white;
      border-radius: 5px 0 0 5px;
      background-color: lightskyblue;
    }
    input[type="range"]:after{
      content: attr(max);
      color: white;
      border-radius:0 5px 5px 0;
      background-color: lightskyblue;
    }
    .b{
      display: inline-block;
      width: 22px;
      padding: 0;
    }
    #outer2{left: 5px}
    #btn1{
      position: relative;
      left: 5px;
    }
    #btn2{
      position: relative;
      left: 5px;
    }
  </style>
  <script>
    window.onload = function(){
      //原生组件range
      var inner = document.getElementById('inner1');
      var range = document.getElementById('range');
      range.onclick = function(){
          inner.innerHTML = range.value;
          inner.style.width = range.value+'%';
      };
      range.onmousemove = function(){
          inner.innerHTML = range.value;
          inner.style.width = range.value+'%';
      };
      //自定义组件
      var outer2 = document.getElementById('outer2');
      var inner2 = document.getElementById('inner2');
      var btn1 = document.getElementById('btn1');
      var btn2 = document.getElementById('btn2');
      var id,id1;
      var value = parseInt(inner2.innerHTML);
      var a = parseFloat(window.getComputedStyle(outer2,null).width)/100;
      //减---
      btn1.onmousedown = function(){
        id1 = setTimeout(function change(){
              if(value>0) {
                value--;
                inner2.innerHTML = value;
                inner2.style.width = (value) * a + 'px';
                id = setTimeout(function(){
                  clearTimeout(id);
                  change();
                },16.7);
              }else{clearTimeout(id);}
            },500);
      };
      btn1.onmouseup = function(){clearTimeout(id1);clearTimeout(id)};
      btn1.onclick = function(){
        console.log('a:'+a+','+'value:'+value);
        if(value>0){
          value--;
          inner2.innerHTML = value;
          inner2.style.width = (value)*a+'px';
        }
      };
      //加+++
      btn2.onmousedown = function(){
        id1 = setTimeout(function change(){
          if(value<100) {
            value++;
            inner2.innerHTML = value;
            inner2.style.width = value * a + 'px';
            id = setTimeout(function(){
              clearTimeout(id);
              change();
            },16.7);
          }else{clearTimeout(id);}
        },500);
      };
      btn2.onmouseup = function(){clearTimeout(id1);clearTimeout(id)};
      btn2.onclick = function(){
        if(value<100){
          value++;
          inner2.innerHTML = value;
          inner2.style.width = value*a+'px';
        }
      }
    }
  </script>
</head>
<body>
  <form id="tip">
    <div>
      <span id="outer1" class="out">
        <span id="inner1" class="in">50</span>
      </span>
      <input id="range" class="ran" type="range" min="0" max="100" step="1" value="50">
    </div>
    <div id="d2">
      <input id="btn1" class="b" type="button" value="<">
      <span id="outer2" class="out">
        <span id="inner2" class="in">50</span>
      </span>
      <input id="btn2" class="b" type="button" value=">">
    </div>
    按住按钮0.5秒, 会持续变化!
  </form>
</body>
</html>

第一个特效的实现逻辑较为简单,此处不再赘述,读者可直接参考代码。

自定义滑动条实现原理

本文重点介绍第二个自定义特效的实现过程。

面对需求或他人的特效案例时,建议先不要急于查看源码,而是先思考实现思路,梳理清楚逻辑后再动手编码。

该自定义特效的核心实现原理如下:

结构上采用一个 span 嵌套另一个 span 的方式。

外层 span 仅设置宽、高和边框,背景色设为透明。

内层 span 高度与外层一致,初始宽度设为默认的 50%,并预先设置好线性渐变背景色。

按钮的 onclick 事件处理较为简单,点击一次即改变内层 span 的宽度及显示的数字。

当触发按钮的 onmousedown 事件时,启动一个计时器。在等待 500ms 后执行 change 函数,该函数通过 setTimeout 回调自身,每 16.7ms 执行一次,从而实现平滑的动画效果。

关键技术难点解析

以下是实现过程中的难点解析:

第一处关键代码为 var a = parseFloat(window.getComputedStyle(outer2,null).width)/100;,用于获取初始宽度值。若使用 outer2.style.width 无法获取计算后的宽度,也可将 a 设为固定值(如 var a = 1.3)。需注意 IE9 以下版本不支持 getComputedStyle 方法,IE 浏览器需使用 Element 对象的 currentStyle 属性。

2. 这一句 btn1.onmouseup = function(){clearTimeout(id1); clearTimeout(id)}; 很关键,没了它,在onclick触发之前,会先触发onmosedown,在500ms后,开始执行,之后一直执行外层的计时器; 3. 其它的都不是难点; 这个实例其实扩展到其它很多应用,比如可以把中间的显示部分替换为文章、图片等等,再把按钮换成自定义的,效果将会很酷的!

若文中存在不足之处,欢迎指正。

以上关于 JavaScript 原生 Range 与自定义特效的简单实例分享完毕,希望能为您提供参考,并感谢您的支持。

作者最新文章