template

<scroll-view scroll-y class="supp-scroll right-scroll-view" :scroll-into-view="toViewId" 
	scroll-with-animation="true" @scroll="handleScroll">
	<view v-for="(item,index) in 100" :key="item">
		<view class="name-sj" :id="'item'+index">商家报价</view>
	</view>
</scroll-view>

js

// 滚动位置
	const toViewId = ref<string>('item0')
	// 滚动到顶部
	const topScroll = ref<string>('')
	// 当前可见的 id
	const currentVisibleId = ref<string>('')
	// 存储每个元素的位置信息
	const itemPositions = ref<{}>({})
	
// 点击左边数据重新请求自动置顶
	const upReste = async ()=>{
		await nextTick()
		setTimeout(async () => {
			toViewId.value = 'topScroll'
			const positions = await getItemPositions()
			itemPositions.value = positions
			// 修改:获取位置后触发第一个区域
			if (Object.keys(positions).length > 0) {
				const sorted = Object.keys(positions).sort((a, b) => positions[a].top - positions[b].top)
				const firstId = sorted[0]
				if (currentVisibleId.value !== firstId) {
					handleReachArea(firstId)
				}
			}
		}, 300)
	}
	
// ---------- 业务逻辑(到达某个区域时触发) ----------
	const handleReachArea = (id) => {
		// 记录当前滚动的item
		currentVisibleId.value = id
		// 赋值当前左边的滚动选择选项
		childIndex.value = getTrailingNumber(id) ? Number(getTrailingNumber(id)) : 0
	}

	// ---------- 获取每个元素的位置信息 ----------
	// props.processingObject 是数据一共多少条
	const instance = getCurrentInstance();
	const getItemPositions = () => {
		return new Promise((resolve) => {
			if (!instance) {
				console.error('组件实例不存在');
				resolve({});
				return;
			}
			const positions = {};
			const query = uni.createSelectorQuery().in(instance);
			let completed = 0;
			props.processingObject.forEach((id, index) => {
				query.select(`#item${index}`).boundingClientRect((rect) => {
					if (rect) {
						positions[`item${index}`] = rect;
					} else {
						console.warn(`未找到元素 #${index}`);
					}
					completed++;
					if (completed === props.processingObject.length) {
						// 修改:使用独立类名查询右侧 scroll-view
						const scrollQuery = uni.createSelectorQuery().in(instance);
						scrollQuery.select('.right-scroll-view').boundingClientRect((scrollRect) => {
							if (!scrollRect) {
								resolve({});
								return;
							}
							const result = {};
							props.processingObject.forEach((id, index) => {
								const rect = positions[`item${index}`];
								if (rect) {
									result[`item${index}`] = {
										top: rect.top - scrollRect.top,
										height: rect.height
									};
								}
							});
							resolve(result);
						}).exec();
					}
				}).exec();
			});
		});
	}

	// ---------- 滚动事件处理(重写:增加顶部/底部边界) ----------
	const handleScroll = (e) => {
		const scrollTop = e.detail.scrollTop;
		const positions = itemPositions.value;
		const ids = Object.keys(positions);
		if (ids.length === 0) return;

		// 按 top 排序
		const sorted = ids.slice().sort((a, b) => positions[a].top - positions[b].top);
		const firstId = sorted[0];
		const lastId = sorted[sorted.length - 1];
		const firstTop = positions[firstId].top;
		const lastBottom = positions[lastId].top + positions[lastId].height;

		let visibleId = '';

		if (scrollTop < firstTop) {
			// 滚动到顶部:第一个区域可见
			visibleId = firstId;
		} else if (scrollTop >= lastBottom) {
			// 滚动到底部:最后一个区域可见
			visibleId = lastId;
		} else {
			// 正常区间判断
			for (let id of sorted) {
				const { top, height } = positions[id];
				if (scrollTop >= top && scrollTop < top + height) {
					visibleId = id;
					break;
				}
			}
			// 若落在间隙(极少情况),取下一个区域
			if (!visibleId) {
				for (let i = 0; i < sorted.length; i++) {
					const id = sorted[i];
					if (scrollTop < positions[id].top) {
						visibleId = id;
						break;
					}
				}
				if (!visibleId) visibleId = lastId;
			}
		}

		if (visibleId && currentVisibleId.value !== visibleId) {
			handleReachArea(visibleId);
		}
	}

	// ---------- 监听 processingObject 变化,更新位置 ----------
	watch(() => props.processingObject, async (newVal) => {
		if (newVal && newVal.length > 0) {
			await nextTick();
			setTimeout(async () => {
				const positions = await getItemPositions();
				itemPositions.value = positions;
				if (Object.keys(positions).length > 0) {
					const sorted = Object.keys(positions).sort((a, b) => positions[a].top - positions[b].top);
					const firstId = sorted[0];
					if (currentVisibleId.value !== firstId) {
						handleReachArea(firstId);
					}
				}
			}, 300);
		}
	}, { immediate: true, deep: false });

	// 判断滚动选中
	function getTrailingNumber(str) {
		let num = '';
		for (let i = str.length - 1; i >= 0; i--) {
			const char = str[i];
			if (char >= '0' && char <= '9') {
				num = char + num;
			} else {
				break;
			}
		}
		return num;
	}

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