转载需注明出处
问题描述
graph TD
首次进入界面 --> 有聊天数据-->滑动到最低部
首次进入界面 --> 无聊天数据--> 滑动到最低部-->聊天中-->用户未滚动鼠标-->自动滑动开启
聊天中-->用户滚动鼠标-->滚动到最底部-->自动滚动开启
用户滚动鼠标-->未滚动到最底部-->禁止自动滑动
实现思路
- scrollIntoview可以将dom滑动到可视区域
- 聊天框每次移动到最底部,可以看作将最底部的一个div设置为可视区域最底端
解决办法
1. 开启界面
对于刚进入界面来说,直接滑动最底部
<template>
<div>
<div class="content">
<!-- 这里放置你的内容 -->
<p v-for="item in 100" :key="item">{{ item }}</p>
<div ref="bottomDivRef"></div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, nextTick } from 'vue';
const bottomDivRef = ref(null);
const scrollToBottom = () => {
// 确保 DOM 更新后再执行滚动
nextTick(() => {
if (bottomDivRef.value) {
bottomDivRef.value.scrollIntoView({
behavior: 'smooth',
block: 'end'
});
}
});
};
// 如果需要在组件加载完成后自动滚动到最底部,可以这样做:
onMounted(() => {
scrollToBottom();
});
</script>
<style scoped>
.content {
height: 200px; /* 或者其他的固定高度 */
overflow-y: auto;
background: red;
}
</style>
2. 开始聊天
如果在用户聊天中需自动滚动到底部
<template>
<div>
<div class="content">
<!-- 这里放置你的内容 -->
<p v-for="item in items" :key="item">{{ item }}</p>
<div ref="bottomDivRef"></div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, nextTick } from 'vue';
const items = ref([]);
const bottomDivRef = ref(null);
// 模拟用户请求数据
const fetchMoreData = () => {
setTimeout(() => {
const newItems = Array.from({ length: 10 }, (_, index) => 100 + index + items.value.length);
items.value.push(...newItems);
scrollToBottom();
}, 2000);
};
const scrollToBottom = () => {
nextTick(() => {
if (bottomDivRef.value) {
bottomDivRef.value.scrollIntoView({
behavior: 'smooth',
block: 'end'
});
}
});
};
// 初始化时滚动到底部
onMounted(() => {
scrollToBottom();
});
// 模拟每隔一段时间获取更多数据
onMounted(() => {
setInterval(fetchMoreData, 3000); // 每5秒获取一次
});
</script>
<style scoped>
.content {
height: 200px; /* 或者其他的固定高度 */
overflow-y: auto;
background: red;
}
</style>
3. 滚动鼠标
如果聊天中用户翻滚了鼠标,鼠标如果滚动到界面最底部,则自动滑动开启。否则禁止界面自动滑动,以用户鼠标翻滚的地方为准(这里我们使用vueuse的useScroll)
<template>
<div>
<div ref="el" class="content">
<!-- 这里放置你的内容 -->
<p v-for="item in items" :key="item">{{ item }}</p>
<div ref="bottomDivRef"></div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, nextTick } from 'vue';
import { useScroll } from '@vueuse/core';
const items = ref([]);
const el = ref(null);
const bottomDivRef = ref(null);
const { isScrolling, arrivedState } = useScroll(el, { behavior: 'smooth' });
const fetchMoreData = () => {
setTimeout(() => {
const newItems = Array.from({ length: 20 }, (_, index) => 1 + index + items.value.length);
items.value.push(...newItems);
// 如果没移动,且在最底部,则继续滚动
if (!isScrolling.value && arrivedState.bottom) {
scrollToBottom();
}
}, 2000);
};
const scrollToBottom = () => {
nextTick(() => {
if (bottomDivRef.value) {
bottomDivRef.value.scrollIntoView({
behavior: 'smooth',
block: 'end',
});
}
});
};
// 初始化时滚动到底部
onMounted(() => {
scrollToBottom();
// 模拟每隔一段时间获取更多数据
setInterval(fetchMoreData, 3000); // 每5秒获取一次
});
</script>
<style scoped lang="scss">
.content {
height: 500px; /* 或者其他的固定高度 */
overflow-y: auto;
background: red;
}
</style>