Files
medicine_app/components/player/CustomSlider.nvue
2024-06-19 16:12:23 +08:00

308 lines
6.5 KiB
Plaintext

<template>
<view class="custom-slider">
<image
v-if="minIcon"
class="icon"
:src="minIcon"></image>
<view
ref="slider"
class="slider"
@touchstart.stop="onTouchStart"
@touchmove.stop="onTouchMove"
@touchend.stop="onTouchEnd">
<view class="bar">
<slot></slot>
</view>
<view
class="active-bar-wrapper">
<view
class="active-bar"
:style="{flex: presentShow}"></view>
</view>
<view v-if="step !== 1" class="steps-wrapper">
<template v-for="(step, index) in steps">
<view v-if="index" class="step"></view>
<view class="holder-bar"></view>
</template>
</view>
<view
class="dot-wrapper">
<view
class="holder-bar"
:style="{flex: presentShow}"></view>
<view class="dot"></view>
</view>
<view class="know-area">
<slot name="know"></slot>
</view>
</view>
<image
v-if="maxIcon"
class="icon"
:src="maxIcon"></image>
</view>
</template>
<script>
// 注意平台差异
// #ifdef APP-NVUE
const dom = weex.requireModule('dom')
// #endif
import { range, addNumber } from '@/utils/formate.js';
export default {
data() {
return {
dragStatus: '',
size: {
width: 0,
height: 0,
top: 0,
bottom: 0,
left: 0,
right: 0
},
currentValue: 0
}
},
props: {
disabled: Boolean,
vertical: {
type: Boolean,
default: false
},
barHeight: [Number, String],
buttonSize: [Number, String],
activeColor: String,
inactiveColor: String,
min: {
type: Number,
default: 0,
},
minIcon: {
type: String,
default: ''
},
max: {
type: Number,
default: 100,
},
maxIcon: {
type: String,
default: ''
},
step: {
type: Number,
default: 1,
},
value: {
type: Number,
default: 0,
},
isDOMShow: {
type: Boolean,
default: false
}
},
computed: {
scope() {
return this.max - this.min;
},
presentShow () {
return (this.currentValue - this.min) / this.scope;
},
steps () {
return this.scope / this.step
}
},
watch: {
isDOMShow (value) {
this.getSliderSize();
},
value (val) {
this.currentValue = val;
}
},
created() {
// 同步初始值
this.currentValue = this.value;
},
mounted () {
this.getSliderSize();
},
methods: {
touchStart(event) {
this.resetTouchStatus();
this.startX = event.touches[0].screenX;
this.startY = event.touches[0].screenY;
},
touchMove(event) {
const touch = event.touches[0];
// safari back will set clientX to negative number
this.deltaX = touch.screenX < 0 ? 0 : touch.screenX - this.startX;
this.deltaY = touch.screenY - this.startY;
this.offsetX = Math.abs(this.deltaX);
this.offsetY = Math.abs(this.deltaY);
// lock direction when distance is greater than a certain value
const LOCK_DIRECTION_DISTANCE = 10;
if (
!this.direction ||
(this.offsetX < LOCK_DIRECTION_DISTANCE &&
this.offsetY < LOCK_DIRECTION_DISTANCE)
) {
this.direction = this.getDirection(this.offsetX, this.offsetY);
}
},
resetTouchStatus() {
this.direction = '';
this.deltaX = 0;
this.deltaY = 0;
this.offsetX = 0;
this.offsetY = 0;
},
getDirection (x, y) {
if (x > y) {
return 'horizontal';
}
if (y > x) {
return 'vertical';
}
return '';
},
onTouchStart(event) {
console.log('start')
this.touchStart(event);
this.startValue = this.format(this.currentValue);
this.dragStatus = 'start';
this.$emit('dragStart');
},
onTouchMove(event) {
this.touchMove(event);
const rect = this.size;
const delta = this.vertical ? this.deltaY : this.deltaX;
const total = this.vertical ? rect.height : rect.width;
const diff = (delta / total) * this.scope;
// console.log('move', delta);
if (delta === 0) return;
this.dragStatus = 'draging';
this.currentValue = this.startValue + diff;
this.updateValue(this.currentValue, true);
},
onTouchEnd(event) {
console.log('end')
if(this.dragStatus === 'draging') {
this.updateValue(this.currentValue, true);
return;
}
if (this.dragStatus === 'start') {
this.onClick(event);
};
},
onClick(event) {
if (this.disabled) return;
const rect = this.size;
const delta = this.vertical
? event.changedTouches[0].screenY - rect.top
: event.changedTouches[0].screenX - rect.left;
const total = this.vertical ? rect.height : rect.width;
let value = +this.min + (delta / total) * this.scope;
// this.startValue = this.value;
this.updateValue(value, true);
},
updateValue(value, end) {
value = this.format(value);
this.currentValue = value;
this.$emit('changing', value);
if (end) {
this.$emit('change', value);
}
},
format(value) {
const min = +this.min;
const max = +this.max;
const step = +this.step;
value = range(value, min, max);
const diff = parseInt((value - min) / step) * step;
return addNumber(min, diff);
},
isSameValue(newValue, oldValue) {
return JSON.stringify(newValue) === JSON.stringify(oldValue);
},
getSliderSize () {
setTimeout(()=> {
const result = dom.getComponentRect(this.$refs.slider, option => {
this.size = option.size;
})
}, 1000);
}
}
}
</script>
<style lang="stylus" scoped>
.custom-slider
flex-direction row
align-items center
justify-content center
.icon
width 40rpx
height 40rpx
.slider
position relative
margin 0 15rpx
height 78rpx
flex-direction row
align-items center
flex 1
.bar
flex 1
flex-direction row
height 6rpx
background-color rgba(153, 153, 153, 0.4)
.active-bar-wrapper
position absolute
top 0
right 0
bottom 0
left 0
flex-direction row
align-items center
.active-bar
height 6rpx
background-color #FF920A
.steps-wrapper
position absolute
top 0
right 0
bottom 0
left 0
flex-direction row
align-items center
.step
width 6rpx
height 6rpx
background-color #FFFFFF
.holder-bar
flex 1
height: 6rpx
.dot-wrapper
position absolute
top 0
right 0
bottom 0
left 0
flex-direction row
align-items center
.holder-bar
height 6rpx
.dot
width 28rpx
height 28rpx
background-color #FFFFFF
border-radius 100%
.know-area
position absolute
top 0
right 0
bottom 0
left 0
</style>