基于mpvue实现微信小程序区间选择(range-slider)
微信小程序实现区间选择,时间范围选择,可设置步长
API
slider props
参数 | 必选 | 类型 | 默认值 | 说明 |
---|---|---|---|---|
disabled | false | Boolean | false | 是否禁用 |
max | false | Number | 24 | 最大值 |
min | false | Number | 0 | 最小值 |
value | false | Number | 0 | 默认值 |
step | false | Number | 0.5 | 步长 |
type | false | String | time | 组件类型 |
组件截图
Talk is cheap. Show me the code
<template>
<div class="range-slider" @click="onClick">
<div class="range-slider-wrap">
<div class="range-slider-bar" :style="atTrackStyle"></div>
<div class="range-slider-button" @touchmove="onTouchMove($event,'aX')" @touchend="onTouchEnd($event, 'aX')" @touchcancel="onTouchEnd" :style="{left: aX + '%'}">
<div class="range-slider-value">{{range[0]}}</div>
</div>
<div class="range-slider-button" @touchmove="onTouchMove($event,'bX')" @touchend="onTouchEnd($event, 'bX')" @touchcancel="onTouchEnd" :style="{left: bX + '%'}">
<div class="range-slider-value">{{range[1]}}</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
disabled: {
type: Boolean,
default: false
},
max: {
type: Number,
default: 24
},
min: {
type: Number,
default: 0
},
step: {
type: Number,
default: 0.5
},
value: {
type: Array,
default: () => [0, 0]
},
type: {
type: String,
default: 'time'
}
},
data () {
return {
aX: 0,
bX: 0,
width: 0, // range宽度
left: 0, // range 到屏幕左边的距离
deltaValue: this.max - this.min,
currentSlider: '',
currentValue: [0, 0]
};
},
computed: {
atTrackStyle () {
const { aX, bX } = this;
const smallerX = Math.min(aX, bX);
const deltaX = Math.abs(aX - bX);
return `left:${smallerX}%;width:${deltaX}%`;
},
range () {
let range = this.currentValue;
return this.type === 'time' ? range.map(item => this.formatHoursToUT(item)) : range;
}
},
methods: {
onTouchMove (event, sliderName) {
event = event.mp;
if (this.disabled) { return; }
const clientX = event.touches[0].clientX;
this.setSliderValue(sliderName, clientX - this.left, 'onChange');
},
onTouchEnd (sliderName) {
if (this.disabled) { return; }
this.currentSlider = sliderName;
this.triggerEvent('onAfterChange');
},
setSliderValue (sliderName, targetValue, funcName) {
const distance = Math.min(Math.max(targetValue, 0), this.width);
const sliderValue = Math.floor((distance / this.width) * 100);
if (funcName) {
this.triggerEvent(funcName);
}
if (sliderName === 'bX' && sliderValue <= this.aX) {
this[sliderName] = this.aX;
return;
}
if (sliderName === 'aX' && sliderValue >= this.bX) {
this[sliderName] = this.bX;
return;
}
this[sliderName] = sliderValue;
},
triggerEvent (funcName) {
const { aX, bX } = this;
const steps = Math.round(this.deltaValue / this.step);
const a = Math.round((aX / 100) * steps) * this.step + this.min;
const b = Math.round((bX / 100) * steps) * this.step + this.min;
const result = [a, b].sort((x, y) => x - y);
console.log(aX, bX, result, '位移');
this.currentValue = result;
this.$emit(funcName, result);
},
onClick (event) {
if (this.currentSlider && !this.disabled) {
let sliderValue = 0;
const detail = event.touches[0].clientX;
sliderValue = detail - this.left;
this.setSliderValue(this.currentSlider, sliderValue, 'onChange');
}
},
setValue (value) {
this.aX = Math.round(((value[0] - this.min) / this.deltaValue) * 100);
this.bX = Math.round(((value[1] - this.min) / this.deltaValue) * 100);
},
formatHoursToUT (value) {
value = value * 3600;
let result = parseInt(value);
let h = Math.floor(result / 3600) < 10 ? '0' + Math.floor(result / 3600) : Math.floor(result / 3600);
let m = Math.floor((result / 60 % 60)) < 10 ? '0' + Math.floor((result / 60 % 60)) : Math.floor((result / 60 % 60));
result = `${h}:${m}`;
return result;
},
getRect (selector, all) {
return new Promise(resolve => {
wx.createSelectorQuery()[all ? 'selectAll' : 'select'](selector)
.boundingClientRect(rect => {
if (all && Array.isArray(rect) && rect.length) {
resolve(rect);
}
if (!all && rect) {
resolve(rect);
}
})
.exec();
});
}
},
mounted () {
const { value } = this;
this.getRect('.range-slider')
.then(rect => {
console.log(rect, value);
this.width = Math.round(rect.width);
this.left = Math.round(rect.left);
this.setValue(value);
});
}
};
</script>
<style lang="less">
.range-slider {
width: 80%;
margin-left: 10%;
.range-slider-wrap {
position:relative;
background-color:#EFEFEF;
width: 100%;
height: 12rpx;
border-radius: 8rpx;
margin: 50rpx 0;
vertical-align: middle;
cursor: pointer;
}
.range-slider-bar {
position:absolute;
background-color:#FBBB00;
height: 12rpx;
}
.range-slider-button {
position:absolute;
top:50%;
transform:translate3d(-50%,-50%,0);
width: 40rpx;
height: 40rpx;
background-color:#fff;
border: 4rpx solid #DDDDDD;
box-shadow: 0 2rpx 8rpx 0 rgba(211,211,211,0.68);
border-radius: 20rpx;
& .range-slider-value {
position: absolute;
top: -70rpx;
left: 50%;
transform: translateX(-50%);
font-size: 40rpx;
color: #9EA4A1;
}
}
}
</style>