You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
207 lines
6.9 KiB
207 lines
6.9 KiB
import { VantComponent } from '../common/component'; |
|
import { touch } from '../mixins/touch'; |
|
import { canIUseModel } from '../common/version'; |
|
import { getRect, addUnit, nextTick, addNumber, clamp } from '../common/utils'; |
|
const DRAG_STATUS = { |
|
START: 'start', |
|
MOVING: 'moving', |
|
END: 'end', |
|
}; |
|
VantComponent({ |
|
mixins: [touch], |
|
props: { |
|
range: Boolean, |
|
disabled: Boolean, |
|
useButtonSlot: Boolean, |
|
activeColor: String, |
|
inactiveColor: String, |
|
max: { |
|
type: Number, |
|
value: 100, |
|
}, |
|
min: { |
|
type: Number, |
|
value: 0, |
|
}, |
|
step: { |
|
type: Number, |
|
value: 1, |
|
}, |
|
value: { |
|
type: null, |
|
value: 0, |
|
observer(val) { |
|
if (val !== this.value) { |
|
this.updateValue(val); |
|
} |
|
}, |
|
}, |
|
vertical: Boolean, |
|
barHeight: null, |
|
}, |
|
created() { |
|
this.updateValue(this.data.value); |
|
}, |
|
methods: { |
|
onTouchStart(event) { |
|
if (this.data.disabled) |
|
return; |
|
const { index } = event.currentTarget.dataset; |
|
if (typeof index === 'number') { |
|
this.buttonIndex = index; |
|
} |
|
this.touchStart(event); |
|
this.startValue = this.format(this.value); |
|
this.newValue = this.value; |
|
if (this.isRange(this.newValue)) { |
|
this.startValue = this.newValue.map((val) => this.format(val)); |
|
} |
|
else { |
|
this.startValue = this.format(this.newValue); |
|
} |
|
this.dragStatus = DRAG_STATUS.START; |
|
}, |
|
onTouchMove(event) { |
|
if (this.data.disabled) |
|
return; |
|
if (this.dragStatus === DRAG_STATUS.START) { |
|
this.$emit('drag-start'); |
|
} |
|
this.touchMove(event); |
|
this.dragStatus = DRAG_STATUS.MOVING; |
|
getRect(this, '.van-slider').then((rect) => { |
|
const { vertical } = this.data; |
|
const delta = vertical ? this.deltaY : this.deltaX; |
|
const total = vertical ? rect.height : rect.width; |
|
const diff = (delta / total) * this.getRange(); |
|
if (this.isRange(this.startValue)) { |
|
this.newValue[this.buttonIndex] = |
|
this.startValue[this.buttonIndex] + diff; |
|
} |
|
else { |
|
this.newValue = this.startValue + diff; |
|
} |
|
this.updateValue(this.newValue, false, true); |
|
}); |
|
}, |
|
onTouchEnd() { |
|
if (this.data.disabled) |
|
return; |
|
if (this.dragStatus === DRAG_STATUS.MOVING) { |
|
this.dragStatus = DRAG_STATUS.END; |
|
nextTick(() => { |
|
this.updateValue(this.newValue, true); |
|
this.$emit('drag-end'); |
|
}); |
|
} |
|
}, |
|
onClick(event) { |
|
if (this.data.disabled) |
|
return; |
|
const { min } = this.data; |
|
getRect(this, '.van-slider').then((rect) => { |
|
const { vertical } = this.data; |
|
const touch = event.touches[0]; |
|
const delta = vertical |
|
? touch.clientY - rect.top |
|
: touch.clientX - rect.left; |
|
const total = vertical ? rect.height : rect.width; |
|
const value = Number(min) + (delta / total) * this.getRange(); |
|
if (this.isRange(this.value)) { |
|
const [left, right] = this.value; |
|
const middle = (left + right) / 2; |
|
if (value <= middle) { |
|
this.updateValue([value, right], true); |
|
} |
|
else { |
|
this.updateValue([left, value], true); |
|
} |
|
} |
|
else { |
|
this.updateValue(value, true); |
|
} |
|
}); |
|
}, |
|
isRange(val) { |
|
const { range } = this.data; |
|
return range && Array.isArray(val); |
|
}, |
|
handleOverlap(value) { |
|
if (value[0] > value[1]) { |
|
return value.slice(0).reverse(); |
|
} |
|
return value; |
|
}, |
|
updateValue(value, end, drag) { |
|
if (this.isRange(value)) { |
|
value = this.handleOverlap(value).map((val) => this.format(val)); |
|
} |
|
else { |
|
value = this.format(value); |
|
} |
|
this.value = value; |
|
const { vertical } = this.data; |
|
const mainAxis = vertical ? 'height' : 'width'; |
|
this.setData({ |
|
wrapperStyle: ` |
|
background: ${this.data.inactiveColor || ''}; |
|
${vertical ? 'width' : 'height'}: ${addUnit(this.data.barHeight) || ''}; |
|
`, |
|
barStyle: ` |
|
${mainAxis}: ${this.calcMainAxis()}; |
|
left: ${vertical ? 0 : this.calcOffset()}; |
|
top: ${vertical ? this.calcOffset() : 0}; |
|
${drag ? 'transition: none;' : ''} |
|
`, |
|
}); |
|
if (drag) { |
|
this.$emit('drag', { value }); |
|
} |
|
if (end) { |
|
this.$emit('change', value); |
|
} |
|
if ((drag || end) && canIUseModel()) { |
|
this.setData({ value }); |
|
} |
|
}, |
|
getScope() { |
|
return Number(this.data.max) - Number(this.data.min); |
|
}, |
|
getRange() { |
|
const { max, min } = this.data; |
|
return max - min; |
|
}, |
|
getOffsetWidth(current, min) { |
|
const scope = this.getScope(); |
|
// 避免最小值小于最小step时出现负数情况 |
|
return `${Math.max(((current - min) * 100) / scope, 0)}%`; |
|
}, |
|
// 计算选中条的长度百分比 |
|
calcMainAxis() { |
|
const { value } = this; |
|
const { min } = this.data; |
|
if (this.isRange(value)) { |
|
return this.getOffsetWidth(value[1], value[0]); |
|
} |
|
return this.getOffsetWidth(value, Number(min)); |
|
}, |
|
// 计算选中条的开始位置的偏移量 |
|
calcOffset() { |
|
const { value } = this; |
|
const { min } = this.data; |
|
const scope = this.getScope(); |
|
if (this.isRange(value)) { |
|
return `${((value[0] - Number(min)) * 100) / scope}%`; |
|
} |
|
return '0%'; |
|
}, |
|
format(value) { |
|
const min = +this.data.min; |
|
const max = +this.data.max; |
|
const step = +this.data.step; |
|
value = clamp(value, min, max); |
|
const diff = Math.round((value - min) / step) * step; |
|
return addNumber(min, diff); |
|
}, |
|
}, |
|
});
|
|
|