首頁 > web前端 > js教程 > 實現微信UI的聊天功能

實現微信UI的聊天功能

php中世界最好的语言
發布: 2018-03-19 16:38:56
原創
3098 人瀏覽過

這次帶給大家實現微信UI的聊天功能,實現微信UI的聊天功能的注意事項有哪些,下面就是實戰案例,一起來看一下。

微信小程式最近很火,火到什麼程度,只要你一打開微信,就是它的身影,幾乎你用的各個APP都可以在微信中找到它的複製版,另外官方自帶的跳一跳更是將它推到了空前至高的位置。對比公眾號,就我的感覺來說,有以下區別:

  • 公眾號略顯繁瑣:我首先要關注才能看到內容,而小程序不用(個人對微信公眾號研究不深,不對之處還望見諒)

  • 小程式性能要好一些:雖然我不是很清楚小程式用什麼實現,就體驗來說確實更接近原生一點;但是微信公眾號是用網頁的形式來展示內容的,其中的兼容性和性能問題不用我說,各位luer就已經清楚了吧

  • 小程序更易開發:小程式發布了一套新的程式碼規則,也提供了一系列的元件,對比公眾號百家爭鳴的形式確實要統一得多

廢話說了這麼多,我也是最近才開始看小程式的實現方式,體驗了一把,確實比較爽,以下就是個人開發總結:

簡易的官網小程式

微信小程式官網中有個簡單的小demo,地址在這裡:https://mp.weixin.qq.com/debug/wxadoc/dev/index.html,按照它的步驟來,一定是可以運行一個和官方一樣的例子出來的,這裡就不貼過程了。主要說一下個人整體感受:

  • js還是原來的js,css還是原來的css,html方面來說,是改了一點東西,比如:p變成了view,文字變成了text,以及img變成了image,但是換湯不換藥,該怎麼用還是怎麼用,而且語意也更加明確。

  • 增加了設定檔.json,全域有一個app.json,是全域的配置,例如導覽列、TAB的配置,全域路由的配置等等,而在每個頁面中,依然是可以進行全域覆蓋的,例如list.json中單獨規定了列表頁長啥樣子。

  • 每個頁面都有生命週期(包括啟動頁),類似於react/vue的聲明週期,更明確在哪個階段可以做哪些事情

  • 程式碼元件化,很多封裝的元件都可以簡單引用,例如map,而在微信公眾號上開發的時候,你可能還需要專門寫一個地圖外掛

  • API更加好用,雖然我沒多少開發過公眾號,但是就之前配置的jssdk來說,就感覺比小程式複雜,小程式只需要一個appId就可以了,然後在程式碼中直接使用wx物件來呼叫各種API

開發一個類似微信UI的簡單聊天程序

只是感興趣稍微做了一下案例,其中功能可能根本就還只是九牛一毛,但是覺得有必要記錄一下,說說自己遇到的問題以及解決辦法,界面整體如下:

實現微信UI的聊天功能

首先,在app.json中寫頁面路由,如下:

1

2

3

4

5

6

7

8

9

10

11

12

{

  "pages":[        

    "pages/index/index",

    "pages/list/list",

    "pages/chat/chat"

  ],

  "window":{

    "backgroundTextStyle":"light",

    "navigationBarBackgroundColor""#000",

    "navigationBarTitleText""WeChat",

    "navigationBarTextStyle":"#fff"

  }}

登入後複製

這裡有3個頁面,首頁放一個按鈕當作入口,清單頁表示聊天記錄,還有一個聊天頁。

清單頁沒有什麼可以講的,設定清單頁的標題可以在list.json中設定即可,如下:

1

2

// list.json{

  "navigationBarTitleText""聊天列表"}

登入後複製

清單頁模擬了一些數據,然後再點擊每一條的時候,進入單一聊天頁面當中,其中需要將當前點擊的一些資訊傳入下一個頁面當中,這裡僅僅只有名字。

1

2

3

4

5

6

7

8

9

