【uni-app】uni-app实现聊天页面功能——功能篇(上)

文章目录

前言

上一篇讲到了如何布局,这一篇将讲一下如何用uni-app实现小程序聊天页面的最主要的功能——发消息后页面滚动到最底部(参考过很多文章最后找到比较适合的方法)。

其他的功能(参考微信),之后的文章会讲述到具体实现方法

  • 点击聊天框的时候,聊天框随键盘抬起且聊天消息列表滚动到最底部,但整体页面不抬起
  • 聊天框textarea根据内容自适应高度,且聊天消息列表随着聊天框的增高而滚动到最底部(说白了就是最底部的消息不会被增高的聊天框给挡住)

思路

由于我们在布局上聊天滚动用的是scroll-view,里面嵌套着一个存放消息列表的容器(如左图所示),scroll-view中的一个重要属性是scroll-top,官方文档解释设置竖向滚动条位置

当消息列表的长度超过scroll-view的高度时,它们之间的高度差就是scroll-view要滚动的距离(如右图所示)。

请添加图片描述

获取节点信息用boundingClientRect这个函数,具体方法可以参考官网uni.createSelectorQuery() | uni-app官网 (dcloud.net.cn)

原本js部分的代码是这样写的:

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
		}
	})
},

但是,在实测时(小程序模拟器和真机)发现滚动的位置有时滚动不到最底部(如图所示,其实下面还有一条消息被遮住了没显示出来),有时又可以滚动到最底部。

请添加图片描述

初步怀疑是节点信息获取不准确,我就去搜了关于boundingClientRect()这个函数的相关信息,官方文档上并没有对这个问题的解释,后来找到解决方案

参考文章:微信小程序 boundingClientRect 获取元素节点位置信息不准确_LGDmar的博客-CSDN博客

问题所在:页面未渲染完成而去获取了节点信息

解决方案

方法一:可以设置一个延时函数setTimeout,因为我们不知道渲染的时间是多少,所以我是经过试验得出一个满足需求的延时时间。

// 滚动至聊天底部
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)
},

方法二:

使用$nextTick,意为在下次DOM更新完之后执行一个回调函数。

//滚动至聊天底部
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)
			})
		}
	})
},

代码实现

vue页面:
(如果需要参考css部分,请看我发布的上篇文章布局篇)

<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>

猜你喜欢

转载自blog.csdn.net/qq_51250105/article/details/130145862