基于uniapp做的聊天系统中,使用scroll-view实现动态滚动到最底部

这里提到两个方法
方法一:用整个scroll-view的高度减去所有聊天内容的高度,得到的就是滚动条需要下拉的距离,即可使聊天页下拉至最底部。
参考文章:《uni-app 之 聊天室滚到最底部》
方法二:利用uniapp中的scroll-view组件的scroll-into-view属性 具体使用方法请参考官网文档

第一种方法:

  1. 将聊天框封装成组件,并判断是谁发送的信息
    首先要给聊天内容部分封装成一个组件(不封装也可以),将组件中的对方和自己用v-if做一个判断,当接收的用户名(或者id,只要是唯一值就可以)等于自己的用户名就显示自己的聊天框,不是自己的用户名就显示对方的。这里接受过来的用户名是服务器传过来的,信息一般包含:(发信人,接受的人(如果是给本人发的,那么这里的接受人就等于本人的用户名),信息,类型等等)
//聊天组件
<!-- 本人 -->
		<view class=" position-relative" style="margin-top: 30rpx;"  v-if="userId === item.payload.owner" >
			<u-row>
				<u-col span="2"></u-col>
				<u-col span="10">
					<view class="d-flex a-center j-end">
						<view class="text-muted font-sm">{{dateFormat2(item.meta.timestamp)}}</view>
					</view>
				</u-col>
			</u-row>
			<u-row>
				<u-col span="10.5">
					<view class="d-flex j-end a-start position-relative top-0" v-if="item.payload.type === 'text' || item.payload.type === 'image'">
						<view class="customer px-2 py-1 d-flex j-end a-center position-relative top-0 baseStyle" 
						style=" right: -10rpx; z-index: 5;" >
							<text style="word-break:break-all;" v-if="item.payload.type === 'text'" :selectable="true">{{item.payload.data}}</text>
							<u-image v-if="item.payload.type === 'image'" :src="item.payload.data" width="300rpx" mode="widthFix" class="pt-1" @click="previewImage(item)"></u-image>
							<!-- <u-image :src="avatar" width="100%" mode="aspectFit"></u-image> -->
						</view>
						<u-icon name="play-right-fill" color="#98E165" class="position-relative mt-2 top-0"></u-icon>
					</view>
					
					<audio v-if="item.payload.type === 'music'" :src="item.payload.data" controls></audio>
					<video v-if="item.payload.type === 'video'" :src="item.payload.data" controls></video>
				</u-col>
				<u-col span="1.5">
					<view class="d-flex j-center a-center position-absolute" style="top: 40rpx;">
						<u-avatar :src="avatar" size="mini" mode="circle"></u-avatar>
					</view>
				</u-col>
			</u-row>
		</view>
	<!-- 对方 -->
		<view class=" position-relative" style="margin-top: 30rpx;" v-else>
			<u-row>
				<u-col span="10">
					<view class="d-flex a-center">
						<!-- <view class="pr-1">{{item.meta.client}}</view> -->
						<view class="font-sm text-muted">{{dateFormat2(item.meta.timestamp)}}</view>
					</view>
				</u-col>
				<u-col span="2"></u-col>
			</u-row>
			<u-row>
				<u-col span="1.5">
					<view class="d-flex j-center position-absolute " style="top: 40rpx;" >
						<u-avatar :src="avatar" size="mini" mode="circle"></u-avatar>
					</view>
				</u-col>
				<u-col span="10">
					<view class="d-flex j-start a-start position-relative top-0" v-if="item.payload.type === 'text' || item.payload.type === 'image'">
						<u-icon name="play-left-fill" color="#fff" class="mt-2 top-0"></u-icon>
						<view class="kefu px-2 py-1 d-flex j-start a-center position-relative top-0 baseStyle" 
						style="left: -10rpx;">
							<text style="word-break:break-all;" v-if="item.payload.type === 'text'" :selectable="true">{{item.payload.data}}</text>
							<u-image v-if="item.payload.type === 'image'" :src="item.payload.data" width="300rpx" mode="widthFix" class="pt-1" @click="previewImage(item)"></u-image>
						</view>
					</view>
					<audio v-if="item.payload.type === 'music'" :src="item.payload.data" controls></audio>
					<video v-if="item.payload.type === 'video'" :src="item.payload.data" controls></video>
				</u-col>
			</u-row>
		</view>
  1. 在script的methods中添加一个滚动到底部的函数
    在scroll-view上添加一个id或者是类名,再在包裹聊天组件的标签上添加一个类或者id,在script中的methods中添加一个滚动到底部的方法,
