1. 需求效果图
2. 方案
为实现这样的效果,首先要解决两个问题:
2.1.点击输入框弹出软键盘后,将已有的少许聊天内容弹出,导致看不到的问题
点击输入框弹出软键盘后,将已有的少许聊天内容弹出,导致看不到的问题。
(1)首先我们需要将input的自动向上推给关掉,这里有个坑:
在input组件中添加:adjust-position='{{false}}' ,而不是:adjust-position='false' 。
这么做虽然不再向上推,但却导致了软键盘弹起时,会遮挡屏幕下部分的消息。
(2)如何解决软键盘弹起时,会遮挡屏幕下部分的消息?
当软键盘弹起时,将scroll-view的高度缩短至软键盘遮挡不到的屏幕上方部分,当软键盘收起时,再将scroll-view的高度还原,这样解决了遮挡问题。
** 提示:**
** input中的bindfocus='focus'可获取软键盘高度并监听软键盘弹起,bindblur='blur'可监听软键盘收起,var windowHeight = wx.getSystemInfoSync().windowHeight;可获得屏幕高度。
scrollHeight(滚动条高度) = windowHeight(屏幕高度) - 软键盘高度;
最后将input组件放在软键盘上面就完成了。**
2.2.键盘弹出或收起时,聊天消息没有自动滚到最底部
首先解决第二个问题,自动滚动到最底部,这很简单,这里提供三种方法(推荐第三种):
(1)计算每条消息的最大高度,设置scroll-top=(单条msg最大高度 * msg条数)px。
(2)用 将展示msg的目标scroll-view包裹,
通过js获取到该view的实际高度:
javascript
var that = this;
var query = wx.createSelectorQuery();
query.select('.scrollMsg').boundingClientRect(function(rect) {
that.setData({
scrollTop: rect.height+'px';
});
}).exec();
(3)(推荐)将所有msg都编号如:msg-0,msg-1,msg-2... 直接锁定最后一条msg,滚动到那里。
在scroll-view中添加:scroll-into-view='{{toView}}',
在wx:for后面添加:wx:for-index="index",
在每个msg布局中添加:id='msg-{{index}}',
javascript
this.setData({
toView: 'msg-' + (msgList.length - 1)
})
3. 代码
3.1.gridGroup.wxml
javascript
<view class="page-layout">
<view class="page-body" id="x_chat">
<view wx:key="{{index}}" wx:for="{{chatList}}">
<view class="chat-item-body">
<view class="chat-item-time">{{item.time}}</view>
<view wx:key="{{index}}" wx:if="{{item.type == '0'}}" class="chat-item-layout chat-left">
<view class="chat-inner-layout">
<view class="chat-item-name">{{item.name}}</view>
<view class="chat-item-msg-layout">
<image class="chat-item-photo" bindtap="scanClick" src="{{item.photoUrl}}" mode="aspectFit"></image>
<view class="chat-inner-msg-left">{{item.msg}}</view>
</view>
</view>
</view>
</view>
<view wx:key="{{index}}" wx:if="{{item.type == '1'}}" class="chat-item-layout chat-right">
<view class="chat-inner-layout">
<view class="chat-item-name-right">{{item.name}}</view>
<view class="chat-item-msg-layout">
<view class="chat-inner-msg-right">{{item.msg}} </view>
<image class="chat-item-photo" bindtap="scanClick" src="{{item.photoUrl}}" mode="aspectFit"></image>
</view>
</view>
</view>
</view>
</view>
<view class="submit-layout">
<input class="submit-input" placeholder="点击输入,开始聊天吧" value="{{inputTemp}}" bindinput="bindKeyInput" />
<view class="submit-submit" type="submit" size="mini" bindtap="submitTo">发送</view>
</view>
</view>
3.2.gridGroup.wxss
javascript
.page-layout {
width: 100%;
height: 100%;
box-sizing: border-box;
}
.page-body {
width: 100%;
display: flex;
flex-direction: column;
padding-bottom: 56px;
}
.chat-item-body {
display: flex;
flex-direction: column;
margin-top: 20rpx;
}
.chat-item-time {
width: 100vw;
text-align: center;
font-size: 28rpx;
color: #ccc;
border-radius: 10rpx;
margin-top: 40rpx;
}
.chat-item-layout {
display: block;
max-width: 82%;
margin: 1rpx 5rpx;
box-sizing: border-box;
padding: 0 1rpx;
}
.chat-right {
float: right;
}
.chat-left {
float: left;
}
.chat-inner-layout {
display: flex;
flex-direction: column;
}
.chat-item-photo {
width: 70rpx;
height: 70rpx;
min-width: 70rpx;
min-height: 70rpx;
border-radius: 50%;
}
.chat-item-msg-layout {
display: flex;
flex-direction: row;
}
.chat-item-name {
display: flex;
flex-direction: row;
align-items: center;
font-size: 28rpx;
color: #999;
border-radius: 10rpx;
margin: 5rpx 0 0 80rpx;
}
.chat-item-name-right {
display: flex;
flex-direction: row;
align-items: center;
font-size: 28rpx;
color: #999;
border-radius: 10rpx;
margin: 5rpx 0 0 5rpx;
}
.chat-inner-msg-left {
display: inline-block;
flex-direction: row;
align-items: center;
color: #000;
font-size: 30rpx;
border-radius: 10rpx;
background: white;
padding: 15rpx 5rpx 15rpx 15rpx;
margin-left: 12rpx;
}
.chat-inner-msg-right {
display: inline-block;
color: #000;
font-size: 30rpx;
border-radius: 10rpx;
background: #87EE5F;
padding: 15rpx 5rpx 15rpx 15rpx;
margin-right: 12rpx;
}
.submit-layout {
position: absolute;
bottom: 0;
width: 100%;
background: #eee;
flex-direction: row;
}
.submit-layout {
width: 100%;
position: fixed;
bottom: 0;
border-top: 1px solid #ddd;
padding: 10rpx 0;
display: flex;
flex-direction: row;
align-items: center;
}
.submit-input {
flex: 1;
background: #fff;
margin: 5rpx 10rpx;
border-radius: 5rpx;
padding: 15rpx 20rpx;
color: #333;
font-size: 30rpx;
}
.submit-submit {
background-color: #13c25f;
color: #333;
font-weight: 700;
font-size: 30rpx;
border-radius: 10rpx;
padding: 18rpx 30rpx;
margin-right: 10rpx;
}
3.3.gridGroup.js
javascript
import tinyCommunityJson from '../../public/json/tinyCommunityJson';
Page({
data: {
inputValue: '',
chatList: tinyCommunityJson.data.rows,
},
onLoad: function (options) {
var title = options.title
// 设置标题
wx.setNavigationBarTitle({
title: title,
})
//滚动到页面底部
that.pageScrollToBottom()
},
/**
* 输入监听
*/
bindKeyInput: function (e) {
this.setData({
inputValue: e.detail.value
})
},
/**
* 发送
*/
submitTo: function (e) {
var that = this;
var inputValue = that.data.inputValue
if (!inputValue) {
wx.showToast({
title: '请输入聊天内容',
icon: 'none'
})
return
}
this.setData({
inputTemp: ""
})
var chatObj = {}
chatObj.type = '1'
chatObj.name = ''
chatObj.msg = inputValue
chatObj.time = that.getCurTime()
chatObj.photoUrl = 'https://zhsq/icon_chat_photo_three.jpg'
var chatList = that.data.chatList
chatList.push(chatObj);
that.setData({
chatList: chatList
})
//滚动到页面底部
that.pageScrollToBottom()
},
/**
* 获取当前时间
*/
getCurTime() {
var date = new Date()
var y = date.getFullYear();
var m = date.getMonth() + 1;
m = m < 10 ? ('0' + m) : m;
var d = date.getDate();
d = d < 10 ? ('0' + d) : d;
var h = date.getHours();
h = h < 10 ? ('0' + h) : h;
var minute = date.getMinutes();
minute = minute < 10 ? ('0' + minute) : minute;
var second = date.getSeconds();
second = second < 10 ? ('0' + second) : second;
return y + '-' + m + '-' + d + ' ' + h + ':' + minute + ':' + second;
},
/**
* 滚动到页面底部
*/
pageScrollToBottom: function () {
let that = this;
wx.createSelectorQuery().select('#x_chat').boundingClientRect(function (rect) {
let top = rect.height * that.data.chatList.length;
wx.pageScrollTo({
scrollTop: top,
duration: 100
})
}).exec()
},
})
3.4.tinyCommunityJson.js
javascript
const data = {
rows: [{
type: '0',
name: '群主',
msg: '大家好,欢迎进入微社区群,如有问题可在群里聊天询问',
time: '2024-01-26 13:43:12',
photoUrl: 'https://zhsq/icon_chat_photo_two.jpg',
},
{
type: '0',
name: '小助手',
msg: '2024微报事、微呼应活动正在进行中,希望大家踊跃参加。',
time: '2024-01-26 13:43:15',
photoUrl: 'https://zhsq/icon_service.png',
},
{
type: '1',
name: '',
msg: '已参加微呼应活动',
time: '2024-01-26 13:56:10',
photoUrl: 'https://zhsq/icon_chat_photo_three.jpg',
},
{
type: '0',
name: '第五网格员',
msg: '已参加微报事活动',
time: '2024-01-26 13:59:12',
photoUrl: 'https://zhsq/icon_chat_photo_one.jpg',
},
],
};
module.exports = {
data: data,
}
4. 优化
聊天框三角形的制作和使用
4.1. gridChat.wxml
javascript
<view>
<!-- 右侧布局 -->
<view class="right-layout">
<view class='right-msg'>我是右侧布局我是右侧布局我是右侧布局我是右侧布局我是右侧布局</view>
<view class="right-arrow-layout">
<image class="right-arrow-img" src='https://zhsq/icon_arrow_right_green.png' mode='widthFix'></image>
</view>
<image class="right-arrow-photo" src='https://zhsq/icon_chat_photo_one.jpg' mode='aspectFill'></image>
</view>
<!-- 左侧布局 -->
<view class="left-layout">
<image class="left-arrow-photo" src='https://zhsq/icon_chat_photo_two.jpg' mode='aspectFill'></image>
<view class="left-arrow-layout">
<image class="left-arrow-img" src='https://zhsq/icon_arrow_left_white.png' mode='widthFix'></image>
</view>
<view class='left-msg'>我是左侧布局</view>
</view>
</view>
4.2. gridChat.wxss
javascript
page {
background-color: #eee;
}
/* 左侧布局 */
.left-layout {
display: flex;
justify-content: flex-start;
padding: 20rpx 60rpx 2vw 2vw;
}
.left-arrow-photo {
width: 60rpx;
height: 60rpx;
min-width: 60rpx;
min-height:60rpx ;
border-radius: 50%;
margin-top: 5rpx;
}
.left-msg {
font-size: 32rpx;
color: #444;
line-height: 45rpx;
padding: 10rpx 20rpx 10rpx 5rpx;
background-color: white;
margin-left: -12rpx;
border-radius: 10rpx;
z-index: 10;
}
.left-arrow-layout {
width: 35rpx;
height: 65rpx;
display: flex;
align-items: center;
z-index: 9;
}
.left-arrow-img {
width: 35rpx;
}
/* 右侧布局 */
.right-layout {
display: flex;
justify-content: flex-end;
padding: 20rpx 2vw 2vw 15vw;
}
.right-arrow-photo {
width: 60rpx;
height: 60rpx;
min-width: 60rpx;
min-height:60rpx ;
border-radius: 50%;
margin-top: 5rpx;
}
.right-msg {
font-size: 32rpx;
color: #444;
line-height: 45rpx;
padding: 10rpx 5rpx 10rpx 20rpx;
background-color: #96EB6A;
margin-right: -12rpx;
border-radius: 10rpx;
z-index: 10;
}
.right-arrow-layout {
width: 35rpx;
height: 65rpx;
margin-right: 5rpx;
display: flex;
align-items: center;
z-index: 9;
}
.right-arrow-img {
width: 35rpx;
}