基于Mithril.js实现可拖拽滑块组件的完整指南
本文详细介绍如何基于Mithril.js、JavaScript和SCSS从零开发一个可拖拽滑块组件。内容涵盖核心逻辑实现、样式配置及调用方法,支持点击圆点、灰条及百分比节点进行拖动,并兼容组件外拖动行为,旨在为不使用UI框架的项目提供轻量级解决方案。
基于Mithril.js实现可拖拽滑块组件的完整指南
本教程旨在展示如何利用Mithril.js、JavaScript和SCSS技术栈构建一个支持拖拽操作的滑块组件,供开发者参考使用。
问题描述:
在项目需求中,需要实现一个支持拖拽交互的滑块组件。由于项目限制无法使用现有的UI框架,因此决定自主开发该组件。以下将直接展示具体实现代码。
技术要求:
开发者需具备Mithril.js、JavaScript以及SCSS的基础知识,以便理解并应用本文提供的代码方案。
JavaScript逻辑与页面结构实现
以下是JavaScript逻辑与页面结构的实现代码。
var m = require("mithril");
require('./slider.scss');
import slider from './slider';
let obj = {
colorWidth: 0, // 已拖拽长度
clickOpen: false, // 是否开启拖拽
sliderDom: '', // 绑定的灰条dom
colorDom: '', // 绑定的有色条dom
radiusDom: '', // 绑定的圆点dom
moveEmentRect: null, // 获取灰条dom参数
Percentage: 0, // 百分比
minWidth: 0, // 拖动区间下限
maxWidth: 0, // 拖动区间上限
sliderCallback: null, // 参数回调
node: [0, 25, 50, 75, 100], // 节点数及占比
// 初始化数据
initslider:function(){
obj.sliderDom = document.getElementsByClassName('slider-body')[0]; // 允许进行开始拖拽的元素
obj.colorDom = document.getElementsByClassName('slider-section')[0]; // 允许进行开始拖拽的元素
obj.radiusDom = document.getElementsByClassName('slider-radius-body')[0]; // 允许进行开始拖拽的元素
obj.moveEmentRect = obj.sliderDom.getBoundingClientRect(); // 获取拖拽父元素的宽度
obj.maxWidth = obj.moveEmentRect.width;
},
// 处理宽度值域
handleWidth:function(EV){
if (EV <= obj.minWidth) {
return obj.minWidth;
} else if (EV >= obj.maxWidth) {
return obj.maxWidth;
} else {
return EV;
}
},
// 鼠标点击 拖动开始
getMousedown:function(e){
if (e.target === obj.sliderDom || e.target === obj.colorDom || e.target === obj.radiusDom) { // 判断是否是可点击拖拽的元素
obj.clickOpen = true; // 打开拖拽状态
let Width = e.clientX - obj.moveEmentRect.left; // 计算拖拽距离
obj.colorWidth = this.handleWidth(Width); // 处理拖拽距离转化为长度
console.log(obj.colorWidth, '拖动开始')
this.sliderCallback && this.sliderCallback({
colorWidth : this.getPercentage(), // 将数据回传页面
})
}
},
// 拖动中
getMoveWidth:function(e){
if (obj.clickOpen) {
let moveX = e.clientX - obj.moveEmentRect.left;
obj.colorWidth = this.handleWidth(moveX);
console.log(obj.colorWidth, '拖动中')
this.sliderCallback && this.sliderCallback({
colorWidth : this.getPercentage(), // 将数据回传页面
})
}
},
// 鼠标松开 拖动结束
getmouseUp:function(){
obj.clickOpen = false;
console.log('拖动结束')
},
// 绑定到body上,实现在组件外面可以拖拽
getBodyMouse: function(){
let body = document.querySelector('body');
body.onmousemove = function(e){
obj.getMoveWidth(e); // 在body上拖拽组件
};
body.onmouseup = function(e){
obj.getmouseUp(e); // 在body上拖拽结束时关闭可拖拽状态
obj.onmouseout(); // 在body上结束拖拽时隐藏百分比
}
},
// 计算拖动的百分比
getPercentage: function () {
let _P = (Number(obj.colorWidth) / Number(obj.maxWidth)).toFixed(2);
this.Percentage = Math.floor((Number(_P || 0) * 100));
return Number(_P);
},
// 鼠标移入显示百分比
onmouseover:function(){
let _S = document.getElementsByClassName('slider-percentage')[0];
_S.style.display = 'block';
},
// 鼠标移除隐藏百分比
onmouseout:function(){
let _S = document.getElementsByClassName('slider-percentage')[0];
_S.style.display = 'none';
},
// 清除数据
closemode: function () {
obj.colorWidth = 0; // 已拖拽长度
obj.clickOpen = false; // 是否开启拖拽
obj.sliderDom = ''; // 绑定的灰条dom
obj.colorDom = ''; // 绑定的有色条dom
obj.radiusDom = ''; // 绑定的圆点dom
obj.moveEmentRect = null; // 获取灰条dom参数
obj.Percentage = 0; // 百分比
obj.minWidth = 0;
obj.maxWidth = 0;
obj.sliderCallback = null; // 参数回调
},
// 百分比选择
getNodePer:function () {
return obj.node.map((item) => {
return m('div',{class:'slider-node', style: `left: ${item}%`, onclick: function(){
obj.getNodeData(item);
}},[
])
})
},
getNodeData:function(item){
obj.colorWidth = Number(obj.maxWidth) * (item / 100);
this.sliderCallback && this.sliderCallback({
colorWidth : this.getPercentage(), // 将数据回传页面
});
},
}
export default {
oninit: function (vnode) {
},
oncreate: function (vnode) {
obj.sliderCallback = vnode.attrs.cb;
obj.initslider();
obj.onmouseout();
obj.getBodyMouse();
},
view: function (vnode) {
return m('div', {class: 'slider'}, [
m('div',{class:"slider-body",onmousedown:function(e){
obj.getMousedown(e);
},onmousemove:function(e){
obj.getMoveWidth(e);
},onmouseup:function(e){
obj.getmouseUp(e);
}},[
m('div',{class:"slider-section", style:`width: ${obj.colorWidth}px`},[
m('div',{class:"slider-radius",onmouseover:function(){
obj.onmouseover();
},onmouseout:function(){
obj.onmouseout();
}},[
m('div',{class:"slider-radius-body"},[])
]),
m('div',{class:"slider-percentage" , style: `left: ${obj.colorWidth - 25}px`},[
obj.Percentage + '%'
]),
]),
obj.getNodePer(),
]),
])
},
onremove: function (vnode) {
obj.closemode();
},
}
SCSS样式代码配置
对应的SCSS样式代码如下。
// 用的是scss预处理样式
// $arrowsSize scss变量
// var(--primary-lighten)用的是全局颜色,可以直接用颜色值代替
// $dark #ligth 为黑夜白天样式,可以不用。
$arrowsSize: 6px; // 三角形 大小
.slider{
width: 100%;
.slider-body{
width: 100%;
height: 6px;
margin: 16px 0;
border-radius: 5px;
position: relative;
cursor: pointer;
.slider-section{
height: 6px;
background-color: var(--primary-lighten);
// width: 30%;
position: absolute;
left: 0;
border-radius: 5px;
.slider-radius{
height: 16px;
width: 16px;
position: absolute;
left: 100%;
z-index: 999;
top: -5px;
transform: translateX(-50%);
background-color: transparent;
text-align: center;
user-select: none;
line-height: normal;
.slider-radius-body{
width: 16px;
height: 16px;
border: 2px solid var(--primary-lighten);
background-color: var(--fontwhite-base);
border-radius: 50%;
transition: .2s;
user-select: none;
&::after {
content: '';
height: 100%;
display: inline-block;
vertical-align: middle;
}
}
}
.slider-percentage{
// display: none;
height: 25px;
width: 50px;
line-height: 25px;
border-radius: 5px;
background-color: var(--mode-darken);
text-align: center;
font-size: 14px;
color: var(--font-darken);
position: absolute;
top: -40px;
// left: 100%;
&::after {
content: '';
display: inline-block;
vertical-align: middle;
width: 0;
height: 0;
position: absolute;
border-top: solid $arrowsSize;
border-left: solid $arrowsSize transparent !important;
border-right: solid $arrowsSize transparent !important;
border-bottom: solid $arrowsSize transparent !important;
top: 25px;
left: 35%;
color: var(--mode-darken);
}
}
}
.slider-node{
position: absolute;
height: 8px;
width: 8px;
border-radius: 100%;
background-color: var(--fontwhite-base);
border: 2px solid var(--primary-lighten);
transform: translateX(-50%);
top: -1px;
}
}
}
#dark .slider-body{
background-color: var(--line-darken3);
}
#light .slider-body{
background-color: var(--line-lighten3);
}
组件调用方法
组件调用方式如下。
getSlider: function () {
return m(slider, {
cb : function(arg){
console.log(arg,22222)
},
});
},
obj.getSlider(),
补充说明:
受公司项目保密协议及电脑安全策略限制,无法录制视频或生成GIF动图,因此此处仅通过静态截图展示运行效果。
最终效果展示。
交互功能包括:点击圆点直接拖动、点击灰色轨道区域选中后拖动,以及点击轨道上的百分比节点进行拖动。此外,事件监听器已绑定至body元素,这意味着用户可以在灰色轨道上开始拖动动作,即使鼠标移出组件边界,拖拽操作依然有效,其交互体验类似于Element UI中的Slider组件。


拖动过程中相关参数的打印输出日志。


本文仅分享在编码过程中积累的技术心得,感谢相关平台提供的支持。
以上就是本文的全部内容,希望对读者的学习有所帮助,也欢迎继续支持本站。
JS处理含Emoji或中文的字符串转数组乱码问题
2026-11-05 16:12:28
JS怎么使用展开运算符合并对象
2026-11-05 14:42:18
ES6+环境下判断JS对象为空的简洁写法
2026-11-05 12:12:23
怎么通过replacer函数自定义JS中JSON的格式化输出
2026-11-05 11:19:44
JS正则表达式基础:字面量与RegExp构造函数的区别及常用元字符
2026-11-05 11:16:58
