z-react-ui
Version:
z-react-ui,是一款基于 Dumi,由 React + TypeScript 开发的组件库 🎉。
241 lines (202 loc) • 8.29 kB
JavaScript
;
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _react = _interopRequireWildcard(require("react"));
var _hooks = require("@/_hooks");
var _ahooks = require("ahooks");
var _classnames = _interopRequireDefault(require("classnames"));
// #----------- 上: ts类型定义 ----------- 分割线 ----------- 下: JS代码 -----------
var VirtualList = function VirtualList(_ref) {
var _positionRef$current2;
var dataSource = _ref.dataSource,
renderItem = _ref.renderItem,
estimatedItemSize = _ref.estimatedItemSize,
realItemSize = _ref.realItemSize,
height = _ref.height,
_ref$bufferScale = _ref.bufferScale,
bufferScale = _ref$bufferScale === void 0 ? 1 : _ref$bufferScale,
className = _ref.className,
showClassName = _ref.showClassName;
var prefixCls = (0, _hooks.usePrefixCls)('virtual-list');
var paramsRef = (0, _react.useRef)();
paramsRef.current = dataSource; // 容器
var divRef = (0, _react.useRef)();
var listRef = (0, _react.useRef)();
var _useValues = (0, _hooks.useValues)({
//可视区域高度
clientHeight: 0,
//起始索引
start: 0,
//结束索引
end: 0
}),
_useValues2 = (0, _slicedToArray2.default)(_useValues, 2),
value = _useValues2[0],
setValue = _useValues2[1];
var _useMemo = (0, _react.useMemo)(function () {
return value;
}, [value]),
clientHeight = _useMemo.clientHeight,
start = _useMemo.start,
end = _useMemo.end;
var _useMemo2 = (0, _react.useMemo)(function () {
var itemSize = estimatedItemSize;
var hasRealHeight = false;
switch ((0, _typeof2.default)(realItemSize)) {
case 'number':
itemSize = realItemSize;
hasRealHeight = true;
break;
case 'string':
itemSize = parseInt(realItemSize);
hasRealHeight = true;
break;
}
return {
itemSize: itemSize,
hasRealHeight: hasRealHeight
};
}, [estimatedItemSize, realItemSize]),
itemSize = _useMemo2.itemSize,
hasRealHeight = _useMemo2.hasRealHeight; // 列表中可见的列表数
var visibleCount = (0, _react.useMemo)(function () {
return Math.ceil(clientHeight / itemSize);
}, [clientHeight, itemSize]); // 可视区域上方的加载列表数量
var aboveCount = (0, _react.useMemo)(function () {
return Math.min(start, bufferScale * visibleCount);
}, [start, bufferScale, visibleCount]); // 可视区域下方的加载列表数量
var belowCount = (0, _react.useMemo)(function () {
return Math.min(paramsRef.current.length - end, bufferScale * visibleCount);
}, [end, bufferScale, visibleCount]); // 可视区域数据
var visibleData = (0, _react.useMemo)(function () {
var startIndex = start - aboveCount;
var endIndex = end + belowCount;
return paramsRef.current.map(function (item, index) {
return (0, _objectSpread2.default)((0, _objectSpread2.default)({}, item), {}, {
_index: index
});
}).slice(startIndex, endIndex);
}, [start, aboveCount, end, belowCount]); // 位置信息
var positionRef = (0, _react.useRef)(); // 只加载一次
(0, _react.useMemo)(function () {
positionRef.current = paramsRef.current.map(function (d, index) {
return {
index: index,
height: itemSize,
top: index * itemSize,
bottom: (index + 1) * itemSize
};
});
}, [itemSize, hasRealHeight]);
(0, _react.useEffect)(function () {
setValue({
clientHeight: divRef.current.clientHeight,
start: 0,
end: visibleCount
});
}, [visibleCount]);
var virtualListRef = (0, _react.useRef)([]); //获取列表项的当前尺寸
var updateItemsSize = (0, _ahooks.usePersistFn)(function () {
// 如果存在真实的高度,则不需要下面步骤了
if (hasRealHeight) return;
var nodes = virtualListRef.current;
if (!nodes || !nodes.length) return;
nodes.forEach(function (node) {
// 会出现nodes长度不一的情况
if (!node) return;
var index = +(node === null || node === void 0 ? void 0 : node.id);
var rect = node.getBoundingClientRect();
var height = rect.height;
var oldHeight = positionRef.current[index].height;
var dValue = oldHeight - height;
var diff = Math.abs(dValue); // 存在差值
if (diff > 0.5) {
positionRef.current[index].bottom = positionRef.current[index].bottom - dValue;
positionRef.current[index].height = height;
for (var k = index + 1; k < positionRef.current.length; k++) {
positionRef.current[k].top = positionRef.current[k - 1].bottom;
positionRef.current[k].bottom = positionRef.current[k].bottom - dValue;
}
}
});
}); // 得到可视区域第一项的索引
var getStartIndex = (0, _ahooks.usePersistFn)(function () {
var scrollTop = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
//二分法查找
var start = binarySearch(positionRef.current, scrollTop);
setValue({
start: start,
end: start + visibleCount
});
});
var binarySearch = (0, _ahooks.usePersistFn)(function (list, value) {
var start = 0;
var end = list.length - 1;
var tempIndex = null; // 最后得到的可视区第一项
while (start <= end) {
var midIndex = parseInt("".concat((start + end) / 2));
var midValue = list[midIndex].bottom;
if (midValue === value) {
return midIndex + 1;
} else if (midValue < value) {
start = midIndex + 1;
} else if (midValue > value) {
tempIndex = midIndex;
end = midIndex - 1;
}
}
return tempIndex;
}); //获取当前的偏移量
var setStartOffset = (0, _ahooks.usePersistFn)(function () {
var _positionRef$current;
var startOffset = ((_positionRef$current = positionRef.current[start - aboveCount]) === null || _positionRef$current === void 0 ? void 0 : _positionRef$current.top) || 0;
listRef.current.style.transform = "translate3d(0,".concat(startOffset, "px,0)");
});
var handleScroll = (0, _ahooks.usePersistFn)(function () {
if (!hasRealHeight) {
updateItemsSize();
} // 获得当前可视区域第一项
var scrollTop = divRef.current.scrollTop; // 获得开始索引、结束索引
getStartIndex(scrollTop);
setStartOffset();
});
(0, _react.useEffect)(function () {
divRef.current.addEventListener('scroll', handleScroll);
return function () {
divRef.current.removeEventListener('scroll', handleScroll);
};
}, []);
return /*#__PURE__*/_react.default.createElement("div", {
className: (0, _classnames.default)("".concat(prefixCls, "-container"), className),
style: {
height: height ? height : '100vh'
},
ref: divRef
}, /*#__PURE__*/_react.default.createElement("div", {
className: "".concat(prefixCls, "-phantom"),
style: {
height: (_positionRef$current2 = positionRef.current[positionRef.current.length - 1]) === null || _positionRef$current2 === void 0 ? void 0 : _positionRef$current2.bottom
}
}), /*#__PURE__*/_react.default.createElement("div", {
className: (0, _classnames.default)(prefixCls, showClassName),
ref: listRef
}, Array.isArray(visibleData) && visibleData.map(function (item, index) {
return /*#__PURE__*/_react.default.createElement("div", {
key: (item === null || item === void 0 ? void 0 : item.id) || index,
className: "zq-visible-data",
id: item._index,
ref: function ref(el) {
return virtualListRef.current[index] = el;
}
}, typeof renderItem === 'function' ? renderItem(item, index, dataSource) : null);
})));
};
var _default = VirtualList;
exports.default = _default;