聊天框处于最底部时有新消息自动滚动

在工作时,受同事的告知,得知了一种聊天框处于最底部时有新消息时将旧消息顶至上层的写法。

原文参考:https://juejin.cn/post/7306693980959588379

在设计聊天系统或AI问答抑或其他类似具有输入输出问答功能时,在每次产生新消息时,我们是希望新消息显示在下方,而旧消息会被顶到其上方,一般通过js进行编写自动滚动到底函数后进行调用,但每次有消息都需要调用,如果是ai回答问题时会逐字输出,在某些情况下可能调用的更加频繁,因此下面要介绍的方法只通过设置css,在处于屏幕最底部时,有新消息置底更新。

我们使用最简单的对话模型来设计html标签:一个大盒子对话框,我们起名为box--dialog,内部包含若干消息小盒子,将它们起名为box--message;为了更好的展示效果并且讲解,给出我们的一个示例简单代码:

<template>
  <div>
    <div class="box--dialog " >
      <div class="box--message" v-for="(msg, index) in messages" :key="index">
        <p>{{ msg }}</p>
      </div>
    </div>
    <button @click="sendMessage">生成一个消息</button>
  </div>


</template>

<script setup>
import { ref } from 'vue';

const messages = ref([]);

let currentMessage = '';
let currentIndex = 0;
let messageInterval = null;

// 模拟生成对话内容的函数
const generateMessage = () => {
  const newMessage = `消息 ${messages.value.length + 1}`;
  if (messages.value.length <= 20) {
    messages.value.push(newMessage);
  }
};


const sendMessage= () => {
  generateMessage();
};

</script>

<style scoped>
.box--dialog {
  margin: 200px 50px;
  height: 500px;
  width: 500px;
  overflow-y: auto;
  background-color: #f0f0f0;
  padding: 10px;
  box-sizing: border-box;
}

.box--message {
  background-color: #ffffff;
  border-radius: 5px;
  padding: 10px;
  margin-bottom: 10px;
  max-width: 50%;
}

</style>

在上述设置编码的情况下,随着消息的产生,后一个输出的box--message会显示在前一个输出的box--message下面,即是下面图片所示样子:

但是,每当产生新消息时,它会有消息产生在下方,就会只显示这一页的消息,如果你不滚动滚动条,是没有办法看见新消息的,

因此我们接下来介绍两个css方法来解决这个问题,这两个方法都基于一点,在不进行设置的情况下,对话框的页面会显示前面的几个消息盒子,所有如果我们将后续新产生的消息盒子都添加在消息数组的头部,那么页面会成功一直显示最新的消息,剩下的只用解决消息的位置问题。因此下列两个方法都基于将新消息插入到messages的头部即新消息为messages[0]。 

messages.value.unshift(newMessage);

方法一:

此方法十分便捷,我们将对话框box--dialog设置为弹性盒,并且使它内部的元素按照由下向上排列,这样每次新消息都会出现在对话框的最底部,实现我们所想:

示例完整代码:

<template>
  <div class="show">
    <div class="box--dialog">
      <div class="box--message" v-for="(msg, index) in messages" :key="index">
        <p>{{ msg }}</p>
      </div>
    </div>
    <button @click="sendMessage">生成一个消息</button>
  </div>


</template>

<script setup>
import {ref} from 'vue';

const messages = ref([]);
const chatContainer = ref(null);

// 模拟生成对话内容的函数
const generateMessage = () => {
  const newMessage = `消息 ${messages.value.length + 1}`;
  if (messages.value.length <= 20) {
    messages.value.unshift(newMessage);
  }
};

const sendMessage = () => {
  generateMessage();
};

</script>

<style scoped>

.show {
  margin: 100px 100px;
  height: 600px;
  width: 500px;
  position: relative;
  border: none;
}

.box--dialog {
  position: absolute;
  height: 500px;
  width: 500px;
  overflow-y: auto;
  background-color: #f0f0f0;
  padding: 10px;
  box-sizing: border-box;
  display: flex;
  flex-direction: column-reverse;
}

.box--message {
  background-color: #ffffff;
  border-radius: 5px;
  padding: 10px;
  margin-bottom: 10px;
  max-width: 30%;
}

button {
  position: absolute;
  bottom: 10px;
  width: 100px;
  left: 50%;
  margin-left: -50px;
}

</style>

方法二:

