@minix-iot/ui
Version:
基于Vue的移动端轻量级UI组件库
283 lines (263 loc) • 6.19 kB
JSX
import { Touch } from '../../utils/touch'
import { DOMExtension } from '../../utils/extensions/dom'
import { MathExtension } from '../../utils/extensions/math'
import { Namespace } from "../../utils/namespace"
const namespace = Namespace.Create("slider")
export default {
name: namespace.name,
props: {
linear: {
type: Array,
default: () => []
},
value: {
type: Number,
default: 0
},
min: {
type: Number,
default: 0
},
max: {
type: Number,
default: 100
},
step: {
type: Number,
default: 1
},
trackHeight: {
type: Number,
default: -1
},
buttonSize: {
type: Number,
default: -1
},
trackColor: {
type: String
},
barColor: {
type: String
},
scaleColor: {
type: String
},
buttonColor: {
type: String
},
theme: {
type: String,
default: 'brand'
},
readonly: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
}
},
model: {
prop: 'value',
event: 'input'
},
data () {
return {
touch: Touch.Create(),
current: 0,
startValue: 0,
dragStatus: ''
}
},
computed: {
wrapperCls () {
return namespace.derive('wrapper', {
disabled: this.disabled
})
},
cls () {
return namespace.derive()
},
trackCls () {
return namespace.derive('track')
},
barCls () {
return namespace.derive('bar', [
this.theme
])
},
buttonCls () {
return namespace.derive('button', [
this.theme
])
},
scaleCls () {
return namespace.derive('scale-wrapper')
},
scaleTextCls () {
return namespace.derive('scale-text')
},
sliderStyle () {
const style = {}
if (this.buttonSize > 0) {
style.height = `${this.buttonSize}px`
}
return style
},
trackStyle () {
const style = {}
if (this.trackColor) {
style.background = this.trackColor
}
if (this.trackHeight) {
style.height = `${this.trackHeight}px`
}
return style
},
barStyle () {
const style = {
width: `${((this.value - this.min) * 100) / this.scope}%`
}
if (this.dragStatus) {
style.transition = 'none'
}
if (this.linear.length > 0) {
style.background = `linear-gradient(to right, ${this.linear.join(',')})`
}
if (this.barColor) {
style.background = this.barColor
}
return style
},
buttonStyle () {
const style = {}
if (this.linear.length > 0) {
style.background = this.linear[this.linear.length - 1]
}
if (this.buttonColor) {
style.background = this.buttonColor
}
if (this.buttonSize > 0) {
style.width = `${this.buttonSize}px`
style.height = `${this.buttonSize}px`
style.borderRadius = `${this.buttonSize / 2} px`
}
return style
},
scaleStyle () {
const style = {}
if (this.scaleColor) {
style.color = this.scaleColor
}
return style
},
canInteract () {
return !this.disabled && !this.readonly
},
scope () {
return this.max - this.min
}
},
methods: {
onClick (e) {
e.stopPropagation()
if (!this.canInteract) {
return
}
const rect = this.$refs.slider.getBoundingClientRect()
const delta = e.clientX - rect.left
const total = rect.width
const value = this.min + (delta / total) * this.scope
this.update(value, true)
},
onTouchStart (e) {
if (!this.canInteract) {
return
}
this.touch.start(e)
this.current = this.value
this.startValue = this.format(this.current)
this.dragStatus = 'start'
},
onTouchMove (e) {
if (!this.canInteract) {
return
}
if (this.dragStatus === 'start') {
this.$emit('dragstart', e)
}
DOMExtension.PreventDefault(e)
this.touch.move(e)
this.dragStatus = 'draging'
const rect = this.$refs.slider.getBoundingClientRect()
const delta = this.touch.deltaX
const total = rect.width
const diff = (delta / total) * this.scope
this.current = this.startValue + diff
this.update(this.current)
},
onTouchEnd (e) {
if (!this.canInteract) {
return
}
if (this.dragStatus === 'draging') {
this.update(this.current, true)
this.$emit('dragend', e)
}
this.dragStatus = ''
},
format (value) {
const min = +this.min
const max = +this.max
const step = + this.step
const val = MathExtension.Clamp(value, min, max)
const diff = Math.round((val - min) / step) * step
return min + diff
},
update (value, end = false) {
const val = this.format(value)
if (val !== this.value) {
this.$emit('input', val)
}
if (end && val !== this.startValue) {
this.$emit('change', val)
}
}
},
render () {
const renderSlider = () => (
<div ref={'slider'} class={this.cls} style={this.sliderStyle} onClick={this.onClick}>
<div class={this.trackCls} style={this.trackStyle}>
<div class={this.barCls} style={this.barStyle}>
<div class={this.buttonCls}
style={this.buttonStyle}
onTouchstart={this.onTouchStart}
onTouchmove={this.onTouchMove}
onTouchend={this.onTouchEnd}
onTouchcancel={this.onTouchEnd}
onClick={e => e.stopPropagation()}
>
</div>
</div>
</div>
</div>
)
const renderScale = () => {
if (this.$slots.default) {
return (
<div class={this.scaleCls} style={this.scaleStyle}>
{this.$slots.default}
</div>
)
}
}
return (
<div class={this.wrapperCls}>
{renderSlider()}
{renderScale()}
</div>
)
}
}