小程式即時通訊demo

RR?發表於2018-09-30

前段時間寫了一個小程式即時聊天demo,仿微信,效果如下:

專案地址是:github.com/lirongrong/…,

大家可以自行下載,按照提示執行,就能看到效果;現在只是做了要給基本版的,要做複雜功能,可以繼續新增。

小程式即時通訊demo

功能

  1. 傳送文字
  2. 傳送圖片(圖片可點選放大)
  3. 傳送拍攝照片 (圖片可點選放大)
  4. 傳送位置 (map元件預設在最頂層,樣式控制不了,bug還在修復中...)
  5. 傳送語音 (包括語音時長,可點選播放)

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
    });
  }
})
複製程式碼

需要優化的地方

  1. 上傳圖片應該要支援多圖上傳並壓縮一下,我做h5的聊天功能的時候壓縮了,這個簡版的小程式沒做,大家可以自行加上
  2. 這個demo只是實現了UI和文字的通訊,圖片、視訊、地圖等的通訊還沒完善
  3. 傳送訊息之後滾到底部的方法需要改進,因為傳送圖片、地圖、語音沒有滾到底部
  4. 需要改進的請大神指點

相關文章