方案1:計算何時的滾動位置
獲取當前點選元素的offsetLeft,然後將offsetLeft設定為容器的scrollLeft。
雖然這樣可以使當前點選元素處於可視區域,但是會導致無法點選前一個元素。
解決辦法是用offsetLeft減去容器寬度的一半,這樣可以讓當前點選元素展示在容器中間,問題就解決了。
<script setup lang="ts">
import { ref } from 'vue';
const containerRef = ref<HTMLElement | null>(null);
const items = ref([
'item1',
'item2',
'item3',
'item4',
'item5',
'item6',
'item7',
'item8',
'item9',
'item10',
]);
// 加上了target.offsetWidth / 2,是為了讓居中操作兼顧到當前點選元素自身的寬度
const getScrollPosition = (target: HTMLElement, container: HTMLElement) =>
target.offsetLeft - container.offsetWidth / 2 + target.offsetWidth / 2;
const onClick = (event: MouseEvent) => {
const container = containerRef.value;
const currentTarget = event.currentTarget
if (container && currentTarget instanceof HTMLElement) {
container.scrollTo({
left: getScrollPosition(currentTarget, container),
behavior: "smooth"
});
}
};
</script>
<template>
<nav ref="containerRef">
<span
v-for="(item, index) in items"
:key="index"
@click="onClick($event)"
>
{{ item }}
</span>
</nav>
</template>
<style lang="less" scoped>
nav {
display: flex;
flex-wrap: nowrap;
overflow: auto;
}
</style>
注意:scrollTo的left有效值為0和scrollWidth之間,所以不用擔心給容器設定了非法的scrollLeft
方案2:藉助scrollIntoView方法
scrollIntoView可以將元素滾動到可視區域
<script setup lang="ts">
import { ref } from 'vue';
const items = ref([
'item1',
'item2',
'item3',
'item4',
'item5',
'item6',
'item7',
'item8',
'item9',
'item10',
]);
const onClick = (event: MouseEvent) => {
const currentTarget = event.currentTarget;
if (currentTarget instanceof HTMLElement) {
currentTarget.scrollIntoView({
behavior: "smooth",
block: "nearest",
inline: "center"
});
}
};
</script>
<template>
<nav>
<span
v-for="(item, index) in items"
:key="index"
@click="onClick($event)"
>
{{ item }}
</span>
</nav>
</template>
<style lang="less" scoped>
nav {
display: flex;
flex-wrap: nowrap;
overflow: auto;
}
</style>
改進為宣告式:
<script setup lang="ts">
import { ref, watch } from 'vue';
const items = ref([
'item1',
'item2',
'item3',
'item4',
'item5',
'item6',
'item7',
'item8',
'item9',
'item10',
]);
const activeIndex = ref(0);
const itemRefs = ref<HTMLElement[]>([]);
watch(activeIndex, (value: number) => {
const targetItem = itemRefs.value[value]
if (targetItem) {
targetItem.scrollIntoView({
behavior: "smooth",
block: "nearest",
inline: "center"
})
}
})
</script>
<template>
<nav>
<span
v-for="(item, index) in items"
:key="index"
ref="itemRefs"
@click="activeIndex = index"
>
{{ item }}
</span>
</nav>
</template>
<style>
nav {
display: flex;
flex-wrap: nowrap;
overflow: auto;
}
</style>