榆山数据端小程序
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.
 
 

381 lines
12 KiB

import baseComponent from '../helpers/baseComponent'
import classNames from '../helpers/classNames'
import shallowEqual from '../helpers/shallowEqual'
import styleToCssString from '../helpers/styleToCssString'
import { getTouchPoints, getPointsNumber } from '../helpers/gestures'
import { getSystemInfo } from '../helpers/checkIPhoneX'
import { defaultFieldNames, props } from './props'
import {
getRealCol,
getRealValue,
getIndexFromValue,
getLabelFromIndex,
} from './utils'
function getStyles(value) {
return Array.isArray(value) ? value.map((n) => styleToCssString(n)) : styleToCssString(value)
}
baseComponent({
properties: props,
data: {
inputValue: null,
selectedIndex: null,
selectedValue: null,
cols: [],
extIndicatorStyle: '',
extItemStyle: '',
extMaskStyle: '',
contentStyle: '',
fieldNames: defaultFieldNames,
itemCount: 7, // 默认显示的子元素个数
styles: {},
},
computed: {
classes: ['prefixCls, labelAlign', function(prefixCls, labelAlign) {
const wrap = classNames(prefixCls, {
[`${prefixCls}--${labelAlign}`]: labelAlign,
})
const mask = `${prefixCls}__mask`
const indicator = `${prefixCls}__indicator`
const content = `${prefixCls}__content`
const item = `${prefixCls}__item`
return {
wrap,
mask,
indicator,
content,
item,
}
}],
},
observers: {
itemHeight(newVal) {
this.updatedStyles(newVal)
},
itemStyle(newVal) {
this.setData({
extItemStyle: getStyles(newVal),
})
},
indicatorStyle(newVal) {
this.setData({
extIndicatorStyle: getStyles(newVal),
})
},
maskStyle(newVal) {
this.setData({
extMaskStyle: getStyles(newVal),
})
},
['value, options'](value, options) {
const { controlled } = this.data
const fieldNames = Object.assign({}, defaultFieldNames, this.data.defaultFieldNames)
const cols = getRealCol(options, fieldNames)
if (!shallowEqual(this.data.cols, cols)) {
this.setData({ cols })
}
if (controlled) {
this.setValue(value, true)
}
},
inputValue(newVal) {
const {
selectedIndex,
selectedValue,
} = this.getValue(newVal)
this.setData({
selectedIndex,
selectedValue,
})
},
},
methods: {
updatedStyles(itemHeight) {
let num = this.data.itemCount
if (num % 2 === 0) {
num--
}
num--
num /= 2
const wrap = `height: ${itemHeight * this.data.itemCount}px;`
const item = `line-height: ${itemHeight}px; height: ${itemHeight}px;`
const content = `padding: ${itemHeight * num}px 0;`
const indicator = `top: ${itemHeight * num}px; height: ${itemHeight}px;`
const mask = `background-size: 100% ${itemHeight * num}px;`
const styles = {
wrap,
item,
content,
indicator,
mask,
}
this.setData({ styles })
},
updated(inputValue, isForce) {
if (this.data.inputValue !== inputValue || isForce) {
this.setData({
inputValue,
})
}
// 设置选择器位置
if (isForce) {
this.select(inputValue, this.data.itemHeight, (y) => this.scrollTo(y, 0, false))
}
},
setValue(value, isForce) {
const { value: inputValue } = this.getValue(value)
this.updated(inputValue, isForce)
},
getValue(value = this.data.inputValue, cols = this.data.cols) {
const { fieldNames } = this.data
const inputValue = getRealValue(value, cols, fieldNames) || null
const selectedValue = inputValue
const selectedIndex = getIndexFromValue(value, cols, fieldNames)
const displayValue = getLabelFromIndex(selectedIndex, cols, fieldNames.label)
return {
value: inputValue,
displayValue,
selectedIndex,
selectedValue,
cols,
}
},
/**
* 设置选择器的位置信息
*/
scrollTo(y, time = .3, runCallbacks = true) {
if (this.scrollY !== y) {
if (this.runCallbacks) {
clearTimeout(this.runCallbacks)
this.runCallbacks = null
}
this.scrollY = y
this.setTransform(-y, time, () => {
runCallbacks && (this.runCallbacks = setTimeout(() => {
this.setTransform(-y, 0, this.scrollingComplete)
}, +time * 1000))
})
}
},
/**
* 滚动结束时的回调函数
*/
onFinish() {
this.isMoving = false
let targetY = this.scrollY
const { cols, itemHeight } = this.data
const height = (cols.length - 1) * itemHeight
let time = .3
// const velocity = this.Velocity.getVelocity(targetY) * 4
// if (velocity) {
// targetY = velocity * 40 + targetY
// time = Math.abs(velocity) * .1
// time = parseFloat(time.toFixed(2))
// }
if (targetY % itemHeight !== 0) {
targetY = Math.round(targetY / itemHeight) * itemHeight
}
if (targetY < 0) {
targetY = 0
} else if (targetY > height) {
targetY = height
}
// check disabled & reset
const child = this.getChildMeta(targetY, itemHeight)
if (child && !child.disabled) {
this.scrollTo(targetY, time < .3 ? .3 : time)
} else {
this.select(this.data.inputValue, itemHeight, (y) => this.scrollTo(y, 0, false))
}
this.onScrollChange()
},
/**
* 手指触摸动作开始
*/
onTouchStart(e) {
if (getPointsNumber(e) > 1) return
this.isMoving = true
this.startY = getTouchPoints(e).y
this.lastY = this.scrollY
this.triggerEvent('beforeChange', this.getValue())
},
/**
* 手指触摸后移动
*/
onTouchMove(e) {
if (!this.isMoving || getPointsNumber(e) > 1) return
this.scrollY = this.lastY - getTouchPoints(e).y + this.startY
this.setTransform(-this.scrollY, false, this.onScrollChange)
// this.Velocity.record(this.scrollY)
},
/**
* 手指触摸动作结束
*/
onTouchEnd(e) {
if (getPointsNumber(e) > 1) return
this.onFinish()
},
/**
* 手指触摸后马上离开
*/
onItemClick(e) {
const { index, disabled } = e.currentTarget.dataset
if (!disabled) {
this.scrollTo(index * this.data.itemHeight)
}
},
/**
* 设置滚动样式
*/
setTransform(y, time, cb) {
const contentStyle = {
transform: `translate3d(0,${y}px,0)`,
transition: time ? `cubic-bezier(0, 0, 0.2, 1.15) ${time}s` : 'none',
}
this.setData({ contentStyle: styleToCssString(contentStyle) }, cb)
},
/**
* 设置选择器
*/
select(value, itemHeight, scrollTo) {
const { cols: children, fieldNames } = this.data
const index = getIndexFromValue(value, children, fieldNames)
this.selectByIndex(index, itemHeight, scrollTo)
},
/**
* 通过元素的索引值设置选择器
*/
selectByIndex(index, itemHeight, zscrollTo) {
if (index < 0 || index >= this.data.cols.length || !itemHeight) return
zscrollTo.call(this, index * itemHeight)
},
/**
* 计算子元素的索引值
*/
computeChildIndex(top, itemHeight, childrenLength) {
const index = Math.round(top / itemHeight)
return Math.min(index, childrenLength - 1)
},
/**
* 获取子元素的属性
*/
getChildMeta(top, itemHeight) {
const { cols: children, fieldNames } = this.data
const index = this.computeChildIndex(top, itemHeight, children.length)
const child = children[index]
return child
},
/**
* 滚动完成的回调函数
*/
scrollingComplete() {
const top = this.scrollY
if (top >= 0) {
const { itemHeight, fieldNames } = this.data
const child = this.getChildMeta(top, itemHeight)
if (child) {
const inputValue = child[fieldNames.value]
if (this.data.inputValue !== inputValue) {
this.fireValueChange(inputValue)
}
}
}
},
/**
* 滚动数据选择变化后的回调函数
*/
onScrollChange() {
const top = this.scrollY
if (top >= 0) {
const { cols: children, itemHeight, fieldNames } = this.data
const index = this.computeChildIndex(top, itemHeight, children.length)
if (this.scrollValue !== index) {
this.scrollValue = index
const child = children[index]
if (child) {
const values = this.getValue(child[fieldNames.value])
this.triggerEvent('scrollChange', values)
}
// 振动反馈
this.vibrateShort()
}
}
},
/**
* 数据选择变化后的回调函数
*/
fireValueChange(value) {
if (!this.data.controlled) {
this.updated(value)
}
this.triggerEvent('valueChange', this.getValue(value))
// 振动反馈
this.vibrateShort()
},
},
created() {
const systemInfo = getSystemInfo()
this.vibrateShort = () => {
if (systemInfo.platform !== 'devtools') {
wx.vibrateShort()
}
}
this.scrollValue = undefined
this.scrollY = -1
this.lastY = 0
this.startY = 0
this.isMoving = false
// this.Velocity = ((minInterval = 30, maxInterval = 100) => {
// let _time = 0
// let _y = 0
// let _velocity = 0
// const recorder = {
// record: (y) => {
// const now = +new Date()
// _velocity = (y - _y) / (now - _time)
// if (now - _time >= minInterval) {
// _velocity = now - _time <= maxInterval ? _velocity : 0
// _y = y
// _time = now
// }
// },
// getVelocity: (y) => {
// if (y !== _y) {
// recorder.record(y)
// }
// return _velocity
// },
// }
// return recorder
// })()
},
attached() {
const { defaultValue, value, controlled, options, itemHeight } = this.data
const inputValue = controlled ? value : defaultValue
const fieldNames = Object.assign({}, defaultFieldNames, this.data.defaultFieldNames)
const cols = getRealCol(options, fieldNames)
this.updatedStyles(itemHeight)
this.setData({ cols, fieldNames })
this.setValue(inputValue, true)
},
})