//标题导航栏
<view class="navBar-bg top-0" :style="'height:' + navBarHeight + 'px;'">
	<view :style="'height:' + statusBarHeight + 'px;'"></view>
	<u-row>
		<u-col span="2"><u-icon name="arrow-left" class="pl-2" @click="back"></u-icon></u-col>
		<u-col span="8">
			<view class="d-flex j-center a-center flex-column">
				<view class="u-font-19">{{ title }}</view>
				<view class="u-font-12">{{ subTitle }}</view>
			</view>
		</u-col>
		<u-col span="2"><view @click="manpower">转人工</view></u-col>
	</u-row>
</view>
//内容框
<scroll-view id="scrollview" style=" background-color: #71D5A1;" scroll-y :style="'height:' + contentH + 'px;'" :scroll-top="scrollTop" :scroll-into-view="scrollIntoIndex">
     <view id="msglistview" class="pb-3">
      <block v-for="(item, index) in simulation" :key="index" :id="'chatItem_'+index"><chat-item :clientId="clientId" :item="item"></chat-item></block>
     </view>
</scroll-view>

//底部输入框
<view>
	//这里调用的是一个自定义组件,组件中有一个获取底部输入栏的高度,详情见下。
	<chat-footer
		:KeyboardHeight="KeyboardHeight"
		:tagList="tagList"
		:isExpression="isExpression"
		:isMoreTool="isMoreTool"
		:moreList="moreList"
		@focusInput="focusInput"
		@expressionBtn="expressionBtn"
		@moreTool="moreTool"
		@sendMsgs="sendMsgs"
		@clickCard="clickCard"
		@fillInput="fillInput"
	></chat-footer>
</view>

这里是底部输入栏组件的全部代码

<template>
	<view>
		<view class="position-fixed left-0 right-0 py-2  footerview" style="width: 100%; background-color: #A0CFFF;" :style="'bottom:' + KeyboardHeight + 'px;'">
			<block v-for="(item, index) in tagList" :key="index">
				<u-tag :text="item.text" mode="light" shape="circle" type="info" class="mx-1" @click="fillInput(item)"></u-tag>
			</block>
			<view class="my-1"><u-divider></u-divider></view>
			<view class="d-flex j-center a-center">
				<view style="width: 80%;" class="px-1">
					<input
						type="text"
						v-model="inputContext"
						placeholder="请输入..."
						class="py-1"
						@focus="focusInput('text')"
						style="background-color: #FFFFFF; border-radius: 10rpx;"
					/>
				</view>
				<view class="d-flex j-center mr-1" @click="expressionBtn"><u-image :src="expression" width="60rpx" height="60rpx"></u-image></view>
				<view class="d-flex j-center mr-1" v-if="!inputContext" @click="moreTool"><u-image :src="addition" width="60rpx" height="60rpx"></u-image></view>
				<u-button type="success" size="mini" class="mr-1" v-if="inputContext" @click="sendMsgs">发送</u-button>
			</view>
			<view v-if="isExpression" class="">
				<!-- <swiper class="slider" :current="emojiData.length - 1">
					<swiper-item v-for="(item, key) in emojiData" :key="key" class="slider-emoji" :class="[key == emojiData.length - 1 ? 'lastbox' : '']">
						<text v-for="(emoji, index) in item" :key="index" @click="selemoji(emoji)" class="slider-emoji-icon font-lg">{{ emoji }}</text>
					</swiper-item>
				</swiper> -->
			</view>
			<view v-if="isMoreTool">
				<u-grid :col="4" :border="false">
					<u-grid-item v-for="(item, index) in moreList" :key="index" bg-color="rgba(255,255,255,0)" @click="clickCard(item)">
						<view style="background-color: #FFFFFF;  border-radius: 40rpx;" class="p-2"><u-image :src="item.icon" width="60rpx" height="60rpx"></u-image></view>
						<view>{{ item.text }}</view>
					</u-grid-item>
				</u-grid>
			</view>
			
		</view>
	</view>
</template>

