react-lazyload
Advanced tools
Comparing version
349
lib/index.js
@@ -8,2 +8,8 @@ /** | ||
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); | ||
exports['default'] = createLazyLoad; | ||
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } | ||
@@ -29,207 +35,236 @@ | ||
var _utilsDebounce = require('./utils/debounce'); | ||
var _utilsRateLimit = require('./utils/rateLimit'); | ||
var _utilsDebounce2 = _interopRequireDefault(_utilsDebounce); | ||
var rateLimitMethods = _interopRequireWildcard(_utilsRateLimit); | ||
var listeners = []; | ||
var pending = []; | ||
var SUPPORTED_EVENTS = ['scroll', 'resize', 'wheel', 'mousewheel']; | ||
/** | ||
* Check if `component` is visible in overflow container `parent` | ||
* @param {node} component React component | ||
* @param {node} parent component's scroll parent | ||
* @return {bool} | ||
* Create a LazyLoad component configured by options | ||
* @param {String} options.eventType Events that LazyLoad should listen to, a string of white space separated event types | ||
* @param {String} options.rateLimit The rate limit method, can be `debounce` or `throttle` | ||
* @param {Number} options.wait The freqeuency of rate limit checking | ||
* @return {React} The configured lazyload component | ||
*/ | ||
var checkOverflowVisible = function checkOverflowVisible(component, parent) { | ||
var node = _reactDom2['default'].findDOMNode(component); | ||
var scrollTop = parent.scrollTop(); | ||
function createLazyLoad() { | ||
var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; | ||
var _options$eventType = options.eventType; | ||
var eventType = _options$eventType === undefined ? 'scroll' : _options$eventType; | ||
var _options$rateLimit = options.rateLimit; | ||
var rateLimit = _options$rateLimit === undefined ? 'debounce' : _options$rateLimit; | ||
var _options$wait = options.wait; | ||
var wait = _options$wait === undefined ? 300 : _options$wait; | ||
var _node$getBoundingClientRect = node.getBoundingClientRect(); | ||
var events = eventType.split(' '); | ||
if (!events.every(function (e) { | ||
return SUPPORTED_EVENTS.indexOf(e) > -1; | ||
})) { | ||
console.warn('[react-lazyload] Only %s events are recommended!', SUPPORTED_EVENTS.join(', ')); | ||
} | ||
var elementHeight = _node$getBoundingClientRect.height; | ||
var listeners = []; | ||
var pending = []; | ||
var offsets = Array.isArray(component.props.offset) ? component.props.offset : [component.props.offset, component.props.offset]; // Be compatible with previous API | ||
var elementTop = node.offsetTop; | ||
/** | ||
* Check if `component` is visible in overflow container `parent` | ||
* @param {node} component React component | ||
* @param {node} parent component's scroll parent | ||
* @return {bool} | ||
*/ | ||
var checkOverflowVisible = function checkOverflowVisible(component, parent) { | ||
var node = _reactDom2['default'].findDOMNode(component); | ||
return elementTop < scrollTop + offsets[0] && elementTop + elementHeight + offsets[1] > scrollTop; | ||
}; | ||
var scrollTop = parent.scrollTop(); | ||
/** | ||
* Check if `component` is visible in document | ||
* @param {node} component React component | ||
* @return {bool} | ||
*/ | ||
var checkNormalVisible = function checkNormalVisible(component) { | ||
var node = _reactDom2['default'].findDOMNode(component); | ||
var _node$getBoundingClientRect = node.getBoundingClientRect(); | ||
var _node$getBoundingClientRect2 = node.getBoundingClientRect(); | ||
var elementHeight = _node$getBoundingClientRect.height; | ||
var top = _node$getBoundingClientRect2.top; | ||
var bottom = _node$getBoundingClientRect2.bottom; | ||
var offsets = Array.isArray(component.props.offset) ? component.props.offset : [component.props.offset, component.props.offset]; // Be compatible with previous API | ||
var elementTop = node.offsetTop; | ||
var supportPageOffset = window.pageXOffset !== undefined; | ||
var isCSS1Compat = (document.compatMode || '') === 'CSS1Compat'; | ||
return elementTop < scrollTop + offsets[0] && elementTop + elementHeight + offsets[1] > scrollTop; | ||
}; | ||
var scrollTop = supportPageOffset ? window.pageYOffset : isCSS1Compat ? document.documentElement.scrollTop : document.body.scrollTop; | ||
/** | ||
* Check if `component` is visible in document | ||
* @param {node} component React component | ||
* @return {bool} | ||
*/ | ||
var checkNormalVisible = function checkNormalVisible(component) { | ||
var node = _reactDom2['default'].findDOMNode(component); | ||
var elementTop = top + scrollTop; | ||
var elementHeight = bottom - top; | ||
var windowInnerHeight = window.innerHeight || document.documentElement.clientHeight; | ||
var _node$getBoundingClientRect2 = node.getBoundingClientRect(); | ||
var offsets = Array.isArray(component.props.offset) ? component.props.offset : [component.props.offset, component.props.offset]; // Be compatible with previous API | ||
var top = _node$getBoundingClientRect2.top; | ||
var bottom = _node$getBoundingClientRect2.bottom; | ||
return elementTop < scrollTop + windowInnerHeight + offsets[0] && elementTop + elementHeight + offsets[1] > scrollTop; | ||
}; | ||
var supportPageOffset = window.pageXOffset !== undefined; | ||
var isCSS1Compat = (document.compatMode || '') === 'CSS1Compat'; | ||
/** | ||
* Detect if element is visible in viewport, if so, set `visible` state to true. | ||
* If `once` prop is provided true, remove component as listener after checkVisible | ||
* | ||
* @param {React} component React component that respond to scroll and resize | ||
*/ | ||
var checkVisible = function checkVisible(component) { | ||
var node = _reactDom2['default'].findDOMNode(component); | ||
var parent = _utilsScrollParent2['default'](node); | ||
var isOverflow = parent !== (node.ownerDocument || document); | ||
var scrollTop = supportPageOffset ? window.pageYOffset : isCSS1Compat ? document.documentElement.scrollTop : document.body.scrollTop; | ||
var visible = isOverflow ? checkOverflowVisible(component, parent) : checkNormalVisible(component); | ||
var elementTop = top + scrollTop; | ||
var elementHeight = bottom - top; | ||
var windowInnerHeight = window.innerHeight || document.documentElement.clientHeight; | ||
if (visible) { | ||
// Avoid extra render if previously is visible, yeah I mean `render` call, | ||
// not actual DOM render | ||
if (!component.state.visible) { | ||
component._firstTimeVisible = component._firstTimeVisible === undefined; | ||
var offsets = Array.isArray(component.props.offset) ? component.props.offset : [component.props.offset, component.props.offset]; // Be compatible with previous API | ||
return elementTop < scrollTop + windowInnerHeight + offsets[0] && elementTop + elementHeight + offsets[1] > scrollTop; | ||
}; | ||
/** | ||
* Detect if element is visible in viewport, if so, set `visible` state to true. | ||
* If `once` prop is provided true, remove component as listener after checkVisible | ||
* | ||
* @param {React} component React component that respond to scroll and resize | ||
*/ | ||
var checkVisible = function checkVisible(component) { | ||
var node = _reactDom2['default'].findDOMNode(component); | ||
var parent = _utilsScrollParent2['default'](node); | ||
var isOverflow = parent !== (node.ownerDocument || document); | ||
var visible = isOverflow ? checkOverflowVisible(component, parent) : checkNormalVisible(component); | ||
if (visible) { | ||
// Avoid extra render if previously is visible, yeah I mean `render` call, | ||
// not actual DOM render | ||
if (!component.state.visible) { | ||
component._firstTimeVisible = component._firstTimeVisible === undefined; | ||
component.setState({ | ||
visible: true | ||
}); | ||
} | ||
if (component.props.once) { | ||
pending.push(component); | ||
} | ||
} else if (component.state.visible) { | ||
if (component._firstTimeVisible !== undefined) { | ||
component._firstTimeVisible = false; | ||
} | ||
component.setState({ | ||
visible: true | ||
visible: false | ||
}); | ||
} | ||
}; | ||
if (component.props.once) { | ||
pending.push(component); | ||
} | ||
} else if (component.state.visible) { | ||
if (component._firstTimeVisible !== undefined) { | ||
component._firstTimeVisible = false; | ||
} | ||
var purgePending = function purgePending() { | ||
pending.forEach(function (component) { | ||
var index = listeners.indexOf(component); | ||
if (index !== -1) { | ||
listeners.splice(index, 1); | ||
} | ||
}); | ||
component.setState({ | ||
visible: false | ||
}); | ||
pending = []; | ||
}; | ||
var rateLimitMethod = rateLimitMethods[rateLimit]; | ||
if (typeof rateLimitMethod !== 'function') { | ||
console.warn('[react-lazyload] %s is not a valid rate limit type, no rate limit method is applied.', rateLimit); | ||
rateLimitMethod = function (f) { | ||
return f; | ||
}; | ||
} | ||
}; | ||
var purgePending = function purgePending() { | ||
pending.forEach(function (component) { | ||
var index = listeners.indexOf(component); | ||
if (index !== -1) { | ||
listeners.splice(index, 1); | ||
var lazyLoadHandler = rateLimitMethod(function () { | ||
for (var i = 0; i < listeners.length; ++i) { | ||
var listener = listeners[i]; | ||
checkVisible(listener); | ||
} | ||
}); | ||
pending = []; | ||
}; | ||
// Remove `once` component in listeners | ||
purgePending(); | ||
}, wait); | ||
var lazyLoadHandler = _utilsDebounce2['default'](function () { | ||
for (var i = 0; i < listeners.length; ++i) { | ||
var listener = listeners[i]; | ||
checkVisible(listener); | ||
} | ||
return (function (_Component) { | ||
_inherits(LazyLoad, _Component); | ||
// Remove `once` component in listeners | ||
purgePending(); | ||
}, 300); | ||
_createClass(LazyLoad, null, [{ | ||
key: 'propTypes', | ||
value: { | ||
once: _react.PropTypes.bool, | ||
offset: _react.PropTypes.oneOfType([_react.PropTypes.number, _react.PropTypes.arrayOf(_react.PropTypes.number)]), | ||
children: _react.PropTypes.node | ||
}, | ||
enumerable: true | ||
}, { | ||
key: 'defaultProps', | ||
value: { | ||
once: false, | ||
offset: 0 | ||
}, | ||
enumerable: true | ||
}]); | ||
var LazyLoad = (function (_Component) { | ||
_inherits(LazyLoad, _Component); | ||
function LazyLoad(props) { | ||
_classCallCheck(this, LazyLoad); | ||
function LazyLoad(props) { | ||
_classCallCheck(this, LazyLoad); | ||
_Component.call(this, props); | ||
_Component.call(this, props); | ||
if (props.scroll === true && props.wheel === true) { | ||
console && console.warn('[react-lazyload] Don\'t use both `scroll` and `wheel` event in LazyLoad component, pick one!'); | ||
this.state = { | ||
visible: false | ||
}; | ||
} | ||
this.state = { | ||
visible: false | ||
}; | ||
} | ||
LazyLoad.prototype.componentDidMount = function componentDidMount() { | ||
if (listeners.length === 0) { | ||
if (events.indexOf('scroll') > -1) { | ||
_utilsEvent.on(window, 'scroll', lazyLoadHandler); | ||
} | ||
LazyLoad.prototype.componentDidMount = function componentDidMount() { | ||
if (listeners.length === 0) { | ||
if (this.props.scroll) { | ||
_utilsEvent.on(window, 'scroll', lazyLoadHandler); | ||
} | ||
if (events.indexOf('wheel') > -1) { | ||
if (window.hasOwnProperty('onwheel')) { | ||
_utilsEvent.on(window, 'wheel', lazyLoadHandler); | ||
} else { | ||
_utilsEvent.on(window, 'mousewheel', lazyLoadHandler); | ||
} | ||
} | ||
if (this.props.wheel) { | ||
if (window.hasOwnProperty('onwheel')) { | ||
_utilsEvent.on(window, 'wheel', lazyLoadHandler); | ||
} else { | ||
_utilsEvent.on(window, 'mousewheel', lazyLoadHandler); | ||
if (events.indexOf('resize') > -1) { | ||
_utilsEvent.on(window, 'resize', lazyLoadHandler); | ||
} | ||
} | ||
if (this.props.resize) { | ||
_utilsEvent.on(window, 'resize', lazyLoadHandler); | ||
} | ||
} | ||
listeners.push(this); | ||
checkVisible(this); | ||
}; | ||
listeners.push(this); | ||
checkVisible(this); | ||
}; | ||
LazyLoad.prototype.shouldComponentUpdate = function shouldComponentUpdate(nextProps, nextState) { | ||
return nextState.visible; | ||
}; | ||
LazyLoad.prototype.shouldComponentUpdate = function shouldComponentUpdate(nextProps, nextState) { | ||
return nextState.visible; | ||
}; | ||
LazyLoad.prototype.componentWillUpdate = function componentWillUpdate(nextProps, nextState) { | ||
if (this.state.visible && nextState.visible && this._firstTimeVisible) { | ||
this._firstTimeVisible = false; | ||
} | ||
}; | ||
LazyLoad.prototype.componentWillUpdate = function componentWillUpdate(nextProps, nextState) { | ||
if (this.state.visible && nextState.visible && this._firstTimeVisible) { | ||
this._firstTimeVisible = false; | ||
} | ||
}; | ||
LazyLoad.prototype.componentWillUnmount = function componentWillUnmount() { | ||
var index = listeners.indexOf(this); | ||
if (index !== -1) { | ||
listeners.splice(index, 1); | ||
} | ||
LazyLoad.prototype.componentWillUnmount = function componentWillUnmount() { | ||
var index = listeners.indexOf(this); | ||
if (index !== -1) { | ||
listeners.splice(index, 1); | ||
} | ||
if (listeners.length === 0) { | ||
_utilsEvent.off(window, 'wheel', lazyLoadHandler); | ||
_utilsEvent.off(window, 'mousewheel', lazyLoadHandler); | ||
_utilsEvent.off(window, 'resize', lazyLoadHandler); | ||
_utilsEvent.off(window, 'scroll', lazyLoadHandler); | ||
} | ||
}; | ||
if (listeners.length === 0) { | ||
_utilsEvent.off(window, 'wheel', lazyLoadHandler); | ||
_utilsEvent.off(window, 'mousewheel', lazyLoadHandler); | ||
_utilsEvent.off(window, 'resize', lazyLoadHandler); | ||
_utilsEvent.off(window, 'scroll', lazyLoadHandler); | ||
} | ||
}; | ||
LazyLoad.prototype.render = function render() { | ||
return _react2['default'].cloneElement(this.props.children, { | ||
visible: this.state.visible, | ||
firstTimeVisible: this._firstTimeVisible | ||
}); | ||
}; | ||
LazyLoad.prototype.render = function render() { | ||
return _react2['default'].cloneElement(this.props.children, { | ||
visible: this.state.visible, | ||
firstTimeVisible: this._firstTimeVisible | ||
}); | ||
}; | ||
return LazyLoad; | ||
})(_react.Component); | ||
} | ||
return LazyLoad; | ||
})(_react.Component); | ||
LazyLoad.propTypes = { | ||
once: _react.PropTypes.bool, | ||
offset: _react.PropTypes.oneOfType([_react.PropTypes.number, _react.PropTypes.arrayOf(_react.PropTypes.number)]), | ||
scroll: _react.PropTypes.bool, | ||
wheel: _react.PropTypes.bool, | ||
resize: _react.PropTypes.bool, | ||
children: _react.PropTypes.node | ||
}; | ||
LazyLoad.defaultProps = { | ||
once: false, | ||
offset: 0, | ||
scroll: true, | ||
wheel: false, | ||
resize: false | ||
}; | ||
exports['default'] = LazyLoad; | ||
module.exports = exports['default']; |
{ | ||
"name": "react-lazyload", | ||
"version": "2.0.0-beta1", | ||
"version": "2.0.0-beta2", | ||
"description": "Lazyload your Component, Image or anything matters the performance.", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
32999
12.41%16
6.67%708
12.92%