方案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>
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。