Skip to content

Instantly share code, notes, and snippets.

@wjramos
Last active November 13, 2018 18:31
Show Gist options
  • Save wjramos/e1cc42ad9100ada27fcb470afb806326 to your computer and use it in GitHub Desktop.
Save wjramos/e1cc42ad9100ada27fcb470afb806326 to your computer and use it in GitHub Desktop.
Viewport Trigger
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
export default class ViewportTrigger extends PureComponent {
static propTypes = {
onViewportEnter: PropTypes.func,
onViewportLeave: PropTypes.func,
children: PropTypes.node,
threshold: PropTypes.number,
};
static defaultProps = {
threshold: 0,
};
state = {
hasEntered: false,
};
componentDidMount() {
window.addEventListener('scroll', this.onToggle, false);
window.addEventListener('resize', this.onToggle, false);
}
componentWillUnmount() {
window.removeEventListener('scroll', this.onToggle, false);
window.removeEventListener('resize', this.onToggle, false);
}
onToggle = () => {
if (this.trigger) {
if (!this.state.hasEntered && this.isInViewport()) {
this.onViewportEnter();
} else if (this.state.hasEntered && !this.isInViewport()) {
this.onViewportLeave();
}
}
}
onViewportEnter() {
const { onViewportEnter } = this.props;
if (onViewportEnter) onViewportEnter();
this.setState({
hasEntered: true,
});
}
onViewportLeave() {
const { onViewportLeave } = this.props;
if (onViewportLeave) onViewportLeave();
this.setState({
hasEntered: false,
});
}
isInViewport() {
const { threshold } = this.props;
if (!this.trigger) return false;
const {
top,
left,
bottom,
right,
} = this.trigger.getBoundingClientRect();
const width = document.documentElement.clientWidth
|| document.body.clientWidth
|| window.innerWidth;
const height = document.documentElement.clientHeight
|| document.body.clientHeight
|| window.innerHeight;
const maxWidth = width + threshold;
const maxHeight = height + threshold;
return ((
(top >= -threshold && top <= maxHeight) || (bottom >= -threshold && bottom <= maxHeight)
)
&& (
(left >= -threshold && left <= maxWidth) || (right >= -threshold && right <= maxWidth)
));
}
setTrigger = (ref) => {
this.trigger = ref;
};
render() {
const { children } = this.props;
return (
<span ref={this.setTrigger}>
{children}
</span>
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment