效果如下gif
只进行了基础的事件和布局,可优化的地方:luminance-box这个div加上后,由于和slider-run-way都是absolute定位,导致slider-run-way的点击事件无法设置值,只能通过滑块设置。暂时想不到咋处理,有想法可以讨论一下
html布局
<div class="light-slider">
<div
class="slider-run-way"
@click.stop="handleClickRunway($event)"
ref="runwayRef"
>
<div class="active-bar" :style="{ width: activeWidth + '%' }"></div>
<div
class="control-wrapper"
:style="{ left: activeWidth + '%' }"
@mousedown="handleMousedown($event)"
>
<div class="control-btn"><span></span><span></span></div>
</div>
</div>
<div class="luminance-box" @click="handleRight">
<img src="../../../../assets/home/homepage_control_bright.png" />
<div class="right-box">
<span class="num">{{ activeWidth }}%</span>
<span class="l-title">luminance</span>
</div>
</div>
</div>
js部分
export default {
name: "lightingControl",
props: {
isDisabled: {
type: Boolean,
default: true,
},
},
data() {
return {
activeWidth: 13,
runWayWidth: 0, // 滑道总宽度
startX: 0,
dragging: false,
isClick: true,
startPosition: 0,
};
},
mounted() {
this.$nextTick(() => {
this.runWayWidth = this.$refs.runwayRef.clientWidth;
});
},
methods: {
handleClickRunway(e) {
if (!this.isClick || this.isDisabled) return;
console.log(e);
const { runWayWidth } = this;
const { offsetX } = e;
const percent = parseInt((offsetX / runWayWidth) * 100);
this.activeWidth = percent;
},
handleRight(e) {
return false;
},
handleMousedown(e) {
if (this.isDisabled) return;
e.preventDefault();
this.startX = e.clientX;
this.onDragStart(e);
window.addEventListener("mousemove", this.onDragging);
window.addEventListener("mouseup", this.onDragEnd);
},
onDragStart(e) {
this.dragging = true;
this.isClick = true;
this.startPosition = parseFloat(this.activeWidth);
},
onDragging(e) {
e.stopPropagation();
if (this.dragging) {
this.isClick = false;
let currentX = e.clientX;
let diff = ((currentX - this.startX) / this.runWayWidth) * 100;
this.newPosition = this.startPosition + diff;
this.setPosition(this.newPosition);
}
},
onDragEnd() {
if (this.dragging) {
setTimeout(() => {
this.dragging = false;
if (!this.isClick) {
this.setPosition(this.newPosition);
this.isClick = true;
}
}, 0);
window.removeEventListener("mousemove", this.onDragging);
window.removeEventListener("mouseup", this.onDragEnd);
}
},
setPosition(newPosition) {
if (newPosition < 0) {
newPosition = 0;
} else if (newPosition > 100) {
newPosition = 100;
}
this.activeWidth = Math.round(newPosition);
},
},
};
css
.light-slider {
margin-top: 16px;
position: relative;
.slider-run-way {
width: 100%;
position: relative;
cursor: pointer;
height: 70px;
background: #d7eeff;
border-radius: 4px 4px 4px 4px;
.active-bar {
height: 100%;
position: absolute;
left: 0;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
background: #36c1fd;
}
.control-wrapper {
display: flex;
align-items: center;
justify-content: center;
height: 30px;
width: 22px;
position: absolute;
user-select: none;
z-index: 1001;
top: 20px;
transform: translateX(-50%);
background-color: transparent;
.control-btn {
width: 16px;
height: 24px;
background: #fff;
box-shadow: 0px 0px 5px 0px rgba(76, 115, 184, 0.5);
border-radius: 2px 2px 2px 2px;
color: #36c1fd;
display: flex;
align-items: center;
justify-content: space-evenly;
span {
width: 2px;
height: 6px;
background: #36c1fd;
border-radius: 30px 30px 30px 30px;
}
}
}
}
.luminance-box {
display: flex;
width: 100%;
height: 70px;
justify-content: space-between;
align-items: center;
position: absolute;
left: 0;
top: 0;
img {
width: 20px;
height: 20px;
margin-left: 14px;
}
.l-title {
font-size: 14px;
}
.right-box {
color: #2e2e48;
display: flex;
flex-direction: column;
align-items: center;
font-weight: bolder;
margin-right: 10px;
.num {
font-size: 24px;
}
}
}
}