UNPKG

z-react-ui

Version:

z-react-ui,是一款基于 Dumi,由 React + TypeScript 开发的组件库 🎉。

241 lines (202 loc) 8.29 kB
"use strict"; 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;