129 lines
3.5 KiB
Vue
129 lines
3.5 KiB
Vue
|
<template>
|
|||
|
<view class="container">
|
|||
|
<scroll-view id="scrollContainer" ref="scrollViewRef" class="scroll-view" scroll-y :scroll-top="scrollTop"
|
|||
|
@scroll="onScroll">
|
|||
|
<view v-for="(item, index) in items" :key="index" class="item" :class="{ active: index === activeIndex }"
|
|||
|
:style="{ marginLeft: marginList[index] + 'rpx' }">
|
|||
|
{{ item }}
|
|||
|
</view>
|
|||
|
</scroll-view>
|
|||
|
</view>
|
|||
|
</template>
|
|||
|
|
|||
|
<script setup>
|
|||
|
import {
|
|||
|
ref,
|
|||
|
onMounted,
|
|||
|
nextTick,
|
|||
|
getCurrentInstance
|
|||
|
} from 'vue';
|
|||
|
|
|||
|
const {
|
|||
|
proxy
|
|||
|
} = getCurrentInstance();
|
|||
|
const scrollViewRef = ref(null);
|
|||
|
// 原始列表数据及状态
|
|||
|
const originalItems = Array.from({
|
|||
|
length: 20
|
|||
|
}, (_, i) => `列表项 ${i+1}`);
|
|||
|
const items = ref([]);
|
|||
|
const marginList = ref([]);
|
|||
|
const activeIndex = ref(0);
|
|||
|
const scrollTop = ref(0);
|
|||
|
let offsetCount = 0;
|
|||
|
|
|||
|
// 计算各项 margin 并更新高亮
|
|||
|
function updateMargins(scrollY) {
|
|||
|
const query = uni.createSelectorQuery().in(proxy);
|
|||
|
query.select('#scrollContainer').boundingClientRect();
|
|||
|
query.selectAll('.item').boundingClientRect();
|
|||
|
query.exec(res => {
|
|||
|
const [scrollRect, itemRects] = res;
|
|||
|
if (!scrollRect || !itemRects) return;
|
|||
|
|
|||
|
const centerY = scrollRect.height / 2;
|
|||
|
const maxMargin = 300; // ← 从 60 调到 120,偏移加倍
|
|||
|
const maxDist = centerY;
|
|||
|
|
|||
|
itemRects.forEach((r, i) => {
|
|||
|
const itemCenter = r.top - scrollRect.top + r.height / 2;
|
|||
|
const dist = Math.abs(itemCenter - centerY);
|
|||
|
const t = Math.min(dist / maxDist, 1);
|
|||
|
const eased = 1 - Math.cos(t * Math.PI / 2);
|
|||
|
const m = eased * maxMargin;
|
|||
|
|
|||
|
marginList.value[i] = Math.round(m);
|
|||
|
});
|
|||
|
|
|||
|
// 剩下高亮逻辑不变
|
|||
|
const slice = marginList.value.slice(offsetCount, offsetCount + originalItems.length);
|
|||
|
const minM = Math.min(...slice);
|
|||
|
activeIndex.value = slice.indexOf(minM) + offsetCount;
|
|||
|
});
|
|||
|
}
|
|||
|
// 滚动时触发
|
|||
|
const scrollTimeout = ref(null);
|
|||
|
|
|||
|
const onScroll = (e) => {
|
|||
|
nextTick(() => updateMargins(e.detail.scrollTop));
|
|||
|
|
|||
|
// 检测停止滚动
|
|||
|
if (scrollTimeout.value) clearTimeout(scrollTimeout.value);
|
|||
|
scrollTimeout.value = setTimeout(() => {
|
|||
|
// 滚动停止后的处理逻辑
|
|||
|
console.log('滚动已停止');
|
|||
|
console.log("??????????????", activeIndex.value - 8)
|
|||
|
// 你可以在这里触发居中对齐、播放动画等
|
|||
|
}, 100); // 100ms 内未触发 scroll,判定为停止
|
|||
|
};
|
|||
|
|
|||
|
onMounted(async () => {
|
|||
|
// 初始先用原始列表测量高度
|
|||
|
items.value = originalItems;
|
|||
|
marginList.value = Array(items.value.length).fill(0);
|
|||
|
await nextTick();
|
|||
|
const query = uni.createSelectorQuery().in(proxy);
|
|||
|
query.select('#scrollContainer').boundingClientRect();
|
|||
|
query.selectAll('.item').boundingClientRect();
|
|||
|
query.exec(res => {
|
|||
|
const [scrollRect, itemRects] = res;
|
|||
|
if (!scrollRect || !itemRects || !itemRects.length) return;
|
|||
|
const itemH = itemRects[0].height;
|
|||
|
offsetCount = Math.ceil((scrollRect.height / 2) / itemH) + 3;
|
|||
|
// 重构带占位的列表
|
|||
|
items.value = [
|
|||
|
...Array(offsetCount).fill(''),
|
|||
|
...originalItems,
|
|||
|
...Array(offsetCount).fill(''),
|
|||
|
];
|
|||
|
marginList.value = Array(items.value.length).fill(0);
|
|||
|
// 设置初始滚动位置
|
|||
|
scrollTop.value = offsetCount * itemH;
|
|||
|
nextTick(() => updateMargins(scrollTop.value));
|
|||
|
});
|
|||
|
});
|
|||
|
</script>
|
|||
|
|
|||
|
<style scoped>
|
|||
|
.container {
|
|||
|
height: 800rpx;
|
|||
|
position: absolute;
|
|||
|
bottom: 300rpx;
|
|||
|
right: 100rpx;
|
|||
|
overflow: hidden;
|
|||
|
z-index: 100;
|
|||
|
}
|
|||
|
|
|||
|
.scroll-view {
|
|||
|
height: 100%;
|
|||
|
}
|
|||
|
|
|||
|
.item {
|
|||
|
padding: 10px;
|
|||
|
/* transition: color 0.2s; */
|
|||
|
}
|
|||
|
|
|||
|
.active {
|
|||
|
color: red;
|
|||
|
}
|
|||
|
</style>
|