308 lines
6.5 KiB
Plaintext
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>
|