Created
October 16, 2018 10:06
-
-
Save mirinzhang/89a6c548ed2f19c22c58a2b16d1e0bf0 to your computer and use it in GitHub Desktop.
Check React-native Components InViewport
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 characters
| /** | |
| * @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> | |
| ); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment