325 lines
12 KiB
JavaScript
325 lines
12 KiB
JavaScript
'use strict';
|
||
|
||
Object.defineProperty(exports, "__esModule", {
|
||
value: true
|
||
});
|
||
|
||
var _react = require('react');
|
||
|
||
var _react2 = _interopRequireDefault(_react);
|
||
|
||
var _propTypes = require('prop-types');
|
||
|
||
var _propTypes2 = _interopRequireDefault(_propTypes);
|
||
|
||
var _util = require('./util');
|
||
|
||
var _config = require('./config');
|
||
|
||
var _config2 = _interopRequireDefault(_config);
|
||
|
||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
||
|
||
function _defaults(obj, defaults) { var keys = Object.getOwnPropertyNames(defaults); for (var i = 0; i < keys.length; i++) { var key = keys[i]; var value = Object.getOwnPropertyDescriptor(defaults, key); if (value && value.configurable && obj[key] === undefined) { Object.defineProperty(obj, key, value); } } return obj; }
|
||
|
||
function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
|
||
|
||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||
|
||
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
|
||
|
||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : _defaults(subClass, superClass); } /**
|
||
* 处理滚动加载逻辑
|
||
*/
|
||
|
||
|
||
var InfiniteScroll = function (_Component) {
|
||
_inherits(InfiniteScroll, _Component);
|
||
|
||
function InfiniteScroll(props) {
|
||
_classCallCheck(this, InfiniteScroll);
|
||
|
||
//默认显示20条,rowsInView根据定高算的。在非固定高下,这个只是一个大概的值。
|
||
var _this = _possibleConstructorReturn(this, _Component.call(this, props));
|
||
|
||
_this.eventListenerOptions = function () {
|
||
var options = _this.props.useCapture;
|
||
|
||
if (_this.isPassiveSupported()) {
|
||
options = {
|
||
useCapture: _this.props.useCapture,
|
||
passive: true
|
||
};
|
||
}
|
||
return options;
|
||
};
|
||
|
||
_this.mousewheelListener = function (e) {
|
||
// Prevents Chrome hangups
|
||
// See: https://stackoverflow.com/questions/47524205/random-high-content-download-time-in-chrome/47684257#47684257
|
||
if (e.deltaY === 1 && !_this.isPassiveSupported()) {
|
||
e.preventDefault();
|
||
}
|
||
};
|
||
|
||
_this.scrollListener = function () {
|
||
var el = _this.scrollComponent;
|
||
var parentNode = _this.getParentElement(el);
|
||
_this.scrollTop = parentNode.scrollTop;
|
||
_this.handleScrollY();
|
||
};
|
||
|
||
_this.handleScrollY = function () {
|
||
var store = _this.props.store;
|
||
|
||
var parentElement = _this.getParentElement(_this.scrollComponent);
|
||
if (!parentElement) {
|
||
return;
|
||
}
|
||
var scrollEl = parentElement;
|
||
var scrollY = scrollEl && scrollEl.clientHeight;
|
||
|
||
var rowHeight = store.getState().rowHeight;
|
||
//默认显示20条,rowsInView根据定高算的。在非固定高下,这个只是一个大概的值。
|
||
_this.rowsInView = scrollY ? Math.floor(scrollY / rowHeight) : _config2["default"].defaultRowsInView;
|
||
|
||
var currentIndex = _this.currentIndex,
|
||
startIndex = _this.startIndex,
|
||
endIndex = _this.endIndex,
|
||
treeList = _this.treeList,
|
||
loadCount = _this.loadCount,
|
||
rowsInView = _this.rowsInView;
|
||
|
||
var index = 0;
|
||
var tempScrollTop = _this.scrollTop;
|
||
//根据 scrollTop 计算 currentIndex
|
||
while (tempScrollTop > 0) {
|
||
tempScrollTop -= rowHeight;
|
||
if (tempScrollTop > 0) {
|
||
index += 1;
|
||
}
|
||
}
|
||
|
||
//true 为向下滚动, false 为向上滚动
|
||
var isScrollDown = index - currentIndex > 0 ? true : false;
|
||
|
||
if (index < 0) index = 0;
|
||
//如果之前的索引和下一次的不一样则重置索引和滚动的位置
|
||
_this.currentIndex = currentIndex !== index ? index : currentIndex;
|
||
|
||
// 如果rowsInView 小于 缓存的数据则重新render
|
||
// 向下滚动 下临界值超出缓存的endIndex则重新渲染
|
||
if (isScrollDown && rowsInView + index > endIndex - _config2["default"].rowDiff) {
|
||
startIndex = index - _config2["default"].loadBuffer > 0 ? index - _config2["default"].loadBuffer : 0;
|
||
endIndex = startIndex + loadCount;
|
||
if (endIndex > treeList.length) {
|
||
endIndex = treeList.length;
|
||
}
|
||
if (endIndex > _this.endIndex) {
|
||
_this.startIndex = startIndex;
|
||
_this.endIndex = endIndex;
|
||
_this.sliceTreeList(_this.startIndex, _this.endIndex);
|
||
}
|
||
}
|
||
// 向上滚动,当前的index是否已经加载(currentIndex),若干上临界值小于startIndex则重新渲染
|
||
if (!isScrollDown && index < startIndex + _config2["default"].rowDiff) {
|
||
startIndex = index - _config2["default"].loadBuffer;
|
||
if (startIndex < 0) {
|
||
startIndex = 0;
|
||
}
|
||
if (startIndex <= _this.startIndex) {
|
||
_this.startIndex = startIndex;
|
||
_this.endIndex = _this.startIndex + loadCount;
|
||
_this.sliceTreeList(_this.startIndex, _this.endIndex);
|
||
}
|
||
}
|
||
};
|
||
|
||
_this.sliceTreeList = function (startIndex, endIndex) {
|
||
var newTreeList = []; //存储截取后的新数据
|
||
newTreeList = _this.treeList.slice(startIndex, endIndex);
|
||
_this.props.handleTreeListChange && _this.props.handleTreeListChange(newTreeList, startIndex, endIndex);
|
||
};
|
||
|
||
_this.rowsInView = _config2["default"].defaultRowsInView;
|
||
//一维数组
|
||
_this.treeList = props.treeList;
|
||
//一次加载多少数据
|
||
_this.loadCount = _config2["default"].loadBuffer ? _this.rowsInView + _config2["default"].loadBuffer * 2 : 16;
|
||
//可视区第一条数据的 index
|
||
_this.currentIndex = 0;
|
||
_this.startIndex = _this.currentIndex; //数据开始位置
|
||
_this.endIndex = _this.currentIndex + _this.loadCount; //数据结束位置
|
||
return _this;
|
||
}
|
||
|
||
InfiniteScroll.prototype.componentDidMount = function componentDidMount() {
|
||
this.options = this.eventListenerOptions();
|
||
this.attachScrollListener();
|
||
};
|
||
|
||
InfiniteScroll.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps) {
|
||
var newTreeList = nextProps.treeList;
|
||
var oldTreeList = this.props.treeList;
|
||
|
||
if (newTreeList !== oldTreeList) {
|
||
this.treeList = newTreeList;
|
||
this.handleScrollY();
|
||
}
|
||
};
|
||
|
||
// componentDidUpdate() {
|
||
// const el = this.scrollComponent;
|
||
// const parentNode = this.getParentElement(el);
|
||
// parentNode.scrollTop = this.scrollTop;
|
||
// };
|
||
|
||
InfiniteScroll.prototype.componentWillUnmount = function componentWillUnmount() {
|
||
this.detachScrollListener();
|
||
this.detachMousewheelListener();
|
||
};
|
||
|
||
InfiniteScroll.prototype.isPassiveSupported = function isPassiveSupported() {
|
||
var passive = false;
|
||
|
||
var testOptions = {
|
||
get passive() {
|
||
passive = true;
|
||
}
|
||
};
|
||
|
||
try {
|
||
document.addEventListener('test', null, testOptions);
|
||
document.removeEventListener('test', null, testOptions);
|
||
} catch (e) {
|
||
// ignore
|
||
}
|
||
return passive;
|
||
};
|
||
|
||
/**
|
||
* 解除mousewheel事件监听
|
||
*/
|
||
InfiniteScroll.prototype.detachMousewheelListener = function detachMousewheelListener() {
|
||
var scrollEl = window;
|
||
if (this.props.useWindow === false) {
|
||
scrollEl = this.scrollComponent.parentNode;
|
||
}
|
||
|
||
scrollEl.removeEventListener('mousewheel', this.mousewheelListener, this.options ? this.options : this.props.useCapture);
|
||
};
|
||
/**
|
||
* 解除scroll事件监听
|
||
*/
|
||
|
||
|
||
InfiniteScroll.prototype.detachScrollListener = function detachScrollListener() {
|
||
var scrollEl = window;
|
||
if (this.props.useWindow === false) {
|
||
scrollEl = this.getParentElement(this.scrollComponent);
|
||
}
|
||
|
||
scrollEl.removeEventListener('scroll', this.scrollListener, this.options ? this.options : this.props.useCapture);
|
||
scrollEl.removeEventListener('resize', this.scrollListener, this.options ? this.options : this.props.useCapture);
|
||
};
|
||
/**
|
||
* 获取父组件(用户自定义父组件或者当前dom的parentNode)
|
||
* @param {*} el
|
||
*/
|
||
|
||
|
||
InfiniteScroll.prototype.getParentElement = function getParentElement(el) {
|
||
var scrollParent = this.props.getScrollParent && this.props.getScrollParent();
|
||
if (scrollParent != null) {
|
||
return scrollParent;
|
||
}
|
||
return el && el.parentNode;
|
||
};
|
||
|
||
InfiniteScroll.prototype.filterProps = function filterProps(props) {
|
||
return props;
|
||
};
|
||
/**
|
||
* 绑定scroll事件
|
||
*/
|
||
|
||
|
||
InfiniteScroll.prototype.attachScrollListener = function attachScrollListener() {
|
||
var store = this.props.store;
|
||
|
||
var parentElement = this.getParentElement(this.scrollComponent);
|
||
if (!parentElement) {
|
||
return;
|
||
}
|
||
var scrollEl = parentElement;
|
||
var scrollY = scrollEl && scrollEl.clientHeight;
|
||
|
||
var rowHeight = store.getState().rowHeight;
|
||
//默认显示20条,rowsInView根据定高算的。
|
||
this.rowsInView = scrollY ? Math.floor(scrollY / rowHeight) : _config2["default"].defaultRowsInView;
|
||
|
||
scrollEl.addEventListener('scroll', (0, _util.throttle)(this.scrollListener, 150), this.options ? this.options : this.props.useCapture);
|
||
scrollEl.addEventListener('resize', (0, _util.throttle)(this.scrollListener, 150), this.options ? this.options : this.props.useCapture);
|
||
};
|
||
/**
|
||
* 滚动事件监听
|
||
*/
|
||
|
||
|
||
/**
|
||
* @description 根据返回的scrollTop计算当前的索引。
|
||
*/
|
||
|
||
|
||
/**
|
||
* 根据 startIndex 和 endIndex 截取数据
|
||
* @param startIndex
|
||
* @param endIndex
|
||
*/
|
||
|
||
|
||
InfiniteScroll.prototype.render = function render() {
|
||
var _this2 = this;
|
||
|
||
var _props = this.props,
|
||
children = _props.children,
|
||
element = _props.element,
|
||
ref = _props.ref,
|
||
getScrollParent = _props.getScrollParent,
|
||
treeList = _props.treeList,
|
||
handleTreeListChange = _props.handleTreeListChange,
|
||
store = _props.store,
|
||
props = _objectWithoutProperties(_props, ['children', 'element', 'ref', 'getScrollParent', 'treeList', 'handleTreeListChange', 'store']);
|
||
|
||
props.ref = function (node) {
|
||
_this2.scrollComponent = node;
|
||
if (ref) {
|
||
ref(node);
|
||
}
|
||
};
|
||
|
||
var childrenArray = [children];
|
||
|
||
return _react2["default"].createElement(element, props, childrenArray);
|
||
};
|
||
|
||
return InfiniteScroll;
|
||
}(_react.Component);
|
||
|
||
InfiniteScroll.propTypes = {
|
||
children: _propTypes2["default"].node.isRequired,
|
||
element: _propTypes2["default"].node,
|
||
ref: _propTypes2["default"].func,
|
||
getScrollParent: _propTypes2["default"].func,
|
||
treeList: _propTypes2["default"].array,
|
||
handleTreeListChange: _propTypes2["default"].func
|
||
};
|
||
InfiniteScroll.defaultProps = {
|
||
element: 'div',
|
||
ref: null,
|
||
getScrollParent: null,
|
||
treeList: [],
|
||
handleTreeListChange: function handleTreeListChange() {}
|
||
};
|
||
exports["default"] = InfiniteScroll;
|
||
module.exports = exports['default']; |