UNPKG

wux-weapp

Version:

一套组件化、可复用、易扩展的微信小程序 UI 组件库

290 lines (270 loc) 9.49 kB
import baseComponent from '../helpers/baseComponent' import classNames from '../helpers/libs/classNames' import warning from '../helpers/libs/warning' import styleToCssString from '../helpers/libs/styleToCssString' import { vibrateShort } from '../helpers/hooks/useNativeAPI' import { useRectAll } from '../helpers/hooks/useDOM' const findActiveByIndex = (current, currentName, children) => { return children.filter((child) => ( child.index === current && child.name === currentName ))[0] } const findActiveByPosition = (scrollTop, offsetY, children) => { return children.filter((child) => ( scrollTop < (child.top + child.height - offsetY) && scrollTop >= (child.top - offsetY) ))[0] } baseComponent({ useExport: true, relations: { '../index-item/index': { type: 'child', observer() { this.callDebounceFn(this.updated) }, }, }, properties: { prefixCls: { type: String, value: 'wux-index', }, height: { type: [String, Number], value: 300, observer: 'updateStyle', }, showIndicator: { type: Boolean, value: true, }, indicatorPosition: { type: String, value: 'center', }, parentOffsetTop: { type: Number, value: 0, }, }, data: { colHight: 0, points: [], scrollTop: 0, children: [], moving: false, current: 0, currentName: '', currentBrief: '', extStyle: '', indicatorStyle: '', }, computed: { classes: ['prefixCls, indicatorPosition', function(prefixCls, indicatorPosition) { const wrap = classNames(prefixCls) const nav = `${prefixCls}__nav` const navRow = `${prefixCls}__nav-row` const navCol = `${prefixCls}__nav-col` const navItem = `${prefixCls}__nav-item` const indicator = classNames(`${prefixCls}__indicator`, { [`${prefixCls}__indicator--${indicatorPosition}`]: indicatorPosition, }) return { wrap, nav, navRow, navCol, navItem, indicator, } }], }, methods: { /** * 更新样式 */ updateStyle(height = this.data.height) { const extStyle = styleToCssString({ height }) if (extStyle !== this.data.extStyle) { this.setData({ extStyle, }) } }, /** * 更新元素 */ updated() { const elements = this.getRelationsByName('../index-item/index') if (elements.length > 0) { elements.forEach((element, index) => { element.updated(index) }) // HACK: https://github.com/wux-weapp/wux-weapp/issues/224 setTimeout(this.getNavPoints.bind(this)) } this.updateChildren() }, /** * 设置当前激活的元素 */ setActive(current, currentName) { if (current !== this.data.current || currentName !== this.data.currentName) { const target = findActiveByIndex(current, currentName, this.data.children) const currentBrief = target !== undefined ? target.brief : currentName.charAt(0) if (target !== undefined) { const { colHight, indicatorPosition } = this.data const indicatorStyle = indicatorPosition === 'right' ? styleToCssString({ top: current * colHight + colHight / 2 }) : '' this.setData({ current, currentName, currentBrief, scrollTop: target.top - this.data.parentOffsetTop, indicatorStyle, }) } // 振动反馈 vibrateShort() this.triggerEvent('change', { index: current, name: currentName, brief: currentBrief }) } }, /** * 手指触摸动作开始 */ onTouchStart(e) { if (this.data.moving) return const { index, name } = e.target.dataset this.setActive(index, name) this.setData({ moving: true }) }, /** * 手指触摸后移动 */ onTouchMove(e) { const target = this.getTargetFromPoint(e.changedTouches[0].pageY) if (target !== undefined) { const { index, name } = target.dataset this.setActive(index, name) } }, /** * 手指触摸动作结束 */ onTouchEnd() { if (!this.data.moving) return setTimeout(() => this.setData({ moving: false }), 300) }, /** * 滚动事件的回调函数 */ onScroll(e) { if (this.data.moving) return this.checkActiveIndex.call(this, this.data, e) }, /** * 获取右侧导航对应的坐标 */ getNavPoints() { const navColCls = `.${this.data.prefixCls}__nav-col` const navItemCls = `.${this.data.prefixCls}__nav-item` useRectAll([navColCls, navItemCls], this) .then(([cols, items]) => { if (!cols.length && !items.length) return this.setData({ colHight: cols[0].height, points: items.map((n) => ({ ...n, offsets: [n.top, n.top + n.height] })), }) }) }, /** * 根据坐标获得对应的元素 */ getTargetFromPoint(y) { const { points } = this.data let target for (let i = points.length - 1; i >= 0; i--) { const [a, b] = points[i].offsets // 1.判断是否为第一个元素且大于最大坐标点 // 2.判断是否为最后一个元素且小于最小坐标点 // 3.判断是否包含于某个坐标系内 if ((i === points.length - 1 && y > b) || (i === 0 && y < a) || (y >= a && y <= b)) { target = points[i] break } } return target }, getInternalHooks(key) { if (key === 'INDEX_HOOK_MARK') { return { updateChildren: this.updateChildren.bind(this), } } warning( false, '`getInternalHooks` is internal usage of the <index />. Should not call directly.' ) return null }, expose() { const scrollTo = (index) => { const { children } = this.data const child = typeof index === 'number' ? children.filter((child) => (child.index === index))[0] : children.filter((child) => (child.name === index))[0] if (child) { this.setData({ moving: true }) this.setActive(child.index, child.name) setTimeout(() => this.setData({ moving: false }), 300) } } return { scrollTo, getInternalHooks: this.getInternalHooks.bind(this), } }, }, created() { const { run: checkActiveIndex } = this.useThrottleFn((data, event) => { const target = findActiveByPosition(event.detail.scrollTop, data.parentOffsetTop, data.children) if (target !== undefined) { const current = target.index const currentName = target.name const currentBrief = target.brief if (current !== data.current || currentName !== data.currentName) { this.setData({ current, currentName, currentBrief, }) this.triggerEvent('change', { index: current, name: currentName, brief: currentBrief }) } } }, 50, { trailing: true, leading: true }) this.checkActiveIndex = checkActiveIndex const propsFilter = (props) => ({ name: props.name, index: props.index, top: props.top, height: props.height, brief: props.brief, }) const { run: updateChildren } = this.useThrottleFn(() => { const nodeList = this.getRelationsByName('../index-item/index') const children = nodeList.map((node) => propsFilter(node.data)) this.setData({ children, }) }, 50, { trailing: true, leading: true }) this.updateChildren = updateChildren }, ready() { this.updateStyle() this.getNavPoints() this.updateChildren() }, })