再看下我们的需求,新消息要产生在旧消息下方,可以进行旋转来实现,我们首先将最外层的box--dialog进行180度旋转,让最底下的消息到最上面来,这样设置后,在给每个box--message也来进行一次180度旋转,这样我们的文字经过360度的旋转就依然还是没变化的,但是我们可以看到滚动条以及消息框发生了变化,它们的位置不正确:

随后有两个关键css设置:

给box--dialog设置:direction: rtl;
给box--message设置:text-align: left;

就能发现,一切都回归正常了:

但是滚动条还是有问题,我们发现由于经过180度翻转导致滚动条与我们滚动方向相反,因此我们还需要对滚动条进行设置:

<div @wheel="handleWheelEvent" class="box--dialog" ref="chatContainer">

const chatContainer = ref(null);

const handleWheelEvent = (event) => {
  event.preventDefault(); // 阻止默认滚动行为
  const { deltaY } = event; // 获取滚动方向和速度
  if (chatContainer.value) {
    chatContainer.value.scrollTop -= deltaY; // 反转方向
  }
};

我们给滚动条所在盒子加一个ref来获取滚动条行为,并且阻止这个行为,在获取滚动条的方向,将这个方向设置为与其反向,就可以将滚动条滚动恢复正常啦。这样我们的需求就基本完成了:

示例完整代码:

<template>
  <div class="show">
    <div @wheel="handleWheelEvent" class="box--dialog" ref="chatContainer" >
      <div class="box--message" v-for="(msg, index) in messages" :key="index">
        <p>{{ msg }}</p>
      </div>
    </div>
    <button @click="sendMessage">生成一个消息</button>
  </div>


</template>

<script setup>
import {ref} from 'vue';

const messages = ref([]);
const chatContainer = ref(null);

const handleWheelEvent = (event) => {
  event.preventDefault(); // 阻止默认滚动行为
  const { deltaY } = event; // 获取滚动方向和速度
  if (chatContainer.value) {
    chatContainer.value.scrollTop -= deltaY; // 反转方向
  }
};

// 模拟生成对话内容的函数
const generateMessage = () => {
  const newMessage = `消息 ${messages.value.length + 1}`;
  if (messages.value.length <= 20) {
    messages.value.unshift(newMessage);
  }
};

const sendMessage = () => {
  generateMessage();
};

</script>

<style scoped>

.show {
  margin: 100px 100px;
  height: 600px;
  width: 500px;
  position: relative;
  border: none;
}

.box--dialog {
  position: absolute;
  height: 500px;
  width: 500px;
  overflow-y: auto;
  background-color: #f0f0f0;
  padding: 10px;
  box-sizing: border-box;
  transform: rotate(180deg);
  direction: rtl;
}

.box--message {
  background-color: #ffffff;
  border-radius: 5px;
  padding: 10px;
  margin-bottom: 10px;
  max-width: 30%;
  transform: rotate(180deg);
  text-align: left;
}

button {
  position: absolute;
  bottom: 10px;
  width: 100px;
  left: 50%;
  margin-left: -50px;
}

</style>

补:最后如果想对第一个消息的位置进行设置,可以进行如下设置:

我们上述的两个方法它们的第一个消息都会显示在最底部:

如果想让第一个消息就到屏幕的最上方,可以在增加一个盒子用来控制:

我们将聊天框设置为弹性盒,并且让主轴在y轴,使其内部盒子竖直排列,在消息盒子box--message的前增加一个盒子box--location,让它在消息盒子的下方,(注意对话框内盒子的排列方式,要将box--location放在我们所看到的最下方,而经过旋转抑或弹性盒的flex-direction:column-reverse都使得这个盒子应该在写代码时在消息盒子的上方,代码中的位置要根据排列方式以及旋转两个因素考虑:要使得box--location这个控制少量消息的位置的盒子处在我们眼睛所看到的消息盒子的下方,即屏幕的最下方),给它设置两个属性:

flex-grow: 1;
flex-shrink: 1;

这样就会在没有消息时由这个盒子占满全部对话框盒子,有消息的时候将盒子撑起至最上方:

注意,在有新消息产生时,我们上述的方法只有控制在滚动条处于最底部时,我们的视角中才会不断出现新消息将旧消息撑至上方实现自动滚动到最底,而在滚动条处于其他位置时并不会自动滚动到最底部。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值