原生js实现滑块区间组件开发

滑块区间组件

功能需求:

  1. 最小值为0,按照给定的最大值,生成区间范围;
  2. 拖动滑块移动时,显示相应的范围区间,滑块条显示对应的状态;
  3. 点击时,使最近的滑块移动到鼠标点击的位置。

默认效果:
原生js实现滑块区间组件开发
当拖动滑块时,显示如下:
原生js实现滑块区间组件开发
分析:

  • 首先布局要写好,一共有4个元素,两个滑块和两个滑块条。布局时要考虑到后期对滑块和滑块条进行事件监听,尽可能少地出现事件冒泡;
  • 拖动滑块时,要区分是左边的滑块还是右边的滑块;
  • 鼠标的click事件和mousedown事件要兼容好,这里统一使用的是mousedown事件;
  • 要确定好左右滑块的最大最小 left 值;
  • 滑块条的显示就很简单了,宽度是左、右滑块的定位差值,left值是左滑块的left值;
  • 因为使用了事件委托机制,而在mousemove和mouseup事件中,无法判断当前操作的是哪一个滑块,所以要在鼠标按下时,将当前操作的对象传到mousemove事件中;

下面附上代码:

html结构,实例化滑块,可以设置当前滑块的区间范围:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>slide</title>
</head>
<body>
   <script type="module">
       import Slide from "./js/Slide.js";

       init();
       function init(){
           //参数为最大范围,不传的话默认是4000
           let slide=new Slide(4200);
           slide.appendTo("body");
       }

   </script>
</body>
</html>

Slide.js文件:完成创建滑块,拖动滑块,点击滑块的功能。

import Utils from "./Utils.js";
export default class Slide{
    static styleCss=false;
    //最小范围
    minNum=0;
    //最大范围
    maxNum;
    //左边按钮的left值
    leftBtnLeft=0;
    //右边按钮的left值
    rightBtnLeft=238;
    constructor(_max=4000){
        //最大值默认为4000
        this.maxNum=_max;
        this.elem=this.createElem();
    }
    createElem(){
        if(this.elem) return this.elem;
        //创建最外层容器
        let div=Utils.createE("div");
        div.className="slideContainer";
        div.innerHTML=`<p class="priceTxt">价格<span id="rangeText">¥${this.minNum}-${this.maxNum}</span></p>
        <div class="rangeContainer" id="rangeContainer">
            <div class="bgRange" id="bgRange"></div>
            <div class="priceRange" id="priceRange"></div>
            <span id="leftBtn" class="leftBtn"></span>
            <span id="rightBtn" class="rightBtn"></span>
        </div>`;
        Utils.getIdElem(div,this);
        //设置样式
        Slide.setStyles();
        //给父元素监听mousedown事件
        this.rangeContainer.addEventListener("mousedown",e=>this.mouseHandler(e))
        return div;
    }
    appendTo(parent){
        Utils.appendTo(this.elem,parent);
    }
    mouseHandler(e){
        //注意:getBoundingClientRect()返回的结果中,width height 都是包含border的
        let rect=this.rangeContainer.getBoundingClientRect();
        switch (e.type) {
            case "mousedown":
                //取消鼠标快速拖动的默认事件
                e.preventDefault();
                this.x = e.offsetX;
                this.btnType=e.target.id;
                //如果点击的是背景条,执行rangeClick函数
                if(/Range/.test(this.btnType)){
                    e.stopPropagation();
                    //点击函数
                    this.rangeClick(e);
                    return;
                }
                //如果点击的是按钮,监听document鼠标移动事件
                this.mouseHandlers=e=>this.mouseHandler(e);
                document.addEventListener("mousemove", this.mouseHandlers);
                document.addEventListener("mouseup", this.mouseHandlers);
                break;
            case "mousemove":
                let x = e.clientX - rect.x - this.x;
                //获取左右按钮的left值
                this.leftBtnLeft=parseInt(getComputedStyle(this.leftBtn).left);
                this.rightBtnLeft=parseInt(getComputedStyle(this.rightBtn).left);
                if (this.btnType === "leftBtn") {
                    //确定左边按钮的取值范围
                    if (x < 0) x = 0;
                    if (x > this.rightBtnLeft) x = this.rightBtnLeft;
                    this.leftBtn.style.left = x + "px";
                } else if (this.btnType === "rightBtn") {
                    //确定右边按钮的取值范围,减去1px边框
                    if (x < this.leftBtnLeft) x = this.leftBtnLeft;
                    if (x > this.bgRange.offsetWidth - 2) x = this.bgRange.offsetWidth - 2;
                    this.rightBtn.style.left = x + "px";
                }
                //文字范围显示
                this.changeRangeText();
                break;
            case "mouseup":
                //移动事件监听
                document.removeEventListener("mousemove", this.mouseHandlers);
                document.removeEventListener("mouseup", this.mouseHandlers);
                break;
        }
    }
    rangeClick(e){
        //计算出鼠标点击位置的值
        let click_X=e.clientX-this.rangeContainer.getBoundingClientRect().x-this.leftBtn.offsetWidth/2;
        //判断,如果当前点击的位置是在左边按钮的左侧、或者当左右按钮重叠时,点击的位置在按钮左侧,让左边按钮移动到鼠标点击的位置
        if(Math.abs(click_X-this.leftBtnLeft)<Math.abs(click_X-this.rightBtnLeft) || 
        (this.leftBtnLeft===this.rightBtnLeft && click_X<this.leftBtnLeft)) this.leftBtn.style.left=click_X+"px";
        //否则,让右边按钮移动到鼠标点击的位置
        else this.rightBtn.style.left=click_X+"px";
        //获取移动后的左右按钮的left值
        this.leftBtnLeft=parseInt(getComputedStyle(this.leftBtn).left);
        this.rightBtnLeft=parseInt(getComputedStyle(this.rightBtn).left);
        //文字范围显示
        this.changeRangeText();
    }
    changeRangeText(){
        //计算出最小范围与最大范围的值,四舍五入
        let minTxt=Math.round(this.leftBtnLeft/(this.bgRange.clientWidth-2)*this.maxNum);
        let maxTxt=Math.round(this.rightBtnLeft/(this.bgRange.clientWidth-2)*this.maxNum);
        this.rangeText.innerText=`¥${minTxt}-${maxTxt}`;
        //滑块颜色的改变
        this.changeRangeSlide();
    }
    changeRangeSlide(){
        //滑块宽度等于左右按钮间的距离
        this.priceRange.style.width=this.rightBtnLeft-this.leftBtnLeft+"px";
        //滑块的left值等于左边按钮的left值
        this.priceRange.style.left=this.leftBtnLeft+"px";
    }
    static setStyles(){
        if(Slide.styleCss) return;
        Slide.styleCss=true;
        Utils.insertCss(".slideContainer",{
            width:"260px",
            height:"70px",
            margin:"50px"
        })
        Utils.insertCss(".priceTxt",{
            fontSize:"14px",
            color:"#666",
            marginBottom:"20px"
        })
        Utils.insertCss(".priceTxt span",{
            float:"right"
        })
        Utils.insertCss(".rangeContainer",{
            width:"260px",
            height:"20px",
            position:"relative",
        })
        Utils.insertCss(".bgRange",{
            width:"240px",
            height:"3px",
            backgroundColor:"#dedede",
            position:"absolute",
            left:"10px",
            top:"9px"
        })
        Utils.insertCss(".priceRange",{
            width:"240px",
            height:"3px",
            background:"#ffa800",
            position:"absolute",
            left:"10px",
            top:"9px"
        })
        Utils.insertCss(".rangeContainer span",{
            width: "20px",
            height: "20px",
            borderRadius:"50%",
            border:"1px solid #ccc",
            background:"#fff",
           position:"absolute",
           top:"0px",
           boxShadow:"2px 2px 2px #333"
        })
        Utils.insertCss(".leftBtn",{
            left:"0px"
        })
        Utils.insertCss(".rightBtn",{
            left:"238px"
        })
    }
}

