项目场景:
微信小程序中实现websocket,即时聊天功能
核心包含以下内容
1.聊天功能实现
2.发送消息后滚动到底部
3.历史记录查询及下拉加载更多功能实现
代码示例:
wxml
<!--pages/wechat2/wechat2.wxml-->
<view class="chat-header">
<view class="header-image-box">
<image class="header-image" src="{{receivebaseInfo.avatar}}"></image>
</view>
<view>
<view class="chat-name">{{receivebaseInfo.nickname}}</view>
<view class="chat-company">{{receivebaseInfo.company_name}} - {{receivebaseInfo.department_name}}</view>
</view>
</view>
<view class='news'>
<view class="historycon">
<scroll-view
scroll-y="true"
scroll-into-view="{{scrollid}}"
scroll-with-animation = "{{true}}"
style="height:{{scrollHeight}}"
class="history"
refresher-enabled="true"
bindrefresherrefresh="refresh"
refresher-triggered="{{triggered}}"
>
<!-- 历史记录部分start -->
<view wx:if="{{historyList.length > 0}}">
<view class="historyText">历史消息</view>
<block wx:for="{{historyList}}" wx:key="index">
<!--此处为other -->
<view wx:if="{{item.type==1}}" id="historyscrollid{{index}}">
<view>
<text class='chat-time' style="display:none;">{{item.date}}</text>
</view>
<view class='other-record'>
<image class='other-head-img' src='{{receivebaseInfo.avatar}}'></image>
<view class='other-record-content-triangle'></view>
<view class='other-record-content'>
{{item.content_msg}}</view>
</view>
</view>
<!--此处为结尾 -->
<!--此处为own -->
<view id="historyscrollid{{index}}" wx:else>
<view>
<text class='chat-time' style="display:none;">{{item.date}}</text>
</view>
<view class='own-record'>
<view class='own-record-content'>{{item.content_msg}}</view>
<view class='own-record-content-triangle'></view>
<image class='own-head-img' src='{{sendAvatar}}'></image>
</view>
</view>
<!-- own结尾 -->
</block>
<view class="historyText">以上为历史消息</view>
</view>
<!-- 历史记录部分end -->
<block wx:for="{{newsList}}" wx:key="index">
<!--此处为other -->
<view wx:if="{{item.type==1}}" id="scrollid{{index}}">
<view>
<text class='chat-time' style="display:none;">{{item.date}}</text>
</view>
<view class='other-record'>
<image class='other-head-img' src='{{receivebaseInfo.avatar}}'></image>
<view class='other-record-content-triangle'></view>
<view class='other-record-content'>
{{item.message}}</view>
</view>
</view>
<!--此处为结尾 -->
<!--此处为own -->
<view id="scrollid{{index}}" wx:else>
<view>
<text class='chat-time' style="display:none;">{{item.date}}</text>
</view>
<view class='own-record'>
<view class='own-record-content'>{{item.message}}</view>
<view class='own-record-content-triangle'></view>
<image class='own-head-img' src='{{sendAvatar}}'></image>
</view>
</view>
<!-- own结尾 -->
</block>
</scroll-view>
</view>
</view>
<view class="sendmessage">
<view class="send-message">
<input class="chat-input" type="text" bindinput="bindChange" confirm-type="done" value='{{input}}' cursor-spacing="16px" hold-keyboard="{{true}}" placeholder="" />
<image class="chat-emotion" src="../../static/img/chat-emotion.svg" bindtap="emotionChange"></image>
<button class="btn" bindtap='send'>发送</button>
</view>
<view class="emotions" wx:if="{{emotionVisible}}">
<view class="emotions-item" wx:for="{{connectemoji}}" wx:key="index" bindtap="addemotion" data-index="{{index}}">{{item}}</view>
</view>
</view>
**说明:**结构主要包括头部聊天方的信息部分,历史记录部分,即时聊天内容部分,发送消息部分
wxss
/* pages/wechat2/wechat2.wxss */
page {
background-color: #f3f3f4;
}
/* 新增样式 */
.chat-header {
display: flex;
align-items: center;
justify-content: flex-start;
height: 88px;
padding: 0 12px;
background-color: #fff;
border-radius: 0px 0px 10px 10px;
}
.header-image-box {
width: 64px;
height: 64px;
border-radius: 50%;
margin-right: 12px;
}
.header-image {
width: 100%;
height: 100%;
border-radius: 50%;
}
.chat-name {
color: #333;
font-size: 16px;
font-weight: 700;
}
.chat-company {
font-size: 14px;
color: rgba(81, 81, 81, 100);
}
.tab {
padding: 20rpx 20rpx 40rpx 50rpx;
height: 20%;
background-color: white;
}
.tab .tent {
font-size: 33rpx;
margin-bottom: 30rpx;
}
.jia_img{
height: 80rpx;
width: 90rpx;
}
.new_imgtent{
height: 180rpx;
width: 190rpx;
}
.tab .fabu {
font-size: 33rpx;
margin-top: 30rpx;
margin-bottom: 30rpx;
}
.xiahuaxia {
width: 80%;
text-align: center;
margin: 0 auto;
position: relative;
top: 60rpx;
}
.chat-time {
text-align: center;
padding: 5rpx 20rpx 5rpx 20rpx;
width: 200rpx;
font-size: 26rpx;
background-color: #e6e6e6;
}
.new_top_txt {
width: 50%;
position: relative;
top: 38rpx;
text-align: center;
margin: 0 auto;
font-size: 30rpx;
color: #787878;
background-color: #f7f7f7;
}
/* 聊天内容 */
.news {
margin-top: 30px;
text-align: center;
margin-bottom: 98px;
}
.img_null {
height: 60rpx;
}
.l {
height: 5rpx;
width: 20%;
margin-top: 30rpx;
color: #000;
}
/* 聊天 */
.my_right {
float: right;
position: relative;
right: 40rpx;
}
.you_left {
float: left;
position: relative;
left: 5rpx;
}
.new_img {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
}
.sanjiao {
top: 20rpx;
position: relative;
width: 0px;
height: 0px;
border-width: 10px;
border-style: solid;
}
.my {
border-color: transparent transparent transparent #95d4ff;
}
.you {
border-color: transparent #95d4ff transparent transparent;
}
.sendmessage {
/* display: flex;
align-items: center;
flex-direction: row; */
width: 100%;
min-height: 60px;
position: fixed;
bottom: 0px;
padding: 0 16px;
background-color: rgba(242, 242, 242, 100);
box-shadow: 0px -1px 5px 1px rgba(57, 57, 57, 0.1);
}
.send-message {
display: flex;
align-items: center;
padding: 16px;
}
.sendmessage input {
height: 80rpx;
background-color: white;
line-height: 80rpx;
font-size: 28rpx;
padding-left: 20rpx;
}
.sendmessage button {
width: 52px !important;
height: 32px;
line-height: 32px;
background: #169171 !important;
color: #fff !important;
font-size: 14px !important;
text-align: center;
border: 0 !important;
padding: 0 !important;
margin: 0 !important;
}
.historycon {
height: 90%;
/* background-color: pink; */
width: 100%;
flex-direction: column;
display: flex;
/* margin-top: 100rpx; */
border-top: 0px;
}
.hei{
margin-top: 50px;
height: 20rpx;
}
.history {
/* height: 300px; */
margin-top: 30rpx;
margin: 20rpx;
font-size: 28rpx;
line-height: 80rpx;
word-break: break-all;
}
.chat-input{
width: 60%;
height: 40px;
border: 0;
border-radius: 8px;
margin-left: 5rpx;
}
.back-icon{
margin-top: 25rpx;
margin-left: 25rpx;
width:40rpx;
height:40rpx;
}
.other-record-content{
background-color: #fff;
width: 180px;
border-radius: 7px;
padding: 0 20px;
text-align: left;
margin: 6px 0;
}
.other-record{
display: flex;
justify-content:flex-start;
}
.other-head-img{
width:70rpx;
height:70rpx;
border-radius: 50%;
margin: 10rpx 10rpx 10rpx 10rpx;
}
.other-record-content-triangle{
width: 0;
height: 0;
border-top: 10rpx solid transparent;
border-right: 15rpx solid #fff;
border-bottom: 10rpx solid transparent;
margin-top: 36rpx;
}
.own-record{
display: flex;
justify-content:flex-end;
padding-right:30rpx;
}
.own-record-content{
background-color: #209072;
width: 180px;
border-radius: 8px;
padding: 0 20px;
color: #fff;
text-align: left;
margin: 6px 0;
}
.own-record-content-triangle {
width: 0;
height: 0;
/* border-top: 20rpx solid transparent;
border-left: 40rpx solid #F0F0F0;
border-bottom: 20rpx solid transparent; */
border-top: 10rpx solid transparent;
border-left: 15rpx solid #209072;
border-bottom: 10rpx solid transparent;
margin-top: 36rpx;
}
.own-head-img{
width:70rpx;
height:70rpx;
border-radius: 50%;
margin: 10rpx 10rpx 10rpx 10rpx;
}
::-webkit-scrollbar{
width: 0;
height: 0;
color: transparent;
}
.chat-emotion {
width: 28px;
height: 28px;
margin: 0 12px;
}
.emotions {
display: flex;
align-items: flex-start;
justify-content: flex-start;
width: 200px;
height: 36px;
margin: 6px;
}
.emotions-item {
width: 24px;
height: 24px;
margin: 0 8px;
}
.historyText {
color: #ccc;
}
js
var utils = require("../../utils/util.js")
const app = getApp()
const api = require('../../utils/request.js'); //相对路径
Page({
/**
* 页面的初始数据
*/
data: {
receivebaseInfo:{},
sendAvatar:'',
newsList:[
{
date: "2020.10.19",
message:'哈喽,好久不见',
type: 0
},
{
date: "2020.10.20",
message:'是呀,好久不见',
type: 1
},
{
date: "2020.10.20",
message:'是呀,好久不见',
type: 1
},
],//消息列表
historyList:[],
input:null,
openid:null,
connectemoji: ["😘","😡","😔","😄","❤"],
emoji_list: ['emoji1i1', 'emoji2i2', 'emoji3i3', 'emoji4i4', 'emoji5i5'],
emotionVisible: false,
inputShowed: false,
scrollTop: 0,
inputBottom: '0px',
receiveMemberId:null,
sendMemberId:null,
scrollid:'scrollid',
scrollHeight:'300px',
// 下拉刷新
triggered:true,
// 历史记录当前页
pageNo: 1,
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
var receiveMemberId = options.receiveMemberId
var sendMemberId = app.globalData.open_id
var sendAvatar = app.globalData.sendAvatar
var _this = this;
_this.setData({
receiveMemberId,
sendMemberId,
sendAvatar
})
console.log(app.globalData.sendAvatar,'hahha')
// 获取内存中的数据
this.getStorageBaseInfo()
// 设置滚动区域的高度
this.setScrollHeight()
// 获取历史记录
this.getHistory()
// 初始化websocket
this.initWebSocket()
// 页面进入滚动到底部
this.scrollBottom()
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
// websocket初始化
initWebSocket: function(){
var _this = this;
var {receiveMemberId, sendMemberId} = this.data
//建立连接
wx.connectSocket({
url: `ws://10.200.18.18:1818/zxxt/${sendMemberId}/${receiveMemberId}`,//本地
success: function () {
console.log('websocket连接成功~')
},
fail: function () {
console.log('websocket连接失败~')
},
})
//连接成功
wx.onSocketOpen(function () {
console.log('onSocketOpen','连接成功,真正的成功');
})
// 接收服务器的消息事件
wx.onSocketMessage(function(res){
// 接收到的消息{date,message,type} type类型为 1 是对方的消息 为 0 是自己的消息
var list = [];
list = _this.data.newsList;
var _data = JSON.parse(res.data);
list.push(_data);
console.log(list)
_this.setData({
newsList:list
})
_this.scrollBottom()
},
)
// 监听连接关闭
wx.onSocketClose(function(){
console.log('监听 WebSocket 连接关闭事件')
})
},
// 获取历史记录
getHistory: function(){
var {receiveMemberId, sendMemberId,pageNo} = this.data
var params = {
receiveMemberId,
sendMemberId,
pageNo,
pageSize:5,
}
api.get("/zxxt/chat/msg/list", params, (res) => {
if (res.code == 'success') {
// var historyList = res.data.data
var historyList = [...res.data.data,...this.data.historyList]
if (historyList && historyList.length > 0) {
historyList.forEach(item => {
if (item.send_member_id == sendMemberId) {
item.type = 0
}else {
item.type = 1
}
});
this.setData({
historyList
})
console.log(this.data.historyList,'历史记录数据')
}else {
// 判断是否是第一次进入查看历史记录:是(不显示弹框,不是则显示弹框)
if(this.data.pageNo > 1) {
wx.showToast({
title: "没有更多历史记录了",
icon: 'none',
duration: 2000
})
}
}
} else {
if (res.message) {
wx.showToast({
title: res.message,
icon: 'none',
duration: 2000
})
}
}
}, (res) => {
if (res.message) {
wx.showToast({
title: res.message,
icon: 'none',
duration: 2000
})
}
})
},
// 滚动到底部
scrollBottom:function() {
var {newsList} = this.data
var scrollid = `scrollid${newsList.length - 1}`
this.setData({
scrollid
})
},
// 设置滚动区域的高度
setScrollHeight:function(){
const client = wx.getSystemInfoSync().windowHeight // 获取当前窗口的高度
var scrollHeight = (client - 236) + 'px'
this.setData({
scrollHeight
})
},
// 获取内存中聊天列表的用户信息
getStorageBaseInfo: function(){
//获取存储信息
wx.getStorage({
key: 'receivebaseInfo',
success: (res)=>{
this.setData({
receivebaseInfo:res.data
})
}
})
},
// 自定义下拉刷新
refresh: function(){
// 下拉的实际操作
var pageNo = this.data.pageNo + 1
this.setData({
pageNo
})
if (this.timer) {
clearTimeout(this.timer)
}
this.timer = setTimeout(()=>{
this.setData({
triggered:false
})
this.getHistory()
},2000)
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
},
send :function(){
var _this = this;
if(_this.data.input){
wx.sendSocketMessage({
data: _this.data.input,
success: (res) =>{
console.log(res)
},
fail: (err)=>{
console.log('sendSocketMessage','失败')
}
})
var list = [];
list = this.data.newsList;
var temp = { 'message': _this.data.input, 'date': utils.formatTime(new Date()), type: 0 };
list.push(temp);
this.setData({
newsList:list,
input:null
})
this.scrollBottom()
// 表情选择隐藏
this.setData({
emotionVisible:false,
})
}
// this.bottom()
const client = wx.getSystemInfoSync().windowHeight // 获取当前窗口的高度
console.log(client,'shurugaodu')
},
bindChange:function(res){
this.setData({
input: res.detail.value,
})
},
back:function(){
wx.closeSocket();
console.log('连接断开');
},
emotionChange:function(){
this.setData({
emotionVisible:!this.data.emotionVisible
})
},
addemotion:function(e){
console.log(e.currentTarget.dataset.index,"点了设默默")
let {connectemoji,input} = this.data
if (input) {
input = input + connectemoji[e.currentTarget.dataset.index];
}else {
input = connectemoji[e.currentTarget.dataset.index]
}
console.log(input,'输入框额值')
this.setData({
input
})
},
// 公共聚焦方法,方法比较笨,但是过度效果平滑流畅
bottom: function() {
var that = this;
// 获取元素的高度
let query = wx.createSelectorQuery();
query.select('.news').boundingClientRect(rect => {
//获取到元素
let scrollTop = rect.height;
this.setData({
scrollTop
})
}).exec();
console.log(this.data.scrollTop,'hahah')
wx.pageScrollTo({
// scrollTop: this.data.scrollTop + 30,
scrollTop: 10000,
// duration: 0
})
},
})
一、聊天功能
核心api
1.wx.connectSocket
2.wx.onSocketOpen
3.wx.onSocketMessage
4.wx.onSocketClose
二、发送消息后滚动到底部
核心方法
1.scroll-view 的使用(wxml)
<scroll-view
scroll-y="true"
scroll-into-view="{{scrollid}}"
scroll-with-animation = "{{true}}"
style="height:{{scrollHeight}}"
class="history"
refresher-enabled="true"
bindrefresherrefresh="refresh"
refresher-triggered="{{triggered}}"
>
</scroll-view>
2.scrollBottom方法(js)
// 滚动到底部
scrollBottom:function() {
var {newsList} = this.data
var scrollid = `scrollid${newsList.length - 1}`
this.setData({
scrollid
})
},
三、历史记录查询及下拉加载更多功能实现
核心部分
1.scroll-view 的使用(wxml)
<scroll-view
scroll-y="true"
scroll-into-view="{{scrollid}}"
scroll-with-animation = "{{true}}"
style="height:{{scrollHeight}}"
class="history"
refresher-enabled="true"
bindrefresherrefresh="refresh"
refresher-triggered="{{triggered}}"
>
</scroll-view>
2.getHistory方法(js)
// 获取历史记录
getHistory: function(){
var {receiveMemberId, sendMemberId,pageNo} = this.data
var params = {
receiveMemberId,
sendMemberId,
pageNo,
pageSize:5,
}
api.get("/zxxt/chat/msg/list", params, (res) => {
if (res.code == 'success') {
// var historyList = res.data.data
var historyList = [...res.data.data,...this.data.historyList]
if (historyList && historyList.length > 0) {
historyList.forEach(item => {
if (item.send_member_id == sendMemberId) {
item.type = 0
}else {
item.type = 1
}
});
this.setData({
historyList
})
console.log(this.data.historyList,'历史记录数据')
}else {
// 判断是否是第一次进入查看历史记录:是(不显示弹框,不是则显示弹框)
if(this.data.pageNo > 1) {
wx.showToast({
title: "没有更多历史记录了",
icon: 'none',
duration: 2000
})
}
}
} else {
if (res.message) {
wx.showToast({
title: res.message,
icon: 'none',
duration: 2000
})
}
}
}, (res) => {
if (res.message) {
wx.showToast({
title: res.message,
icon: 'none',
duration: 2000
})
}
})
},
注:此方法可写在初始化websocket的回掉函数中
四、微信小程序即时聊天功能其他注意事项
1.ws://10.200.157.18:8005/ 与wss的区别
2.微信小程序input使用hold-keyboard保持键盘不收起配置 hold-keyboard="{{true}}"
3.键盘遮挡input及textarea配置 cursor-spacing=“16px”
<input class="chat-input" type="text" bindinput="bindChange" confirm-type="done" value='{{input}}' cursor-spacing="16px" hold-keyboard="{{true}}" placeholder="" />
五、本文参考文章
包含前端后代码很有参考价值
相关git推荐