UNPKG

z-react-ui

Version:

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

222 lines (193 loc) 7.53 kB
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2"; import _typeof from "@babel/runtime/helpers/esm/typeof"; import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray"; import React, { useEffect, useMemo, useRef } from 'react'; import { usePrefixCls, useValues } from '@/_hooks'; import { usePersistFn } from 'ahooks'; import classNames from '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 = usePrefixCls('virtual-list'); var paramsRef = useRef(); paramsRef.current = dataSource; // 容器 var divRef = useRef(); var listRef = useRef(); var _useValues = useValues({ //可视区域高度 clientHeight: 0, //起始索引 start: 0, //结束索引 end: 0 }), _useValues2 = _slicedToArray(_useValues, 2), value = _useValues2[0], setValue = _useValues2[1]; var _useMemo = useMemo(function () { return value; }, [value]), clientHeight = _useMemo.clientHeight, start = _useMemo.start, end = _useMemo.end; var _useMemo2 = useMemo(function () { var itemSize = estimatedItemSize; var hasRealHeight = false; switch (_typeof(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 = useMemo(function () { return Math.ceil(clientHeight / itemSize); }, [clientHeight, itemSize]); // 可视区域上方的加载列表数量 var aboveCount = useMemo(function () { return Math.min(start, bufferScale * visibleCount); }, [start, bufferScale, visibleCount]); // 可视区域下方的加载列表数量 var belowCount = useMemo(function () { return Math.min(paramsRef.current.length - end, bufferScale * visibleCount); }, [end, bufferScale, visibleCount]); // 可视区域数据 var visibleData = useMemo(function () { var startIndex = start - aboveCount; var endIndex = end + belowCount; return paramsRef.current.map(function (item, index) { return _objectSpread(_objectSpread({}, item), {}, { _index: index }); }).slice(startIndex, endIndex); }, [start, aboveCount, end, belowCount]); // 位置信息 var positionRef = useRef(); // 只加载一次 useMemo(function () { positionRef.current = paramsRef.current.map(function (d, index) { return { index: index, height: itemSize, top: index * itemSize, bottom: (index + 1) * itemSize }; }); }, [itemSize, hasRealHeight]); useEffect(function () { setValue({ clientHeight: divRef.current.clientHeight, start: 0, end: visibleCount }); }, [visibleCount]); var virtualListRef = useRef([]); //获取列表项的当前尺寸 var updateItemsSize = 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 = 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 = 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 = 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 = usePersistFn(function () { if (!hasRealHeight) { updateItemsSize(); } // 获得当前可视区域第一项 var scrollTop = divRef.current.scrollTop; // 获得开始索引、结束索引 getStartIndex(scrollTop); setStartOffset(); }); useEffect(function () { divRef.current.addEventListener('scroll', handleScroll); return function () { divRef.current.removeEventListener('scroll', handleScroll); }; }, []); return /*#__PURE__*/React.createElement("div", { className: classNames("".concat(prefixCls, "-container"), className), style: { height: height ? height : '100vh' }, ref: divRef }, /*#__PURE__*/React.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.createElement("div", { className: classNames(prefixCls, showClassName), ref: listRef }, Array.isArray(visibleData) && visibleData.map(function (item, index) { return /*#__PURE__*/React.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); }))); }; export default VirtualList;