207 lines
6.1 KiB
JavaScript
207 lines
6.1 KiB
JavaScript
import baseComponent from '../helpers/baseComponent'
|
|
import classNames from '../helpers/classNames'
|
|
import styleToCssString from '../helpers/styleToCssString'
|
|
import { getSystemInfo } from '../helpers/checkIPhoneX'
|
|
|
|
baseComponent({
|
|
relations: {
|
|
'../index-item/index': {
|
|
type: 'child',
|
|
observer() {
|
|
this.debounce(this.updated)
|
|
},
|
|
},
|
|
},
|
|
properties: {
|
|
prefixCls: {
|
|
type: String,
|
|
value: 'wux-index',
|
|
},
|
|
height: {
|
|
type: [String, Number],
|
|
value: 300,
|
|
observer: 'updateStyle',
|
|
},
|
|
showIndicator: {
|
|
type: Boolean,
|
|
value: true,
|
|
},
|
|
parentOffsetTop: {
|
|
type: Number,
|
|
value: 0,
|
|
},
|
|
},
|
|
data: {
|
|
scrollTop: 0,
|
|
sections: [],
|
|
moving: false,
|
|
current: 0,
|
|
currentName: '',
|
|
extStyle: '',
|
|
},
|
|
computed: {
|
|
classes: ['prefixCls', function(prefixCls) {
|
|
const wrap = classNames(prefixCls)
|
|
const nav = `${prefixCls}__nav`
|
|
const navItem = `${prefixCls}__nav-item`
|
|
const indicator = `${prefixCls}__indicator`
|
|
|
|
return {
|
|
wrap,
|
|
nav,
|
|
navItem,
|
|
indicator,
|
|
}
|
|
}],
|
|
},
|
|
methods: {
|
|
/**
|
|
* 更新样式
|
|
*/
|
|
updateStyle(height = this.data.height) {
|
|
const extStyle = styleToCssString({ height })
|
|
|
|
if (extStyle !== this.data.extStyle) {
|
|
this.setData({
|
|
extStyle,
|
|
})
|
|
}
|
|
},
|
|
/**
|
|
* 更新元素
|
|
*/
|
|
updated() {
|
|
const elements = this.getRelationNodes('../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))
|
|
}
|
|
|
|
if (this.data.sections.length !== elements.length) {
|
|
this.setData({
|
|
sections: elements.map((element) => element.data),
|
|
})
|
|
}
|
|
},
|
|
/**
|
|
* 设置当前激活的元素
|
|
*/
|
|
setActive(current, currentName) {
|
|
if (current !== this.data.current || currentName !== this.data.currentName) {
|
|
const target = this.data.sections.filter((section) => section.index === current && section.name === currentName)[0]
|
|
if (target) {
|
|
this.setData({
|
|
current,
|
|
currentName,
|
|
scrollTop: target.top - this.data.parentOffsetTop,
|
|
})
|
|
}
|
|
|
|
// 振动反馈
|
|
this.vibrateShort()
|
|
}
|
|
|
|
this.triggerEvent('change', { index: current, name: currentName })
|
|
},
|
|
/**
|
|
* 手指触摸动作开始
|
|
*/
|
|
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(e) {
|
|
if (!this.data.moving) return
|
|
setTimeout(() => this.setData({ moving: false }), 300)
|
|
},
|
|
/**
|
|
* 滚动事件的回调函数
|
|
*/
|
|
onScroll(e) {
|
|
if (this.data.moving) return
|
|
const { scrollTop } = e.detail
|
|
this.data.sections.forEach((section, index) => {
|
|
if (scrollTop < section.top + section.height && scrollTop >= section.top) {
|
|
if (index !== this.data.current || section.name !== this.data.currentName) {
|
|
this.setData({
|
|
current: index,
|
|
currentName: section.name,
|
|
})
|
|
}
|
|
}
|
|
})
|
|
},
|
|
/**
|
|
* 获取右侧导航对应的坐标
|
|
*/
|
|
getNavPoints() {
|
|
const className = `.${this.data.prefixCls}__nav-item`
|
|
wx
|
|
.createSelectorQuery()
|
|
.in(this)
|
|
.selectAll(className)
|
|
.boundingClientRect((rects) => {
|
|
if (rects.filter((n) => !n).length) return
|
|
this.setData({
|
|
points: rects.map((n) => ({ ...n, offsets: [n.top, n.top + n.height] })),
|
|
})
|
|
})
|
|
.exec()
|
|
},
|
|
/**
|
|
* 根据坐标获得对应的元素
|
|
*/
|
|
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
|
|
},
|
|
},
|
|
created() {
|
|
const systemInfo = getSystemInfo()
|
|
this.vibrateShort = () => {
|
|
if (systemInfo.platform !== 'devtools') {
|
|
wx.vibrateShort()
|
|
}
|
|
}
|
|
},
|
|
ready() {
|
|
this.updateStyle()
|
|
this.getNavPoints()
|
|
},
|
|
})
|