gui-one-nutui-react-taro
Version:
京东风格的轻量级移动端 React 组件库,支持一套代码生成 H5 和小程序
316 lines (314 loc) • 12.7 kB
JavaScript
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
var _excluded = ["sourceData", "ItemRender", "itemEqualSize", "itemSize", "horizontal", "overscan", "key", "handleScroll", "onScroll", "className", "containerSize"];
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
import React__default, { useRef, useState, useEffect, useCallback } from 'react';
import { u as useConfig } from './configprovider.taro-6c7b3056.js';
// 缓存列表初始化信息
var initPositinoCache = function initPositinoCache(reaItemSize) {
var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
var index = 0;
var positions = Array(length);
while (index < length) {
positions[index] = {
index: index,
height: reaItemSize,
width: reaItemSize,
top: index * reaItemSize,
bottom: (index + 1) * reaItemSize,
left: index * reaItemSize,
right: (index + 1) * reaItemSize
};
index++;
}
return positions;
};
// 获取列表总高度
var getListTotalSize = function getListTotalSize(positions, horizontal) {
var index = positions.length - 1;
var size = 0;
if (index < 0) {
size = 0;
} else {
size = horizontal ? positions[index].right : positions[index].bottom;
}
return size;
};
// 通过二分法找到 scrollOffset 对应的值
var binarySearch = function binarySearch(positionsList) {
var value = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
var horizontal = arguments.length > 2 ? arguments[2] : undefined;
var start = 0;
var end = positionsList.length - 1;
var tempIndex = null;
var key = horizontal ? 'right' : 'bottom';
while (start <= end) {
var midIndex = Math.floor((start + end) / 2);
var midValue = positionsList[midIndex][key];
// 相等则直接返回(因为是bottom, 因此startIndex应该是下一个节点)
if (midValue === value) {
return midIndex + 1;
}
// 中间值 < 传入值,则说明 value对应的节点 大于 start, start往后移动一位
if (midValue < value) {
start = midIndex + 1;
}
// 中间值 > 传入值,则说明 value 在 中间值之前,end 节点移动到 mid - 1
else if (midValue > value) {
// tempIndex存放最靠近值为value的所有
if (tempIndex === null || tempIndex > midIndex) {
tempIndex = midIndex;
}
end = midIndex - 1;
}
}
tempIndex = tempIndex || 0;
return tempIndex;
};
var getEndIndex = function getEndIndex(_ref) {
var sourceData = _ref.sourceData,
startIndex = _ref.startIndex,
visibleCount = _ref.visibleCount,
_ref$itemEqualSize = _ref.itemEqualSize,
itemEqualSize = _ref$itemEqualSize === void 0 ? true : _ref$itemEqualSize,
positions = _ref.positions,
offSetSize = _ref.offSetSize,
overscan = _ref.overscan,
_ref$sizeKey = _ref.sizeKey,
sizeKey = _ref$sizeKey === void 0 ? 'width' : _ref$sizeKey;
var dataLength = sourceData.length;
var tempIndex = null;
if (itemEqualSize) {
var endIndex = startIndex + visibleCount;
tempIndex = dataLength > 0 ? Math.min(dataLength, endIndex) : endIndex;
} else {
var sizeNum = 0;
for (var i = startIndex; i < dataLength; i++) {
sizeNum += positions[i][sizeKey] || 0;
if (sizeNum > offSetSize) {
var _endIndex = i + overscan;
tempIndex = dataLength > 0 ? Math.min(dataLength, _endIndex) : _endIndex;
break;
}
}
if (sizeNum < offSetSize) {
tempIndex = dataLength;
}
}
tempIndex = tempIndex || 0;
return tempIndex;
};
// 更新Item大小
var updateItemSize = function updateItemSize(positions, items, sizeKey) {
var newPos = positions.concat();
Array.from(items).forEach(function (item) {
var index = Number(item.getAttribute('data-index'));
var styleVal = item.getAttribute('style');
if (styleVal && styleVal.includes('none')) return;
var nowSize = item.getBoundingClientRect()[sizeKey];
var oldSize = positions[index][sizeKey];
// 存在差值, 更新该节点以后所有的节点
var dValue = oldSize - nowSize;
if (dValue) {
if (sizeKey === 'width') {
newPos[index].right -= dValue;
newPos[index][sizeKey] = nowSize;
for (var k = index + 1; k < positions.length; k++) {
newPos[k].left = positions[k - 1].right;
newPos[k].right -= dValue;
}
} else if (sizeKey === 'height') {
newPos[index].bottom -= dValue;
newPos[index][sizeKey] = nowSize;
for (var _k = index + 1; _k < positions.length; _k++) {
newPos[_k].top = positions[_k - 1].bottom;
newPos[_k].bottom -= dValue;
}
}
}
});
};
var defaultProps = {};
var VirtualList = function VirtualList(props) {
var _props$sourceData = props.sourceData,
sourceData = _props$sourceData === void 0 ? [] : _props$sourceData,
ItemRender = props.ItemRender,
_props$itemEqualSize = props.itemEqualSize,
itemEqualSize = _props$itemEqualSize === void 0 ? true : _props$itemEqualSize,
_props$itemSize = props.itemSize,
itemSize = _props$itemSize === void 0 ? 200 : _props$itemSize,
_props$horizontal = props.horizontal,
horizontal = _props$horizontal === void 0 ? false : _props$horizontal,
_props$overscan = props.overscan,
overscan = _props$overscan === void 0 ? 2 : _props$overscan,
key = props.key,
handleScroll = props.handleScroll,
onScroll = props.onScroll,
className = props.className,
containerSize = props.containerSize,
rest = _objectWithoutProperties(props, _excluded);
var sizeKey = horizontal ? 'width' : 'height';
var scrollKey = horizontal ? 'scrollLeft' : 'scrollTop';
var offsetKey = horizontal ? 'left' : 'top';
useConfig();
// 虚拟列表容器ref
var scrollRef = useRef(null);
// 虚拟列表显示区域ref
var itemsRef = useRef(null);
var firstItemRef = useRef(null);
// 列表位置信息
var _useState = useState([{
index: 0,
left: 0,
top: 0,
bottom: 0,
width: 0,
height: 0,
right: 0
}]),
_useState2 = _slicedToArray(_useState, 2),
positions = _useState2[0],
setPositions = _useState2[1];
// 列表总大小
var _useState3 = useState(99999999),
_useState4 = _slicedToArray(_useState3, 2),
listTotalSize = _useState4[0],
setListTotalSize = _useState4[1];
// 可视区域条数
var _useState5 = useState(0),
_useState6 = _slicedToArray(_useState5, 2),
visibleCount = _useState6[0],
setVisibleCount = _useState6[1];
var _useState7 = useState(containerSize || 0),
_useState8 = _slicedToArray(_useState7, 2),
offSetSize = _useState8[0],
setOffSetSize = _useState8[1];
var _useState9 = useState({
startOffset: 0,
startIndex: 0,
overStart: 0,
endIndex: 10 // 可视区域结束索引
}),
_useState10 = _slicedToArray(_useState9, 2),
options = _useState10[0],
setOptions = _useState10[1];
// 列表位置信息
useEffect(function () {
var pos = initPositinoCache(itemSize, sourceData.length);
setPositions(pos);
var totalSize = getListTotalSize(pos, horizontal);
setListTotalSize(totalSize);
}, [sourceData, itemSize, horizontal]);
var getElement = useCallback(function () {
var _scrollRef$current;
return ((_scrollRef$current = scrollRef.current) === null || _scrollRef$current === void 0 ? void 0 : _scrollRef$current.parentElement) || document.body;
}, []);
useEffect(function () {
if (containerSize) return;
var size = horizontal ? getElement().offsetWidth : getElement().offsetHeight;
setOffSetSize(size);
}, [getElement, horizontal, containerSize]);
useEffect(function () {
// 初始-计算visibleCount
if (offSetSize === 0) return;
var count = Math.ceil(offSetSize / itemSize) + overscan;
setVisibleCount(count);
setOptions(function (options) {
return _objectSpread(_objectSpread({}, options), {}, {
endIndex: count
});
});
}, [getElement, horizontal, itemSize, overscan, offSetSize]);
var updateTotalSize = useCallback(function () {
if (!itemsRef.current) return;
var items = itemsRef.current.children;
if (!items.length) return;
// 更新缓存
updateItemSize(positions, items, sizeKey);
var totalSize = getListTotalSize(positions, horizontal);
setListTotalSize(totalSize);
}, [positions, sizeKey, horizontal]);
var scroll = useCallback(function () {
requestAnimationFrame(function (e) {
var scrollSize = getElement()[scrollKey];
var startIndex = binarySearch(positions, scrollSize, horizontal);
var overStart = startIndex - overscan > -1 ? startIndex - overscan : 0;
// const offSetSize = horizontal ? getElement().offsetWidth : getElement().offsetHeight
if (!itemEqualSize) {
updateTotalSize();
}
var endIndex = getEndIndex({
sourceData: sourceData,
startIndex: startIndex,
visibleCount: visibleCount,
itemEqualSize: itemEqualSize,
positions: positions,
offSetSize: offSetSize,
sizeKey: sizeKey,
overscan: overscan
});
var startOffset = positions[startIndex][offsetKey];
setOptions({
startOffset: startOffset,
startIndex: startIndex,
overStart: overStart,
endIndex: endIndex
});
// 无限下滑
if (endIndex > sourceData.length - 1) {
if (onScroll) {
onScroll();
} else if (handleScroll) {
handleScroll();
}
}
});
}, [positions, getElement, sourceData, visibleCount, itemEqualSize, updateTotalSize, offsetKey, sizeKey, scrollKey, horizontal, overscan, handleScroll, offSetSize]);
useEffect(function () {
var element = getElement();
element.addEventListener('scroll', scroll, false);
return function () {
element.removeEventListener('scroll', scroll, false);
};
}, [getElement, scroll]);
return React__default.createElement("div", _objectSpread(_objectSpread({
className: className ? "".concat(className, " nut-virtualList-box") : 'nut-virtualList-box'
}, rest), {}, {
style: _defineProperty({}, sizeKey, containerSize ? "".concat(offSetSize, "px") : '')
}), React__default.createElement("div", {
ref: scrollRef,
className: horizontal ? 'nut-horizontal-box' : 'nut-vertical-box',
style: _defineProperty({
position: 'relative'
}, sizeKey, "".concat(listTotalSize, "px"))
}, React__default.createElement("ul", {
className: horizontal ? 'nut-virtuallist-items nut-horizontal-items' : 'nut-virtuallist-items nut-vertical-items',
ref: itemsRef,
style: {
transform: horizontal ? "translate3d(".concat(options.startOffset, "px,0,0)") : "translate3d(0,".concat(options.startOffset, "px,0)")
}
}, sourceData.slice(options.overStart, options.endIndex).map(function (data, index) {
var startIndex = options.startIndex,
overStart = options.overStart;
var dataIndex = overStart + index;
var styleVal = dataIndex < startIndex ? 'none' : 'block';
var keyVal = key && data[key] ? data[key] : dataIndex;
return React__default.createElement("li", {
ref: dataIndex === 0 ? firstItemRef : null,
"data-index": "".concat(dataIndex),
className: dataIndex % 2 === 0 ? 'nut-virtuallist-item even' : 'nut-virtuallist-item odd',
key: "".concat(keyVal),
style: {
display: styleVal
}
}, ItemRender ? React__default.createElement(ItemRender, {
data: data,
index: "".concat(dataIndex)
}) : data);
}))));
};
VirtualList.defaultProps = defaultProps;
VirtualList.displayName = 'NutVirtualList';
export { VirtualList as V };