js滑块区间组件 原生js实现滑块区间组件
蒲公英芽 人气:0功能需求:
1、最小值为0,按照给定的最大值,生成区间范围;
2、拖动滑块移动时,显示相应的范围区间,滑块条显示对应的状态;
3、点击时,使最近的滑块移动到鼠标点击的位置。
默认效果:
当拖动滑块时,显示如下:
分析:
- 首先布局要写好,一共有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); } } }
加载全部内容