z-react-ui
Version:
z-react-ui,是一款基于 Dumi,由 React + TypeScript 开发的组件库 🎉。
222 lines (193 loc) • 7.53 kB
JavaScript
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;