Utils.js文件:是一个工具包文件。

export default class Utils{
    static createE(elem,style,prep){
        elem=document.createElement(elem);
        if(style) for(let prop in style) elem.style[prop]=style[prop];
        if(prep) for(let prop in prep) elem[prop]=prep[prop];
        return elem;
    }
    static appendTo(elem,parent){
        if (parent.constructor === String) parent = document.querySelector(parent);
        parent.appendChild(elem);
    }
    static randomNum(min,max){
        return Math.floor(Math.random*(max-min)+min);
    }
    static randomColor(alpha){
        alpha=alpha||Math.random().toFixed(1);
        if(isNaN(alpha)) alpha=1;
        if(alpha>1) alpha=1;
        if(alpha<0) alpha=0;
        let col="rgba(";
        for(let i=0;i<3;i++){
            col+=Utils.randomNum(0,256)+",";
        }
        col+=alpha+")";
        return col;
    }
    static insertCss(select,styles){
        if(document.styleSheets.length===0){
            let styleS=Utils.createE("style");
            Utils.appendTo(styleS,document.head);
        }
        let styleSheet=document.styleSheets[document.styleSheets.length-1];
        let str=select+"{";
        for(var prop in styles){
            str+=prop.replace(/[A-Z]/g,function(item){
                return "-"+item.toLocaleLowerCase();
            })+":"+styles[prop]+";";
        }
        str+="}"
        styleSheet.insertRule(str,styleSheet.cssRules.length);
    }
    static getIdElem(elem,obj){
        if(elem.id) obj[elem.id]=elem;
        if(elem.children.length===0) return obj;
        for(let i=0;i<elem.children.length;i++){
            Utils.getIdElem(elem.children[i],obj);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值