Article directory
foreword
The previous article talked about how to layout, this article will talk about how to use uni-app to realize the most important function of the chat page of the mini program - after sending a message, the page scrolls to the bottom (refer to many articles and finally find a more suitable method ).
For other functions (refer to WeChat), later articles will describe the specific implementation methods
- When the chat box is clicked, the chat box is lifted with the keyboard and the chat message list scrolls to the bottom, but the overall page does not lift
- The chat box textarea adapts to the height according to the content, and the chat message list scrolls to the bottom as the chat box increases (to put it bluntly, the bottom message will not be blocked by the increased chat box)
train of thought
Since we use chat scrolling on the layout scroll-view
, a container for storing message lists is nested inside (as shown in the left figure), an important attribute in scroll-view is that the scroll-top
official document explains the setting of the vertical scroll bar position .
When the length of the message list exceeds the height of the scroll-view, the height difference between them is the scrolling distance of the scroll-view (as shown on the right).
Use this function to obtain node information boundingClientRect
. For specific methods, please refer to the official website uni.createSelectorQuery() | uni-app official website (dcloud.net.cn)
pit
The code of the original js part is written like this:
scrollToBottom(){
let query = uni.createSelectorQuery().in(this);
// 获取节点信息
query.select('#scrollview').boundingClientRect();
query.select('#msglistview').boundingClientRect();
query.exec((res) =>{
if(res[1].height > res[0].height){
this.scrollTop = res[1].height - res[0].height
}
})
},
However, during the actual measurement (small program simulator and real machine), it is found that the scrolling position sometimes does not reach the bottom (as shown in the figure, in fact, there is a message below that is covered and not displayed), and sometimes it can be scrolled to the very bottom.
I initially suspected that the acquisition of node information was inaccurate, so I searched for boundingClientRect()
relevant information about this function. There was no explanation for this problem in the official document, and I found a solution later.
Reference article: Wechat applet boundingClientRect obtains inaccurate element node location information_LGDmar's Blog-CSDN Blog
The problem : the page is not rendered and the node information is obtained
Solution :
Method 1: You can set a delay function setTimeout
, because we don't know the rendering time, so I got a delay time that meets the requirements through experiments.
// 滚动至聊天底部
scrollToBottom(){
// 外层加一个延时函数是为了能获取到节点的准确信息
setTimeout(()=>{
let query = uni.createSelectorQuery().in(this);
// 获取节点信息
query.select('#scrollview').boundingClientRect();
query.select('#msglistview').boundingClientRect();
query.exec((res) =>{
if(res[1].height > res[0].height){
this.scrollTop = this.rpxTopx(res[1].height - res[0].height)
}
})
},15)
},
Method Two:
Use $nextTick
means to execute a callback function after the next DOM update.
//滚动至聊天底部
scrollToBottom(){
let query = uni.createSelectorQuery().in(this);
//获取节点信息
query.select('#scrollview').boundingClientRect();
query.select('#msglistview').boundingClientRect();
query.exec((res) =>{
if(res[1].height > res[0].height){
this.$nextTick(()=>{
this.scrollTop = this.rpxTopx(res[1].height - res[0].height)
})
}
})
},
Code
Vue page:
(If you need to refer to the css part, please see the layout of my previous article)
<template>
<view class="chat">
<scroll-view :style="{height: `${windowHeight}rpx`}"
id="scrollview"
scroll-y="true"
:scroll-top="scrollTop"
:scroll-with-animation="true"
class="scroll-view"
>
<!-- 聊天主体 -->
<view id="msglistview" class="chat-body">
<!-- 聊天记录 -->
<view v-for="(item,index) in msgList" :key="index">
<!-- 自己发的消息 -->
<view class="item self" v-if="item.userContent != ''" >
<!-- 文字内容 -->
<view class="content right">
{
{item.userContent}}
</view>
<!-- 头像 -->
<view class="avatar">
</view>
</view>
<!-- 机器人发的消息 -->
<view class="item Ai" v-if="item.botContent != ''">
<!-- 头像 -->
<view class="avatar">
</view>
<!-- 文字内容 -->
<view class="content left">
{
{item.botContent}}
</view>
</view>
</view>
</view>
</scroll-view>
<!-- 底部消息发送栏 -->
<!-- 用来占位,防止聊天消息被发送框遮挡 -->
<view class="chat-bottom">
<view class="send-msg">
<view class="uni-textarea">
<textarea v-model="chatMsg"
maxlength="300"
:show-confirm-bar="false"
auto-height></textarea>
</view>
<button @click="handleSend" class="send-btn">发送</button>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
//滚动距离
scrollTop: 0,
userId:'',
//发送的消息
chatMsg:"",
msgList:[
{
botContent: "hello,请问我有什么可以帮助你的吗?",
recordId: 0,
titleId: 0,
userContent: "",
userId: 0
},
{
botContent: "",
recordId: 0,
titleId: 0,
userContent: "你好呀我想问你一件事",
userId: 0
},
]
}
},
computed: {
windowHeight() {
return this.rpxTopx(uni.getSystemInfoSync().windowHeight)
}
},
methods: {
// px转换成rpx
rpxTopx(px){
let deviceWidth = wx.getSystemInfoSync().windowWidth
let rpx = ( 750 / deviceWidth ) * Number(px)
return Math.floor(rpx)
},
//滚动至聊天底部
scrollToBottom(){
//外层加一个延时函数是为了能获取到节点的准确信息
setTimeout(()=>{
let query = uni.createSelectorQuery().in(this);
//获取节点信息
query.select('#scrollview').boundingClientRect();
query.select('#msglistview').boundingClientRect();
query.exec((res) =>{
if(res[1].height > res[0].height){
this.scrollTop = this.rpxTopx(res[1].height - res[0].height)
}
})
},15)
},
// 发送消息
handleSend() {
//如果消息不为空
if(!this.chatMsg||!/^\s+$/.test(this.chatMsg)){
let obj = {
botContent: "",
recordId: 0,
titleId: 0,
userContent: this.chatMsg,
userId: 0
}
this.msgList.push(obj);
this.chatMsg = '';
}else {
this.$modal.showToast('不能发送空白消息')
}
},
}
}
</script>