最简单实现聊天框发送消息后自动滚动到底部

2,890 阅读1分钟

背景

在使用ai助手聊天的时候,每当聊天框中展示新消息,需要将聊天框滚动到底部,展示最新信息。跟普通聊天工具相比,ai助手聊天使用的是流响应,消息体会随着ai模型的输出不断变化。

实现思路

  1. 监听消息体的变化,频繁的进行javascript调用滚动到底部(极度不推荐,一听就有性能问题)。
  2. 参考Poe的聊天框实现方式,通过改变消息列表的排列方向来实现 (推荐)

实现的功能

  1. 当发送新消息时,滚动到聊天框底部

GIF 2024-3-19 16-53-13.gif

这一点比较简单,在发送的时候滚动到底部

关键代码:

sendMessage() {
  this.list.push({ id: this.list.length + 1, message: this.message });
  // 平滑滚动到.container的底部
  this.$nextTick(() => {
    document.querySelector('.container').scrollTop = 0
  })
},

  1. 在聊天框底部接受消息体期间,滚动条一直处于最底部

GIF 2024-3-19 16-54-00.gif

注意:这里是实现方式的关键:

首先,需要将 .box 的样式设置为:

display: flex;
flex-direction: column-reverse;

效果对比:

image.png

针对数据较少时的空白块,我们通过添加 .empty-box 元素来进行填充,给元素添加以下样式,在有空白时撑开剩余空间,数据多时高度为0。

    flex-grow: 1;

代码实现

demo演示:

组件代码:

<template>
  <div class="container">
    <div class="box">
      <div class="list">
        <div class="item" v-for="item in list" :key="item.id">{{ item.message }}</div>
      </div>
      <div class="empty-box"></div>
      <div class="footer">
        <input type="text" v-model="message" />
        <button @click="sendMessage">发送</button>
        <button @click="addMessageText">加文字</button>
      </div>
    </div>
  </div>
</template>
<script>
  export default {
  name: 'Demo001',
  data() {
    return {
      message: '',
      list: [
        {
          id: 1,
          message: '这是第一条消息',
        },
        {
          id: 2,
          message: '这是第二条消息',
        },
        {
          id: 3,
          message: '这是第3条消息',
        },
        {
          id: 4,
          message: '这是第4条消息',
        },
        {
          id: 5,
          message: '这是第5条消息',
        },
        {
          id: 6,
          message: '这是第6条消息',
        },
        {
          id: 7,
          message: '这是第7条消息',
        },
        {
          id: 8,
          message: '这是第8条消息',
        }
      ]
    }
  },
  methods: {
    sendMessage() {
      this.list.push({ id: this.list.length + 1, message: this.message });
      // 平滑滚动到.container的底部
      this.$nextTick(() => {
        document.querySelector('.container').scrollTop = 0
      })
    },
    addMessageText() {
      this.list[this.list.length - 1].message = this.list[this.list.length - 1].message + ' ' + this.message
    }
  }
}
</script>
<style lang="scss">
  .container {
    height: 500px;
    width: 400px;
    overflow-y: scroll;
    overflow-x: hidden;
    display: flex;
    flex-direction: column-reverse;
    padding: 0 16px;
    border: 2px solid #eee;
    scroll-behavior: smooth;

    .box {
      width: 100%;
      flex: 1 1;
      align-self: center;
      display: flex;
      flex-direction: column;

      .list {
        display: flex;
        flex-direction: column;
      }

      .empty-box {
        flex-grow: 1;
      }

      .footer {
        position: sticky;
        width: 100%;
        bottom: 0;
        background: #fff;
      }
    }
  }
</style>