Created
October 16, 2018 10:06
-
-
Save mirinzhang/89a6c548ed2f19c22c58a2b16d1e0bf0 to your computer and use it in GitHub Desktop.
Revisions
-
泡面君 created this gist
Oct 16, 2018 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,209 @@ /** * @File 检查元素是否在视口中 * @Author: yabingzyb.zhang * @Date: 2018-10-16 15:45:13 * @Last Modified by: yabingzyb.zhang * @Last Modified time: 2018-10-16 17:43:12 */ import React, { Component, PropTypes } from 'react'; import { View, Dimensions } from 'react-native'; // 滑动方向 const DIRECTS = { UP: 'UP', DOWN: 'DOWN' }; export default class extends Component { static propTypes = { // 检查元素出现的方向:0: 所有方向,1:从下向上,2:从上向下,3:垂直方向 type: PropTypes.number, // 检测元素位置间隔 delay: PropTypes.number, // 状态发生变化时的回调 onChange: PropTypes.func.isRequired, // 是否停止监听 disabled: PropTypes.bool }; static defaultProps = { type: 0, delay: 100, disabled: false, onChange: () => { } }; constructor(props) { super(props); this.state = { rectTop: 0, rectBottom: 0, verticalDirect: '' }; this.viewPort = null; } componentDidMount() { if (!this.props.disabled) { this.startWatching(); } } componentWillUnmount() { this.stopWatching(); } componentWillReceiveProps(nextProps) { if (nextProps.disabled) { this.stopWatching(); } else { this.prevStatus = null; this.startWatching(); } } /** * 开始监听元素 */ startWatching() { if (this.interval) { return; } this.interval = setInterval(() => { if (!this.viewPort) { return; } this.viewPort.measure((x, y, width, height, pageX, pageY) => { const { rectBottom, verticalDirect: prevVerticalDirect } = this.state, offsetY = rectBottom - (pageY + height), verticalDirect = offsetY === 0 ? prevVerticalDirect : (offsetY > 0 ? DIRECTS.DOWN : DIRECTS.UP); this.setState({ rectTop: pageY, rectBottom: pageY + height, rectWidth: pageX + width, verticalDirect }, () => { this.isInViewPort(); }); }); }, this.props.delay || 100); } /** * 停止监听 */ stopWatching() { this.interval = clearInterval(this.interval); } /** * 检查元素是否在视口中 */ isInViewPort() { const { onChange, type = 0 } = this.props, window = Dimensions.get('window'), { prevStatus, state } = this; let isVisible; switch (type) { case 0: isVisible = this.checkAll(state, window); break; case 1: isVisible = this.checkVerticalUp(state, window); break; case 2: isVisible = this.checkVerticalDown(state, window); break; case 3: isVisible = this.checkVertical(state, window); break; default: isVisible = false; } if (prevStatus !== isVisible) { this.prevStatus = isVisible; onChange && onChange(isVisible); } } /** * 检查元素在整个页面中是否出现 * @param {object} state * @param {object} window */ checkAll(state, window) { const { rectBottom, rectTop, rectWidth } = state, { height, width } = window; return rectBottom && rectTop >= 0 && rectBottom <= height && rectWidth > 0 && rectWidth <= width; } /** * 检查元素在垂直方向是否出现在视口中,从下向上滑动 * @param {object} state * @param {object} window */ checkVerticalUp(state, window) { const { rectBottom, rectTop, verticalDirect } = state, { height } = window; return rectBottom && rectTop >= 0 && verticalDirect === DIRECTS.DOWN && rectBottom <= height; } /** * 检查元素在垂直方向是否出现在视口中,从上向下滑动 * @param {object} state * @param {object} window */ checkVerticalDown(state, window) { const { rectBottom, rectTop, verticalDirect } = state, { height } = window; return rectBottom && rectTop >= 0 && verticalDirect === DIRECTS.UP && rectBottom <= height; } /** * 检查元素在垂直方向是否出现在视口中 * @param {object} state * @param {object} window */ checkVertical(state, window) { const { rectBottom, rectTop } = state, { height } = window; return rectBottom && rectTop >= 0 && rectBottom <= height; } render() { return ( <View collapsable={false} ref={component => { this.viewPort = component; }} {...this.props}> {this.props.children} </View> ); } }