@nutui/nutui
Version:
京东风格的轻量级移动端 Vue2、Vue3 组件库(支持小程序开发)
267 lines (266 loc) • 8.89 kB
JavaScript
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
import { ref, reactive, computed, watch, toRefs, openBlock, createElementBlock, normalizeStyle, createElementVNode, Fragment, renderList, renderSlot } from "vue";
import { c as createComponent } from "../component-DQf3CENX.js";
import { u as useRect } from "../index-B1qsj2XR.js";
import { _ as _export_sfc } from "../_plugin-vue_export-helper-1tPrXgE0.js";
var CompareResult = /* @__PURE__ */ ((CompareResult2) => {
CompareResult2[CompareResult2["eq"] = 1] = "eq";
CompareResult2[CompareResult2["lt"] = 2] = "lt";
CompareResult2[CompareResult2["gt"] = 3] = "gt";
return CompareResult2;
})(CompareResult || {});
function binarySearch(list, value, compareFunc) {
let start = 0;
let end = list.length - 1;
let tempIndex = null;
while (start <= end) {
tempIndex = Math.floor((start + end) / 2);
const midValue = list[tempIndex];
const compareRes = compareFunc(midValue, value);
if (compareRes === 1) {
return tempIndex;
}
if (compareRes === 2) {
start = tempIndex + 1;
} else if (compareRes === 3) {
end = tempIndex - 1;
}
}
return tempIndex;
}
const { create } = createComponent("list");
const _sfc_main = create({
props: {
listData: {
type: Array,
default: () => {
return [];
}
},
bufferSize: {
type: Number,
default: 5
},
containerHeight: {
type: Number
},
height: {
type: Number,
default: 80
},
margin: {
type: Number,
default: 10
}
},
emits: ["scrollUp", "scrollDown", "scrollBottom"],
setup(props, { emit }) {
const clientHeight = document.documentElement.clientHeight || document.body.clientHeight || 667;
const list = ref(null);
const phantom = ref(null);
const actualContent = ref(null);
const state = reactive({
start: 0,
originStartIndex: 0,
scrollTop: 0,
list: props.listData.slice(),
cachePositions: [],
phantomHeight: props.height * props.listData.length
});
const getContainerHeight = computed(() => {
if (props.containerHeight) {
return Math.min(props.containerHeight, clientHeight);
}
return clientHeight;
});
const visibleCount = computed(() => {
return Math.ceil(getContainerHeight.value / props.height);
});
const end = computed(() => {
return Math.min(state.originStartIndex + visibleCount.value + props.bufferSize, state.list.length);
});
const visibleData = computed(() => {
return state.list.slice(state.start, end.value);
});
const getTransform = () => {
if (actualContent.value) {
return `translate3d(0, ${state.start >= 1 ? state.cachePositions[state.start - 1].bottom : 0}px, 0)`;
}
};
const initCachedPosition = () => {
state.cachePositions = [];
for (let i = 0; i < state.list.length; ++i) {
state.cachePositions[i] = {
index: i,
height: props.height,
top: i * props.height,
bottom: (i + 1) * (props.height + props.margin),
dValue: 0
};
}
};
const updateCachedPosition = () => {
let nodes = actualContent.value.childNodes;
nodes = Array.from(nodes).filter((node) => node.nodeType === 1);
const start = nodes[0];
nodes.forEach((node, index2) => {
if (!node) return;
const rect = useRect(node);
const { height: height2 } = rect;
const oldHeight = state.cachePositions[index2 + state.start].height;
const dValue = oldHeight - height2;
if (dValue) {
state.cachePositions[index2 + state.start].bottom -= dValue;
state.cachePositions[index2 + state.start].height = height2;
state.cachePositions[index2 + state.start].dValue = dValue;
}
});
let startIndex = 0;
if (start) {
startIndex = state.start;
}
const cachedPositionsLen = state.cachePositions.length;
let cumulativeDiffHeight = state.cachePositions[startIndex].dValue;
state.cachePositions[startIndex].dValue = 0;
for (let i = startIndex + 1; i < cachedPositionsLen; ++i) {
const item = state.cachePositions[i];
state.cachePositions[i].top = state.cachePositions[i - 1].bottom;
state.cachePositions[i].bottom = state.cachePositions[i].bottom - cumulativeDiffHeight;
if (item.dValue !== 0) {
cumulativeDiffHeight += item.dValue;
item.dValue = 0;
}
}
const height = state.cachePositions[cachedPositionsLen - 1].bottom;
state.phantomHeight = height;
};
const getStartIndex = (scrollTop = 0) => {
let idx = binarySearch(
state.cachePositions,
scrollTop,
(currentValue, targetValue) => {
const currentCompareValue = currentValue.bottom;
if (currentCompareValue === targetValue) {
return CompareResult.eq;
}
if (currentCompareValue < targetValue) {
return CompareResult.lt;
}
return CompareResult.gt;
}
);
const targetItem = state.cachePositions[idx];
if (targetItem.bottom < scrollTop) {
idx += 1;
}
return idx;
};
const resetAllVirtualParam = () => {
state.originStartIndex = 0;
state.start = 0;
state.scrollTop = 0;
list.value.scrollTop = 0;
initCachedPosition();
state.phantomHeight = props.height * state.list.length;
};
const handleScrollEvent = () => {
var _a;
const scrollTop = (_a = list.value) == null ? void 0 : _a.scrollTop;
const { originStartIndex } = state;
const currentIndex = getStartIndex(scrollTop);
if (currentIndex !== originStartIndex) {
state.originStartIndex = currentIndex;
state.start = Math.max(state.originStartIndex - props.bufferSize, 0);
if (end.value >= state.list.length - 1) {
emit("scrollBottom");
}
}
emit(scrollTop > state.scrollTop ? "scrollUp" : "scrollDown", scrollTop);
state.scrollTop = scrollTop;
};
initCachedPosition();
watch(
() => props.listData,
(val) => {
state.list = val.slice();
if (state.list.length === val.length) {
initCachedPosition();
updateCachedPosition();
} else {
resetAllVirtualParam();
return;
}
}
);
watch(
() => state.start,
() => {
if (actualContent.value && state.list.length > 0) {
updateCachedPosition();
}
}
);
return __spreadProps(__spreadValues({}, toRefs(state)), {
list,
phantom,
actualContent,
getTransform,
visibleData,
getContainerHeight,
handleScrollEvent
});
}
});
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
return openBlock(), createElementBlock("div", {
ref: "list",
class: "nut-list",
style: normalizeStyle({ height: `${_ctx.getContainerHeight}px` }),
onScrollPassive: _cache[0] || (_cache[0] = (...args) => _ctx.handleScrollEvent && _ctx.handleScrollEvent(...args))
}, [
createElementVNode("div", {
ref: "phantom",
class: "nut-list-phantom",
style: normalizeStyle({ height: _ctx.phantomHeight + "px" })
}, null, 4),
createElementVNode("div", {
ref: "actualContent",
class: "nut-list-container",
style: normalizeStyle({ transform: _ctx.getTransform() })
}, [
(openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.visibleData, (item, index2) => {
return openBlock(), createElementBlock("div", {
key: item,
class: "nut-list-item"
}, [
renderSlot(_ctx.$slots, "default", {
item,
index: index2 + _ctx.start
})
]);
}), 128))
], 4)
], 36);
}
const index = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render]]);
export {
index as default
};