<script>
export default {
	name: 'chat-footer',
	props: {
		// classes: Array,
		KeyboardHeight:Number,
		tagList:Array,
		// inputContext:String,
		moreList:Array,
		gridStyle:Object,
		isMoreTool:Boolean,
		isExpression:Boolean
	},
	data() {
		return {
			// isExpression:false,
			// isMoreTool:false,
			expression: require('../static/expression.png'),
			addition: require('../static/addition.png'),
			inputContext:''
		};
	},
	updated() {
		// this.getFooterHeight();
	},
	methods:{
		focusInput(type,) {
			this.$emit('focusInput',type,)
		},
		expressionBtn() {
			this.$emit('expressionBtn')
		},
		moreTool() {
			this.$emit('moreTool')
		},
		// 键盘enter回车发送
		submit() {
			this.sendMsgs()
		},
		sendMsgs() {
			const text = this.inputContext
			this.$emit('sendMsgs', text)
			this.inputContext = ''
		},
		clickCard(item) {
			// console.log(this.inde);
			this.$emit('clickCard',item)
		},
		fillInput(item) {
			this.inputContext = item.text
			const type = 'text'
			this.$emit('fillInput', type)
			this.sendMsgs()
		}
	}
};
</script>

<style></style>

这个组件输入栏的样式是
在这里插入图片描述

//设置页面滚动到底部
scrollToBottom() {
 	let that = this
    let query = uni.createSelectorQuery()
    query.select('#scrollview').boundingClientRect()
    query.select('#msglistview').boundingClientRect()
    query.exec((res) => {
   	  	 // console.log(res)
	     //获取所有内部子元素的高度
	     //判断子元素高度是否大于显示高度
	     if(res[1].height > res[0].height){
		      // 用子元素的高度减去显示的高度就获益获得滚动的高度
		      that.scrollTop = res[1].height - res[0].height
	     }
    })
  }// 获取底部菜单栏的高度
getFooterHeight() {
	let that = this;
	let query = uni.createSelectorQuery();
	query.select('.footerview').boundingClientRect();
	query.exec(function(res) {
		if (res[0]) {
			that.footViewHeight = res[0].height;
		}
		console.log(that.footViewHeight);
	});
	this.scrollToBottom();

	// console.log(this.footViewHeight);
}
  1. 在updated中调用scrollToBottom()方法,每次发生改变时都会调用滚动到底部的方法
updated() {
	//调用滚动到底部的方法
	this.scrollToBottom();
	this.getFooterHeight();
},

完成!!!

第二种方法

将聊天框封装成组件,和方法一中一样。这里就不在赘述。
样式布局除了scroll-view聊天框部分。其他顶部和底部输入栏都和方法一 一样。这里主要写聊天框部分,如下

//在scroll-view上添加scroll-into-view的属性,在循环聊天组件的id设置为'chatItem_' + index  这里style绑定chatBodyBottom来定位内容框的位置
<scroll-view scroll-y="true" class="main-scroll" :style="chatBodyBottom" :scroll-into-view="scrollIntoIndex" :scroll-with-animation="true" @click="showChat">
	<view v-for="(item, index) in chatData" :key="index" :id="'chatItem_' + index">
		<chat-item :userId="clientInfoData.userId" :item="item" ref="chatItem" @previewImage="previewImage"></chat-item>
	</view>
</scroll-view>

是聊天页滚动到最底部的方法是:

// 跳转页面底部
scrollToBottom() {
	this.$nextTick(() => {
		//这里将 最后一条聊天的下标'chatItem_' + index 赋值给 刚刚绑定的scrollIntoIndex。这样就可以实现滚动到底部
		let index = this.chatData.length - 1;
		this.scrollIntoIndex = 'chatItem_' + index;
	});
},

其他和方法一中的一样
这里提到怎么适配 有顶部标题栏和底部输入栏
在computed计算属性中添加

computed: {
	//设置聊天内容框距顶部和底部的距离。以此来了定位内容框的位置
	chatBodyBottom() {
		return `bottom:${this.footViewHeight + this.KeyboardHeight}px;top:${this.navBarHeight}px;`;
	}
},

在data中都有定义KeyboardHeight和navBarHeight。分别是键盘高度和顶部标题栏高度

结束最后,我要说几点,以上布局样式中有很多方法以及数据,在文章的methods和data中没有体现。没用过uniapp的可能难理解,见谅。

  • 5
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值