UNPKG

gui-one-nutui-react-taro

Version:

京东风格的轻量级移动端 React 组件库,支持一套代码生成 H5 和小程序

316 lines (314 loc) 12.7 kB
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 };