前段時間寫了一個小程式即時聊天demo,仿微信,效果如下:
專案地址是:github.com/lirongrong/…,
大家可以自行下載,按照提示執行,就能看到效果;現在只是做了要給基本版的,要做複雜功能,可以繼續新增。
功能
- 傳送文字
- 傳送圖片(圖片可點選放大)
- 傳送拍攝照片 (圖片可點選放大)
- 傳送位置 (map元件預設在最頂層,樣式控制不了,bug還在修復中...)
- 傳送語音 (包括語音時長,可點選播放)
websorket長連線
是基於nodejs-websocket的服務,程式碼如下:(最基礎版)
var ws = require("nodejs-websocket")
// Scream server example: "hi" -> "HI!!!"
//建立一個server
var server = ws.createServer(function (conn) {
console.log("New connection")
conn.on("text", function (str) {
// console.log("Received "+str)
// conn.sendText(str.toUpperCase()+"!!!")
//連結成功之後,傳送歡迎語
console.log("連線成功")
//歡迎語
if(str == 'null'){
conn.sendText("有什麼能幫到您?");
}
//輸入文字
else if(str != 'null' && str){
conn.sendText("文字")
}
//輸入多媒體
else{
conn.sendText("多媒體文字")
}
console.log(str);
})
conn.on("close", function (code, reason) {
console.log("Connection closed")
})
}).listen(8001)
複製程式碼
在專案根目錄下執行npm run dev
服務就能啟動了,
啟動之後websorket地址為:ws://localhost:8001
chat.js
直接看程式碼,註釋都寫清楚了
// pages/user/chat.js
var util = require('../utils/util.js');
var app = getApp();
//websocket心跳重連物件
let heartCheck = {
timeout: 1000,//1s
timeoutObj: null,
serverTimeoutObj: null,
//重置
reset: function () {
clearTimeout(this.timeoutObj);
clearTimeout(this.serverTimeoutObj);
return this;
},
//開始
start: function () {
wx.sendSocketMessage({
data: "null",
});
},
};
//微信小程式新錄音介面,錄出來的是aac或者mp3,這裡要錄成mp3
const recorderManager = wx.getRecorderManager();
const options = {
duration: 600000,//錄音時長,這裡設定的是最大值10分鐘
sampleRate: 44100,
numberOfChannels: 1,
encodeBitRate: 192000,
format: 'mp3',
//frameSize: 50
};
//音訊播放
const innerAudioContext = wx.createInnerAudioContext()
Page({
data: {
taskId:'',
userId:'',
chatList:[],//聊天內容
isShowModelUp:false,//底部彈框顯示true,隱藏為false
isLuYin:false,//沒有錄音false,開始錄音true
luYinText:'按住說話',
audioUrl:'',//錄音檔案地址
isShowLuYin:false,//true為開始播放,false為取消播放
inputValue:'',//輸入框內容
lockReconnect:false,//預設進來是斷開連結的
limit:0,//重連次數
},
onLoad: function (options) {
this.linkSocket();
},
//連線socket
linkSocket:function(){
let that = this;
wx.connectSocket({
//url: app.globalData.wsUrl + 'websocket?' + this.data.taskId + '&' + this.data.userId,
url:app.globalData.wsUrl,
success() {
console.log('連線成功')
wx.onSocketMessage((res) => {
console.log(res.data);
//收到訊息
that.pushChatList(0, {
text: res.data
});
})
wx.onSocketOpen(() => {
console.log('WebSocket連線開啟')
heartCheck.reset().start()
})
wx.onSocketError(function (res) {
console.log('WebSocket連線開啟失敗')
that.reconnect()
})
wx.onSocketClose(function (res) {
console.log('WebSocket已關閉!')
that.reconnect()
})
}
})
},
//斷線重連
reconnect() {
var that = this;
if (that.lockReconnect) return;
that.lockReconnect = true;
clearTimeout(that.timer)
if (that.data.limit < 12) {
that.timer = setTimeout(() => {
that.linkSocket();
that.lockReconnect = false;
}, 5000);
that.setData({
limit: that.data.limit + 1
})
}
},
//開啟底部彈框
showModelUp:function(){
var that=this;
if (that.data.isShowModelUp==false){
that.setData({
isShowModelUp: true,
})
}else{
that.setData({
isShowModelUp: false,
})
}
},
//關閉底部彈框
closeModelUp:function(){
var that=this;
that.setData({
isShowModelUp:false,
})
},
//選擇照片
chooseImage:function(){
var that=this;
wx.chooseImage({
count: 1, // 預設9
sizeType: ['original', 'compressed'], // 可以指定是原圖還是壓縮圖,預設二者都有
sourceType: ['album'], // 可以指定來源是相簿還是相機,預設二者都有
success: function (res) {
// 返回選定照片的本地檔案路徑列表,tempFilePath可以作為img標籤的src屬性顯示圖片
var tempFilePaths = res.tempFilePaths;
console.log(res);
that.pushChatList(1,{
imgUrl: tempFilePaths,
})
//關閉彈窗
that.closeModelUp();
that.pageScrollToBottom();
}
})
},
//介面滾到最底端
pageScrollToBottom: function () {
wx.createSelectorQuery().select('#bottom').boundingClientRect(function (rect) {
console.log(rect.top);
console.log(rect.bottom);
// 使頁面滾動到底部
wx.pageScrollTo({
scrollTop: rect.bottom + 200
})
}).exec()
},
//預覽圖片
previewImage:function(e){
console.log(e);
var url=e.currentTarget.dataset.src;
var that=this;
wx.previewImage({
current: url[0], // 當前顯示圖片的http連結
urls: url // 需要預覽的圖片http連結列表
})
},
//拍攝
paishe:function(){
var that = this;
wx.chooseImage({
count: 1, // 預設9
sizeType: ['original', 'compressed'], // 可以指定是原圖還是壓縮圖,預設二者都有
sourceType: ['camera'], // 可以指定來源是相簿還是相機,預設二者都有
success: function (res) {
// 返回選定照片的本地檔案路徑列表,tempFilePath可以作為img標籤的src屬性顯示圖片
var tempFilePaths = res.tempFilePaths;
console.log(res);
that.pushChatList(1,{
imgUrl: tempFilePaths,
})
//關閉彈窗
that.closeModelUp();
that.pageScrollToBottom();
}
})
},
//傳送位置
getlocat: function () {
var that = this
wx.getLocation({
type: 'gcj02', //返回可以用於wx.openLocation的經緯度
success: function (res) {
that.setData({
latitude: res.latitude,
longitude: res.longitude,
markers: [{
latitude: res.latitude,
longitude: res.longitude,
name: '時代一號',
desc: '現在的位置'
}],
})
that.pushChatList(1,{
map: true
})
}
})
that.closeModelUp();
that.pageScrollToBottom();
},
//切換是否錄音按鈕
btnRecord:function(){
var that=this;
if (that.data.isLuYin==false){
that.setData({
isLuYin: true
});
}else{
that.setData({
isLuYin: false,
luYinText: '按住說話'
});
}
},
//開始錄音
startRecord:function(e){
var that=this;
that.setData({
luYinText:'錄音中...',
});
recorderManager.start(options);
recorderManager.onStart(() => {
console.log('recorder start')
})
},
//結束錄音
stopRecord:function(){
var that = this;
that.setData({
luYinText: '按住說話'
});
recorderManager.stop();
recorderManager.onStop((res) => {
console.log('recorder stop', res)
const { tempFilePath } = res;
that.pushChatList(1,{
audioUrl: res.tempFilePath,
audioDuration: (res.duration / 60000).toFixed(2),//錄音時長,轉為分,向後取兩位,
})
that.setData({
audioUrl: res.tempFilePath,
audioDuration: (res.duration / 60000).toFixed(2),//錄音時長,轉為分,向後取兩位,
})
})
//關閉彈窗
that.closeModelUp();
that.pageScrollToBottom();
},
//錄音、停止播放
playRecord:function(e){
console.log(e);
var that=this;
innerAudioContext.autoplay = true;
innerAudioContext.src = that.data.audioUrl
//innerAudioContext.src = 'http://ws.stream.qqmusic.qq.com/M500001VfvsJ21xFqb.mp3?guid=ffffffff82def4af4b12b3cd9337d5e7&uin=346897220&vkey=6292F51E1E384E061FF02C31F716658E5C81F5594D561F2E88B854E81CAAB7806D5E4F103E55D33C16F3FAC506D1AB172DE8600B37E43FAD&fromtag=46';//測試音訊檔案
if (!e.currentTarget.dataset.isshowluyin){//開始播放
//innerAudioContext.play();//相容起見用它
innerAudioContext.onPlay(() => {
console.log('開始播放');
that.setData({
isShowLuYin: true
});
return;
});
}else{//暫停播放
innerAudioContext.pause();
console.log("暫停");
that.setData({
isShowLuYin: false
});
return;
}
},
//輸入框點選完成按鈕時觸發
btnConfirm:function(e){
var that = this;
if (typeof (e) == 'undefined' || e.detail.value == ''){
return false;
}else {
var value = e.detail.value;
that.pushChatList(1,{
text: value
});
that.setData({
inputValue:''//清空輸入框
})
//傳送資料
wx.sendSocketMessage({
data: value
})
//關閉彈窗
that.closeModelUp();
that.pageScrollToBottom();
}
},
//頁面隱藏/切入後臺時觸發
onHide:function(){
wx.onSocketClose(function (res) {
console.log('WebSocket已關閉!')
})
},
//頁面解除安裝時觸發
onUnload:function(){
wx.onSocketClose(function (res) {
console.log('WebSocket已關閉!')
})
},
//pushchatList
//enu:0 是客服傳送的訊息
//enu:1 是我傳送的訊息
pushChatList:function(enu,options){
var that = this;
var defaults = {
userImage: '',
text: '',
isAdmin: false,
}
options = util.extendObj(defaults,options);
options.time = util.formatDateTime(util.getNowFormatDate());
console.log(options);
if(enu == 0){
options.userImage = '../images/admin.png';
options.isAdmin = false;
}else if(enu==1){
options.userImage = app.globalData.wxUserInfo.avatarUrl;
options.isAdmin = true;
}
var t = that.data.chatList;
t.push(options)
that.setData({
chatList: t
});
}
})
複製程式碼
需要優化的地方
- 上傳圖片應該要支援多圖上傳並壓縮一下,我做h5的聊天功能的時候壓縮了,這個簡版的小程式沒做,大家可以自行加上
- 這個demo只是實現了UI和文字的通訊,圖片、視訊、地圖等的通訊還沒完善
- 傳送訊息之後滾到底部的方法需要改進,因為傳送圖片、地圖、語音沒有滾到底部
- 需要改進的請大神指點