Skip to content

Instantly share code, notes, and snippets.

@mirinzhang
Created October 16, 2018 10:06
Show Gist options
  • Save mirinzhang/89a6c548ed2f19c22c58a2b16d1e0bf0 to your computer and use it in GitHub Desktop.
Save mirinzhang/89a6c548ed2f19c22c58a2b16d1e0bf0 to your computer and use it in GitHub Desktop.
Check React-native Components InViewport
/**
* @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