//chat.js//获取应用实例const app = getApp()const friends = require('./list-mock-data.js')Page({

  data: {

    friends: friends.list

  },

  gotoChat(event) {

    const currentUser = event.currentTarget.dataset.user;

    wx.navigateTo({

      url: '../chat/chat?nickname=' + currentUser.nickname

    })  }})

登入後複製

然後進入聊天頁面,首先進入聊天頁面我想到的是,每一個氣泡加上它的頭像是否可以做成一個組件,因為只有左右的區分而已,另外如果再加上時間的話,再將時間傳遞過去就可以了。

因此chat.wxml最開始就是這樣規劃的:

1

2

<block>

  <template></template></block>

登入後複製

template中的代码就不展示了,最开始我写模板的时候,是开了一个codePen,然后模拟写出来之后,再往模板中套,保证基本的样子差不多,然后再在模板上进行细微的改动就可以了。

聊天页顶部的标题是通过列表页中传过来的,在页面加载完成的时候,设置就好了:

1

2

3

4

5

// chat.js// 设置昵称setNickName(option) {

    const nickname = option.nickname || 'Marry';

    wx.setNavigationBarTitle({

      title: nickname    });

  },

登入後複製

最开始的样子就是这样子的:

實現微信UI的聊天功能

至此,基本的页面形态就已经完成了。

遇到的一些问题:

  • 每次进入页面的时候,即使聊天内容已经超过了聊天区域,都会显示为最开始的地方

  • 输入新的聊天记录的时候,如果聊天内容不是处于最底部,那么新加的内容会看不到

针对这两个问题,我按照自己最初的想法是:进入页面获取scrollHieght然后计算scrollTop值,将其滚动就好了,至于第二个问题按照类似的方法就可以解决了,但是我查看小程序的API之后,并没有发现如何计算scrollHeight的方法。只有类似的API,如:boundingClientRectscrollTop

好在天无绝人之路,看到了scroll-view中的scroll-into-view属性,于是就想出了解决上面两个问题的方法:

  • 进入页面,获取历史纪录,获取最后一条消息的ID值,记为lastId,在渲染的时候,消息列表中的每个ID值传入组件,作为每个消息记录的唯一标识,然后使用scroll-in-view={{ id }}就可以轻松地使最后一条消息进入视野当中

  • 在聊天的时候,新加的记录会更新这个lastId值,这样就自动更新视图了

1

2

3

4

5

6

7

8

// chat.wxml<scroll-view>

    <block>

      <template></template>    </block>

  </scroll-view>// chat.jsPage({  data: {    messages: [],         // 聊天记录    msg: '',              // 当前输入    lastId: ''            // 最后一条消息的ID    // ...  },  // ...  send() {    // ...    const data = {      id: `msg${++nums}`,      message: msg,      messageType: 0,      url: '../../images/5.png'

    };

    this.setData({ msg: '', lastId: data.id });

  }

});

登入後複製

这样就可以大致实现类似于聊天的效果了,但是还有一个小问题,每次从列表中进入单个聊天页面的时候,会有一个斜向左上方滑动的过程,原因是:页面的转场动画是向左的,但是自动滚动到最后一条记录的动作是向上的,所以会有动作叠加,既然这样,我只需要让滚动的过程延迟一段时间就好

1

2

3

4

5

6

7

8

9

// 延迟页面向顶部滑动

  delayPageScroll() {

    const messages = this.data.messages;

    const length = messages.length;

    const lastId = messages[length - 1].id;

    setTimeout(() => {

      this.setData({ lastId });

    }, 300);

  },

登入後複製

至此问题就算是解决了,在真机模拟的时候,IOS还有一个问题,就是当点击输入框的时候,整体页面会向上顶起来,这个问题我在论坛中也有看到,但是没有找到解决办法,如果各位有遇到,还望不吝赐教。

扩展延伸

如果是一个真正的聊天程序应该怎么做呢?我的设想是这样的:

實現微信UI的聊天功能-one

