diff --git a/package.json b/package.json index 50b2a1e..d545e7f 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,5 @@ "react-native-index-page": "~0.2.1" }, "typings": "./lib/index.d.ts", - "pre-commit": [ - "lint" - ] + "pre-commit": [] } diff --git a/src/Picker.tsx b/src/Picker.tsx index d2214c7..ca4a276 100644 --- a/src/Picker.tsx +++ b/src/Picker.tsx @@ -6,7 +6,7 @@ import PickerMixin from './PickerMixin'; type IPickerProp = { select: Function; doScrollingComplete: Function; - coumputeChildIndex: Function; + computeChildIndex: Function; }; class Picker extends React.Component { @@ -19,10 +19,11 @@ class Picker extends React.Component { contentRef: any; indicatorRef: any; itemHeight: number; + // children的索引值 scrollValue: any; scrollHanders = (() => { - let scrollY = -1; + let scrollY = -1; // 滚动的距离 let lastY = 0; let startY = 0; let scrollDisabled = false; @@ -59,6 +60,9 @@ class Picker extends React.Component { let _y = 0; let _velocity = 0; const recorder = { + /** + * @param {number} y 滚动的距离 + * */ record: (y) => { const now = +new Date(); _velocity = (y - _y) / (now - _time); @@ -87,7 +91,7 @@ class Picker extends React.Component { let time = .3; const velocity = Velocity.getVelocity(targetY) * 4; - if (velocity) { + if (velocity) { // 过滤点击 targetY = velocity * 40 + targetY; time = Math.abs(velocity) * .1; } @@ -106,6 +110,7 @@ class Picker extends React.Component { this.onScrollChange(); }; + // 记录开始坐标,开启move状态,暂存最后一次的坐标 const onStart = (y: number) => { if (scrollDisabled) { return; @@ -116,18 +121,26 @@ class Picker extends React.Component { lastY = scrollY; }; + /** + * 记录滚动距离,速度信息 + * 调用 this.onScrollChange -> 设置滚动后的child索引,并触发 props.onScrollChange的回调 + * 调用setTransform,设置滚动的距离 + * */ const onMove = (y: number) => { if (scrollDisabled || !isMoving) { return; } + // 滚动的距离 scrollY = lastY - y + startY; + // 记录当前速度信息 Velocity.record(scrollY); this.onScrollChange(); setTransform(this.contentRef.style, `translate3d(0,${-scrollY}px,0)`); }; + // 手机(touch)兼容 PC(mouse) return { touchstart: (evt: React.TouchEvent) => onStart(evt.touches[0].screenY), mousedown: (evt: React.MouseEvent) => onStart(evt.screenY), @@ -152,6 +165,7 @@ class Picker extends React.Component { }; })(); + // 初始化默认选中值 constructor(props) { super(props); @@ -170,8 +184,11 @@ class Picker extends React.Component { }; } + // 初始化root容器 componentDidMount() { const { contentRef, indicatorRef, maskRef, rootRef } = this; + + // 设置滚动区域,样式,indicator栏的定位 const rootHeight = rootRef.getBoundingClientRect().height; // https://github.com/react-component/m-picker/issues/18 const itemHeight = this.itemHeight = indicatorRef.getBoundingClientRect().height; @@ -184,12 +201,22 @@ class Picker extends React.Component { contentRef.style.padding = `${itemHeight * num}px 0`; indicatorRef.style.top = `${itemHeight * num}px`; maskRef.style.backgroundSize = `100% ${itemHeight * num}px`; + + // 是否禁用滚动 this.scrollHanders.setDisabled(this.props.disabled); + // 初始化滚动位置 this.props.select(this.state.selectedValue, this.itemHeight, this.scrollTo); + /** + * 判断是否支持addEventListener的options.passive 禁用preventDefault优化滚动体验 + * 参考:http://www.cnblogs.com/ziyunfei/p/5545439.html + * */ const passiveSupported = this.passiveSupported(); + const willPreventDefault = passiveSupported ? { passive: false } : false; const willNotPreventDefault = passiveSupported ? { passive: true } : false; + + // 为root容器绑定各类监听事件 Object.keys(this.scrollHanders).forEach(key => { if (key.indexOf('touch') === 0 || key.indexOf('mouse') === 0) { const pd = key.indexOf('move') >= 0 ? willPreventDefault : willNotPreventDefault; @@ -198,6 +225,7 @@ class Picker extends React.Component { }); } + // 移除监听 componentWillUnmount() { Object.keys(this.scrollHanders).forEach(key => { if (key.indexOf('touch') === 0 || key.indexOf('mouse') === 0) { @@ -242,18 +270,22 @@ class Picker extends React.Component { || this.props.children !== nextProps.children; } + // 用户外部修改selectValue componentDidUpdate() { this.props.select(this.state.selectedValue, this.itemHeight, this.scrollToWithoutAnimation); } + // 有动画的滚动 scrollTo = (top) => { this.scrollHanders.scrollTo(0, top); } + // 无动画的滚动 scrollToWithoutAnimation = (top) => { this.scrollHanders.scrollTo(0, top, 0); } + // 设置state.selectedValue选中的值,触发props.onValueChange fireValueChange = (selectedValue) => { if (selectedValue !== this.state.selectedValue) { if (!('selectedValue' in this.props)) { @@ -267,11 +299,14 @@ class Picker extends React.Component { } } + // 设置滚动后的child索引,并触发 props.onScrollChange的回调 onScrollChange = () => { + // 滚动的距离 const top = this.scrollHanders.getValue(); if (top >= 0) { const children = React.Children.toArray(this.props.children); - const index = this.props.coumputeChildIndex(top, this.itemHeight, children.length); + // 计算选中状态的child索引,滚动超出最大children.length时,取它们之间的min值 + const index = this.props.computeChildIndex(top, this.itemHeight, children.length); if (this.scrollValue !== index) { this.scrollValue = index; const child: any = children[index]; @@ -284,6 +319,7 @@ class Picker extends React.Component { } } + // 为了触发this.fireValueChange scrollingComplete = () => { const top = this.scrollHanders.getValue(); if (top >= 0) { diff --git a/src/PickerMixin.tsx b/src/PickerMixin.tsx index 04a9c3d..172b293 100644 --- a/src/PickerMixin.tsx +++ b/src/PickerMixin.tsx @@ -24,27 +24,22 @@ export default function(ComposedComponent) { this.selectByIndex(0, itemHeight, scrollTo); } - selectByIndex(index, itemHeight, zscrollTo) { + selectByIndex(index, itemHeight, scrollTo) { if (index < 0 || index >= React.Children.count(this.props.children) || !itemHeight) { return; } - zscrollTo(index * itemHeight); + scrollTo(index * itemHeight); } - coumputeChildIndex(top, itemHeight, childrenLength) { - let index = top / itemHeight; - const floor = Math.floor(index); - if (index - floor > 0.5) { - index = floor + 1; - } else { - index = floor; - } + computeChildIndex(top, itemHeight, childrenLength) { + // 恰好多出0.5的情况,向下取整,但这点误差无伤大雅 + let index = Math.round(top / itemHeight); return Math.min(index, childrenLength - 1); } doScrollingComplete = (top, itemHeight, fireValueChange) => { const children = React.Children.toArray(this.props.children); - const index = this.coumputeChildIndex(top, itemHeight, children.length); + const index = this.computeChildIndex(top, itemHeight, children.length); const child: any = children[index]; if (child) { fireValueChange(child.props.value); @@ -58,7 +53,7 @@ export default function(ComposedComponent) { );