123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379 |
- import baseComponent from '../helpers/baseComponent'
- import classNames from '../helpers/libs/classNames'
- import shallowEqual from '../helpers/libs/shallowEqual'
- import styleToCssString from '../helpers/libs/styleToCssString'
- import fieldNamesBehavior from '../helpers/mixins/fieldNamesBehavior'
- import { getTouchPoints, getPointsNumber } from '../helpers/shared/gestures'
- import { vibrateShort } from '../helpers/hooks/useNativeAPI'
- import { 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({
- behaviors: [fieldNamesBehavior],
- properties: props,
- data: {
- inputValue: null,
- selectedIndex: null,
- selectedValue: null,
- cols: [],
- extIndicatorStyle: '',
- extItemStyle: '',
- extMaskStyle: '',
- contentStyle: '',
- 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 = this.getFieldNames()
- 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.getFieldNames()
- 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)
- const disabledName = this.getFieldName('disabled')
- if (child && !child[disabledName]) {
- 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 fieldNames = this.getFieldNames()
- const { cols: children } = 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 } = 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 valueName = this.getFieldName('value')
- const { itemHeight } = this.data
- const child = this.getChildMeta(top, itemHeight)
- if (child) {
- const inputValue = child[valueName]
- if (this.data.inputValue !== inputValue) {
- this.fireValueChange(inputValue)
- }
- }
- }
- },
- /**
- * 滚动数据选择变化后的回调函数
- */
- onScrollChange() {
- const top = this.scrollY
- if (top >= 0) {
- const valueName = this.getFieldName('value')
- const { cols: children, itemHeight } = 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[valueName])
- this.triggerEvent('scrollChange', values)
- }
- // 振动反馈
- vibrateShort()
- }
- }
- },
- /**
- * 数据选择变化后的回调函数
- */
- fireValueChange(value) {
- if (!this.data.controlled) {
- this.updated(value)
- }
- this.triggerEvent('valueChange', this.getValue(value))
- // 振动反馈
- vibrateShort()
- },
- },
- created() {
- 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 = this.getFieldNames()
- const cols = getRealCol(options, fieldNames)
-
- this.updatedStyles(itemHeight)
- this.setData({ cols })
- this.setValue(inputValue, true)
- },
- })
|