由于当时自己的机器由于莫名的原因不能够进行登录,后来采用了本地开了一个websocket的服务器来实现消息的发送。服务器代码相当简单,只是消息的转发而已

1

2

3

4

5

6

// server.jsconst WebSocket = require('ws');const wss = new WebSocket.Server({ port: 12112 });wss.on('connection', ws => {

  console.log('connection established');

  ws.on('message', message => {

    console.log("on message coming");

    ws.send(message);

  });});

登入後複製

chat.js中需模拟历史消息的发送以及新加消息的发送,因此代码整体看起来是这样的:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

//chat.js//获取应用实例const app = getApp()const msgs = require('./chat-mock-data.js');Page({

  data: {

    messages: [],         // 聊天记录

    msg: '',              // 当前输入

    scrollTop: 0,         // 页面的滚动值

    socketOpen: false,    // websocket是否打开

    lastId: '',           // 最后一条消息的ID

    isFirstSend: true     // 是否第一次发送消息(区分历史和新加)

  },

  onLoad(option) {

    // 设置标题

    this.setNickName(option);

  },

  //事件处理函数

  onReady() {

    // 连接websocket服务器

    this.connect();

  },

  onUnload() {

    const socketOpen = this.data.socketOpen;

    if (socketOpen) {

      wx.closeSocket({});

      wx.onSocketClose(res => {

        console.log('WebSocket 已关闭!')      });

    }

  },

  connect() {

    wx.connectSocket({

      url: 'ws://localhost:12112'

    });

    wx.onSocketOpen(res => {

      this.setData({ socketOpen: true });

      // 模拟历史消息的发送

      wx.sendSocketMessage({

        data: JSON.stringify(msgs),

      })    });

    wx.onSocketMessage(res => {

      const isFirstSend = this.data.isFirstSend;

      const data = JSON.parse(res.data);

      let messages = this.data.messages;

      let lastId = '';

       

      // 第一次为接收历史消息,

      // 之后的为新加的消息

      if (isFirstSend) {

        messages = messages.concat(data);

        lastId = messages[0].id;

        this.setData({ messages, lastId, isFirstSend: false });

        // 延迟页面向顶部滑动

        this.delayPageScroll();

      else {

        messages.push(data);

        const length = messages.length;

        lastId = messages[length - 1].id;

        this.setData({ messages, lastId });

      }

    });

    wx.onSocketError(res => {

      console.log(res);

      console.log('WebSocket连接打开失败,请检查!')    })  },

  // 设置昵称

  setNickName(option) {

    const nickname = option.nickname || 'Marry';

    wx.setNavigationBarTitle({

      title: nickname    });

  },

  // 延迟页面向顶部滑动

  delayPageScroll() {

    const messages = this.data.messages;

    const length = messages.length;

    const lastId = messages[length - 1].id;

    setTimeout(() => {

      this.setData({ lastId });

    }, 300);

  },

  // 输入

  onInput(event) {

    const value = event.detail.value;

    this.setData({ msg: value });

  },

  // 聚焦

  onFocus() {

    this.setData({ scrollTop: 9999999 });

  },

  // 发送消息

  send() {

    const socketOpen = this.data.socketOpen;

    let messages = this.data.messages;

    let nums = messages.length;

    let msg = this.data.msg;

    if (msg === '') {

      return false;

    }

    const data = {

      id: `msg${++nums}`,

      message: msg,

      messageType: 0,

      url: '../../images/5.png'

    };

    this.setData({ msg: '' });

     

    if (socketOpen) {

      wx.sendSocketMessage({

        data: JSON.stringify(data)      })    }

  }})

登入後複製

整体来说,自己的思路就像是上面的代码所描述的,这个只是初步的构想,还有很多东西需要完善:

  • 头像

  • 列表页和聊天页新消息的处理

  • 数据库的历史消息存储

  • 图片以及语音的发送

  • 消息本地化存储

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

Vue指令的使用

JS闭包的使用

以上是實現微信UI的聊天功能的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板