<template>
<view
class="u-slider"
@tap="onClick"
:class="[disabled ? 'u-slider--disabled' : '']"
:style="{
backgroundColor: inactiveColor
}"
>
<view
class="u-slider__gap"
:style="[
barStyle,
{
height: height + 'rpx',
backgroundColor: activeColor
}
]"
>
<view
class="u-slider__button-wrap"
@touchstart="onTouchStart"
@touchmove="onTouchMove"
@touchend="onTouchEnd"
@touchcancel="onTouchEnd"
>
<slot v-if="$slots.default" />
<view
v-else
class="u-slider__button"
:style="[blockStyle, {
height: blockWidth + 'rpx',
width: blockWidth + 'rpx'
}]"
></view>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'u-slider',
props: {
value: {
type: [Number, String],
default: 0
},
disabled: {
type: Boolean,
default: false
},
blockWidth: {
type: [Number, String],
default: 30
},
min: {
type: [Number, String],
default: 0
},
max: {
type: [Number, String],
default: 100
},
step: {
type: [Number, String],
default: 1
},
height: {
type: [Number, String],
default: 6
},
activeColor: {
type: String,
default: '#2979ff'
},
inactiveColor: {
type: String,
default: '#c0c4cc'
},
blockColor: {
type: String,
default: '#ffffff'
},
blockStyle: {
type: Object,
default() {
return {}
}
}
},
data() {
return {
startX: 0,
status: 'end',
newValue: 0,
distanceX: 0,
startValue: 0,
barStyle: {},
sliderRect: {
left: 0,
width: 0
}
}
},
watch: {
value(n) {
if (this.status == 'end') this.updateValue(this.value, false)
}
},
created() {
this.updateValue(this.value, false)
},
mounted() {
this.$uGetRect('.u-slider').then(rect => {
this.sliderRect = rect
})
},
methods: {
onTouchStart(event) {
if (this.disabled) return
this.startX = 0
let touches = event.touches[0]
this.startX = touches.clientX
this.startValue = this.format(this.value)
this.status = 'start'
},
onTouchMove(event) {
if (this.disabled) return
if (this.status == 'start') this.$emit('start')
let touches = event.touches[0]
this.distanceX = touches.clientX - this.sliderRect.left
this.newValue = (this.distanceX / this.sliderRect.width) * 100
this.status = 'moving'
this.$emit('moving')
this.updateValue(this.newValue, true)
},
onTouchEnd() {
if (this.disabled) return
if (this.status === 'moving') {
this.updateValue(this.newValue, false)
this.$emit('end')
}
this.status = 'end'
},
updateValue(value, drag) {
const width = this.format(value)
let barStyle = {
width: width + '%'
}
if (drag == true) {
barStyle.transition = 'none'
} else {
delete barStyle.transition
}
this.$emit('input', width)
this.barStyle = barStyle
},
format(value) {
return (
Math.round(Math.max(this.min, Math.min(value, this.max)) / this.step) *
this.step
)
},
onClick(event) {
if (this.disabled) return
const value =
((event.detail.x - this.sliderRect.left) / this.sliderRect.width) * 100
this.updateValue(value, false)
this.$emit('slideClick')
}
}
}
</script>
<style lang="scss" scoped>
@import '../../libs/css/style.components.scss';
.u-slider {
position: relative;
border-radius: 999px;
border-radius: 999px;
background-color: #ebedf0;
}
.u-slider:before {
position: absolute;
right: 0;
left: 0;
content: '';
top: -8px;
bottom: -8px;
z-index: -1;
}
.u-slider__gap {
position: relative;
border-radius: inherit;
transition: width 0.2s;
transition: width 0.2s;
background-color: #1989fa;
}
.u-slider__button {
width: 24px;
height: 24px;
border-radius: 50%;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
background-color: #fff;
cursor: pointer;
}
.u-slider__button-wrap {
position: absolute;
top: 50%;
right: 0;
transform: translate3d(50%, -50%, 0);
}
.u-slider--disabled {
opacity: 0.5;
}
</style>