123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255 |
- <template>
- <view
- class="tn-slider-class tn-slider"
- :class="{'tn-slider--disabled': disabled}"
- :style="{
- backgroundColor: inactiveColor
- }"
- @tap="click"
- >
- <!-- slider滑动线 -->
- <view
- class="tn-slider__gap"
- :style="[
- barStyle,
- {
- height: this.$t.string.getLengthUnitValue(lineHeight),
- backgroundColor: activeColor
- }
- ]"
- >
- <!-- slider滑块 -->
- <view
- class="tn-slider__button-wrap"
- @touchstart="touchStart"
- @touchmove="touchMove"
- @touchend="touchEnd"
- @touchcancel="touchEnd"
- >
- <view v-if="$slots.default || $slots.$default">
- <slot></slot>
- </view>
- <view
- v-else
- class="tn-slider__button"
- :style="[blockStyle, {
- height: this.$t.string.getLengthUnitValue(blockWidth),
- width: this.$t.string.getLengthUnitValue(blockWidth),
- backgroundColor: blockColor
- }]"
- ></view>
- </view>
- </view>
- </view>
- </template>
- <script>
- export default {
- name: 'tn-slider',
- props: {
- // 进度值
- value: {
- type: [Number, String],
- default: 0
- },
- // 最小值
- min: {
- type: Number,
- default: 0
- },
- // 最大值
- max: {
- type: Number,
- default: 100
- },
- // 步进值
- step: {
- type: Number,
- default: 1
- },
- // 禁用
- disabled: {
- type: Boolean,
- default: false
- },
- // 滑块宽度
- blockWidth: {
- type: Number,
- default: 30
- },
- // 滑动条高度
- lineHeight: {
- type: Number,
- default: 8
- },
- // 滑动条激活的颜色
- activeColor: {
- type: String,
- default: '#01BEFF'
- },
- // 滑动条未被激活的颜色
- inactiveColor: {
- type: String,
- default: '#E6E6E6'
- },
- // 滑块的颜色
- 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(val) {
- // 只有在非滑动状态时,才可以通过value更新滑块值,这里监听,是为了让用户触发
- if (this.status === 'end') this.updateValue(val, false)
- }
- },
- created() {
- this.updateValue(this.value, false)
- },
- mounted() {
- this._tGetRect('.tn-slider').then(res => {
- this.sliderRect = res
- })
- },
- methods: {
- // 开始滑动
- touchStart(event) {
- if (this.disabled) return
- if (!event.changedTouches[0]) return
-
- this.startX = 0
- // 触摸点
- this.startX = event.changedTouches[0].pageX
- this.startValue = this.format(this.value)
-
- // 标识当前开始触摸
- this.status = 'start'
- },
- // 滑动移动中
- touchMove(event) {
- if (this.disabled) return
- if (!event.changedTouches[0]) return
-
- // 连续触摸的过程会一直触发本方法,但只有手指触发且移动了才被认为是拖动了,才发出事件
- // 触摸后第一次移动已经将status设置为moving状态,故触摸第二次移动不会触发本事件
- if (this.status === 'start') this.$emit('start')
- let movePageX = event.changedTouches[0].pageX
- // 滑块的左边不一定跟屏幕左边接壤,所以需要减去最外层父元素的左边值
- this.distanceX = movePageX - this.sliderRect.left
- // 获得移动距离对整个滑块的百分比值,此为带有多位小数的值,不能用此更新视图
- // 否则造成通信阻塞,需要每改变一个step值时修改一次视图
- this.newValue = ((this.distanceX / this.sliderRect.width) * (this.max - this.min)) + this.min
- this.status = 'moving'
- this.$emit('moving')
-
- this.updateValue(this.newValue, true)
- },
- // 滑动结束
- touchEnd() {
- if(this.disabled) return
- if (this.status === 'moving') {
- this.updateValue(this.newValue, false)
- this.$emit('end')
- }
- this.status = 'end'
- },
- // 更新数值
- updateValue(value, drag) {
- // 去掉小数部分,对step进行步进处理
- value = this.format(value)
- const width = Math.round((value - this.min) / (this.max - this.min) * 100)
- // 不允许滑动的距离小于0和超过100
- if (width < 0 || width > 100) return
- // 设置移动的百分比
- let barStyle = {
- width: width + '%'
- }
- // 移动期间取消动画
- if (drag === true) {
- barStyle.transition = 'none'
- } else {
- // 非移动期间,删掉对过渡为空的声明,让css中的声明起效
- delete barStyle.transition
- }
-
- // 修改value值
- this.$emit('input', value)
- this.barStyle = barStyle
- },
- // 点击事件
- click(event) {
- if (this.disabled) return
- // 直接点击的情况,计算方式和touchMove方法一致
- const value = (((event.detail.x - this.sliderRect.left) / this.sliderRect.width) * (this.max - this.min)) + this.min
- this.updateValue(value, false)
- },
- // 格式化滑动的值
- format(value) {
- return Math.round(Math.max(this.min, Math.min(value, this.max)) / this.step) * this.step
- }
- }
- }
- </script>
- <style lang="scss" scoped>
- .tn-slider {
- width: 100%;
- position: relative;
- border-radius: 1000rpx;
- // 增加点击的范围
- border-width: 20rpx;
- border-style: solid;
- border-color: transparent;
- background-color: $tn-font-holder-color;
- background-clip: content-box;
-
- &__gap {
- position: relative;
- border-radius: inherit;
- transition: width 0.2s;
- background-color: #01BEFF;
- }
-
- &__button {
- width: 30rpx;
- height: 30rpx;
- border-radius: 50%;
- box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.6);
- background-color: #FFFFFF;
- cursor: pointer;
-
- &-wrap {
- position: absolute;
- top: 50%;
- right: 0;
- transform: translate3d(50%, -50%, 0);
- }
- }
-
- &--disabled {
- opacity: 0.6;
- }
- }
- </style>
|