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> |