diff --git a/.gitignore b/.gitignore index 66fd9da..ae4859a 100755 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ Thumbs.db npm-debug.log node_modules bower_components +package-lock.json ################################################ # Grunt diff --git a/dist/inview.js b/dist/inview.js index 5614dbb..82fd7bf 100644 --- a/dist/inview.js +++ b/dist/inview.js @@ -12,6 +12,10 @@ var _react = require('react'); var _react2 = _interopRequireDefault(_react); +var _debounce = require('lodash/debounce'); + +var _debounce2 = _interopRequireDefault(_debounce); + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } @@ -66,7 +70,9 @@ var ReactInviewWrapper = function ReactInviewWrapper() { _ref$showGuides = _ref.showGuides, showGuides = _ref$showGuides === undefined ? false : _ref$showGuides, _ref$fullElementInVie = _ref.fullElementInView, - fullElementInView = _ref$fullElementInVie === undefined ? true : _ref$fullElementInVie; + fullElementInView = _ref$fullElementInVie === undefined ? true : _ref$fullElementInVie, + _ref$debounceTime = _ref.debounceTime, + debounceTime = _ref$debounceTime === undefined ? 100 : _ref$debounceTime; return function (ComposedComponent) { @@ -84,6 +90,9 @@ var ReactInviewWrapper = function ReactInviewWrapper() { boundingBox: {}, viewPortBox: {} }; + + _this.scrollListener = _this.scrollListener.bind(_this); + _this.handleScroll = (0, _debounce2.default)(_this.handleScroll.bind(_this), debounceTime); return _this; } @@ -95,7 +104,7 @@ var ReactInviewWrapper = function ReactInviewWrapper() { } if (typeof window !== 'undefined') { - window.addEventListener('scroll', this.handleScroll.bind(this)); + window.addEventListener('scroll', this.scrollListener); this.handleScroll(); } } @@ -103,9 +112,18 @@ var ReactInviewWrapper = function ReactInviewWrapper() { key: 'componentWillUnmount', value: function componentWillUnmount() { if (typeof window !== 'undefined') { - window.removeEventListener('scroll', this.handleScroll.bind(this)); + window.removeEventListener('scroll', this.scrollListener); } } + }, { + key: 'scrollListener', + value: function scrollListener() { + var _this2 = this; + + window.requestAnimationFrame(function () { + _this2.handleScroll(); + }); + } }, { key: 'handleScroll', value: function handleScroll() { @@ -123,13 +141,15 @@ var ReactInviewWrapper = function ReactInviewWrapper() { } else { elementIsInView = isElementTopVisible(element, boundingBox, viewPortBox); } + var newState = { + elementIsInView: elementIsInView, + boundingBox: boundingBox, + viewPortBox: viewPortBox + }; if (elementIsInView) { - this.setState({ elementHasBeenInView: elementIsInView }); + newState.elementHasBeenInView = elementIsInView; } - - this.setState({ elementIsInView: elementIsInView }); - this.setState({ boundingBox: boundingBox }); - this.setState({ viewPortBox: viewPortBox }); + this.setState(newState); } }, { key: '_showGuides', @@ -167,7 +187,7 @@ var ReactInviewWrapper = function ReactInviewWrapper() { return _react2.default.createElement( 'div', { style: styles, ref: 'container' }, - _react2.default.createElement(ComposedComponent, _extends({ update: this.handleScroll.bind(this) }, this.state, this.props)), + _react2.default.createElement(ComposedComponent, _extends({ update: this.handleScroll }, this.state, this.props)), this._showGuides() ); } diff --git a/package.json b/package.json index 0d14c89..8fab6f8 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,10 @@ "standard": "^6.0.8" }, "peerDependencies": { - "react": "^0.14.0 || ^15.0.0", - "react-dom": "^0.14.0 || ^15.0.0" + "react": "^0.14.0 || ^15.0.0 || ^16.0.0", + "react-dom": "^0.14.0 || ^15.0.0 || ^16.0.0" + }, + "dependencies": { + "lodash": "^4.17.4" } } diff --git a/src/inview.jsx b/src/inview.jsx index e543c2e..8701e8f 100644 --- a/src/inview.jsx +++ b/src/inview.jsx @@ -1,4 +1,5 @@ import React, { Component } from 'react'; +import debounce from "lodash/debounce"; // Returns dimensions of container function getViewPortBox(offsetY, boundingBox) { @@ -10,8 +11,8 @@ function getViewPortBox(offsetY, boundingBox) { if (offsetY >= 0 && offsetY <= 1) { const newHeight = vHeight * (1- offsetY); const newTop = (vHeight - newHeight) /2; - vTop = newTop; - vHeight = newHeight; + vTop = newTop; + vHeight = newHeight; } return { @@ -33,14 +34,14 @@ function isElementFullyVisible (el, rect, viewport) { return ( rect.top >= viewport.top && rect.left >= 0 && - rect.bottom <= viewport.top + viewport.height && + rect.bottom <= viewport.top + viewport.height && rect.right <= (window.innerWidth || document.documentElement.clientWidth) ); } function isElementTopVisible (el, rect, viewport) { const topIsInView = !(rect.top >= (viewport.top + viewport.height)); - const topIsAboveView = !((rect.top + rect.height - viewport.height) <= viewport.top); + const topIsAboveView = !((rect.top + rect.height - viewport.height) <= viewport.top); return ( topIsInView && topIsAboveView ); @@ -49,7 +50,8 @@ function isElementTopVisible (el, rect, viewport) { let ReactInviewWrapper = function ReactInviewWrapper ({ offsetY = 0, showGuides = false, - fullElementInView = true + fullElementInView = true, + debounceTime = 100 }={}) { return (ComposedComponent) => { @@ -57,13 +59,16 @@ let ReactInviewWrapper = function ReactInviewWrapper ({ constructor() { super(); - + this.state = { elementIsInView: false, elementIsHasBeenInView: false, boundingBox: {}, viewPortBox: {} }; + + this.scrollListener = this.scrollListener.bind(this); + this.handleScroll = debounce(this.handleScroll.bind(this), debounceTime); } componentDidMount() { @@ -72,7 +77,7 @@ let ReactInviewWrapper = function ReactInviewWrapper ({ } if (typeof(window) !== 'undefined') { - window.addEventListener('scroll', this.handleScroll.bind(this)); + window.addEventListener('scroll', this.scrollListener); this.handleScroll(); } @@ -80,12 +85,18 @@ let ReactInviewWrapper = function ReactInviewWrapper ({ componentWillUnmount() { if (typeof(window) !== 'undefined') { - window.removeEventListener('scroll', this.handleScroll.bind(this)); - } + window.removeEventListener('scroll', this.scrollListener); + } + } + + scrollListener() { + window.requestAnimationFrame(() => { + this.handleScroll(); + }); } handleScroll() { - if (typeof this.refs.container === 'undefined') { return; } + if (typeof this.refs.container === 'undefined') { return; } const element = this.refs.container.children[0]; const boundingBox = getBoundingBox(element); @@ -97,20 +108,22 @@ let ReactInviewWrapper = function ReactInviewWrapper ({ } else { elementIsInView = isElementTopVisible(element, boundingBox, viewPortBox); } + const newState = { + elementIsInView: elementIsInView, + boundingBox: boundingBox, + viewPortBox: viewPortBox + }; if (elementIsInView) { - this.setState({elementHasBeenInView: elementIsInView}); + newState.elementHasBeenInView = elementIsInView; } - - this.setState({elementIsInView: elementIsInView}); - this.setState({boundingBox: boundingBox}); - this.setState({viewPortBox: viewPortBox}); + this.setState(newState); } - + _showGuides() { if (showGuides && typeof this.state.viewPortBox.top !== 'undefined') { const {top, left, height, width} = this.state.viewPortBox; let styles = { - 'backgroundColor': '#ccc', + 'backgroundColor': '#ccc', 'position': 'fixed', 'opacity': '.5', 'top': top, @@ -121,8 +134,8 @@ let ReactInviewWrapper = function ReactInviewWrapper ({ }; return
; - } - } + } + } render() { let styles = {}; @@ -133,7 +146,7 @@ let ReactInviewWrapper = function ReactInviewWrapper ({ } return (
- + { this._showGuides() }
);