前端筆記之微信小程序(四)WebSocket&Socket.io&搖一搖案例&地圖|地理位置

1、WebSocket概述

http://www.ruanyifeng.com/blog/2017/05/websocket.htmljavascript

 

Workerman一款開源高性能異步PHP socket即時通信框架https://workerman.nethtml

 

HTTP是無鏈接的:有請求才會有響應,若是沒有請求,服務器想主動推送信息給瀏覽器是不可能的。前端

 

好比圖文直播、聊天室原理:長輪詢。java

setInterval(function(){
$.get()
},1000)

間隔必定的時間,主動向服務器發起請求,詢問是否有新消息。node

 

WebSocket是一種網絡通訊協議,HTML5中的新協議。須要服務器和瀏覽器共同支持,實現全雙工通訊。jquery

 

服務器:PHP5.6Java1.7Nodejs 6以上。git

瀏覽器:Android 6.0及以上版本。web

WebSocket HTML5 開始提供的一種在單個 TCP 鏈接上進行全雙工通信的協議。express

 

WebSocket 使得客戶端和服務器之間的數據交換變得更加簡單,容許服務端主動向客戶端推送數據。在 WebSocket API 中,瀏覽器和服務器只須要完成一次握手,二者之間就直接能夠建立持久性的鏈接,並進行雙向數據傳輸。npm

WebSocket API 中,瀏覽器和服務器只須要作一個握手的動做,而後,瀏覽器和服務器之間就造成了一條快速通道。二者之間就直接能夠數據互相傳送。


2、Socket.io

socket.io是一個跨瀏覽器支持WebSocket的實時通信的JSNodejs中實現socket很是好用的包。

 

API

https://www.npmjs.com/package/socket.io

https://socket.io/

npm install --save socket.io

默認有一個自動路由的js文件http://127.0.0.1:3000/socket.io/socket.io.js

 

 

前端代碼(從官網抄的模板):

<!doctype html>
<html>
  <head>
    <title>Socket.IO chat</title>
    <style>
      * { margin: 0; padding: 0; box-sizing: border-box; }
      body { font: 13px Helvetica, Arial; }
      form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
      form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
      form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
      #messages { list-style-type: none; margin: 0; padding: 0; }
      #messages li { padding: 5px 10px; }
      #messages li:nth-child(odd) { background: #eee; }
    </style>
  </head>
  <body>
    <ul id="messages"></ul>
    <form action="">
          <input id="m" autocomplete="off" />
        <button>Send</button>
    </form>
    <script type="text/javascript" src="/socket.io/socket.io.js"></script> <script type="text/javascript"> var socket = io(); </script>
  </body>
</html>

 

後端:

var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);

app.get('/', function(req, res){
      res.sendFile(__dirname + '/index.html');
});

//監聽客戶端,有用戶鏈接的時候觸發(創建先後端鏈接)
io.on('connection', function(socket){
      console.log('有個用戶鏈接了'); });

http.listen(3000);
node app.js

 

如今兩個端已經實時通信鏈接上了:

 

 

消息收發的響應:

前端emit發:

<script type="text/javascript">
      var socket = io();
      $("button").click(function(){
             socket.emit("info", "你好");
             return false;
      });
</script>

 

服務端on收:

io.on('connection', function(socket){
      console.log('有個用戶鏈接了');
      socket.on("info", function(data){ console.log(data); });
});

接下來的事情:

實現聊天室功能:若是有某個客戶端用戶將消息發給了服務端,服務端要發給全部已經鏈接的客戶端,這裏就涉及到廣播,廣播就是給全部已經鏈接服務端的socket對象進行集體的消息發送。

 

完整的聊天室前端:

<!doctype html>
<html>
  <head>
    <title>Socket.IO chat</title>
    <style>
     ...
    </style>
  </head>
  <body>
    <ul id="messages"></ul>
    <form action="">
          <input id="m" autocomplete="off" />
        <button>Send</button>
    </form>
    <script type="text/javascript" src="/socket.io/socket.io.js"></script>
    <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
    <script type="text/javascript">
        var socket = io();

        //點擊按鈕發出數據
        $("button").click(function(){
            socket.emit("info" , {
                content : $("#m").val()
            });
            $("#m").val("");
            return false;  //阻止瀏覽器默認請求行爲,禁止刷新頁面
        });

        //客戶端收到服務端的msg廣播消息的時候觸發的函數
        socket.on("msg", function(data){
            $("<li>" + data.content + "</li>").prependTo("ul");
        });
    </script>
  </body>
</html>

 

後端:

io.on('connection', function(socket){
      console.log('有個用戶鏈接了');
      //服務端收到了info的消息
      socket.on("info" , function(data){
          console.log(data.content);
          //當即廣播通知全部已鏈接的客戶端
          io.emit('msg', data);
      });
});

http.listen(3000);

3、微信小程序和WebSocket

小程序的上線版本,必須是https協議會wss協議(即websocket的安全版本)

 

若是結合微信小程序使用,Nodejs不能使用socket.io,由於socket.io在前端須要用script引入一個js文件,可是小程序不支持這樣引入。可是沒有關係,由於小程序自帶websocketAPI

 

 

前端開啓:

Page({
    onLoad(){
        wx.connectSocket({
            url: 'ws://127.0.0.1:8080',
        })
    }
})
示例代碼

 

後端須要安裝一個依賴:

https://www.npmjs.com/package/ws

npm install --save ws

後端app.js  

const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', function connection(ws){
    console.log("有人連接了");
});
示例代碼

 

<!--index.wxml-->
<view class="container">
    <view class="t">{{a}}</view>
    <button bindtap="sendmsg">按我</button>
</view>
//index.js
Page({
    data: {
        a: 0
    },
    onLoad(){
        //前端發起websocket鏈接
        wx.connectSocket({
            // 能夠在WiFi環境下的IP地址測試
            // url: 'ws://192.168.0.150:8080', 
            url: 'ws://127.0.0.1:8080'
        })

        //監聽WebSocket接受到服務器的廣播消息通知事件
        wx.onSocketMessage((res)=>{
            console.log(res.data)
            this.setData({
                a:res.data
            })
        })
    },
    //點擊按鈕發送消息給服務端
    send(){
        wx.sendSocketMessage({
            data: "你好!",
        })
    }
})
示例代碼

 

後端app.js

Nodejsws這個庫沒有廣播功能,必須讓開發者將socket對象存爲數組,要廣播的時候,遍歷數組中每一個項,依次給他們發送信息便可。

const WebSocket = require('ws');
//建立鏈接和監聽端口
const wss = new WebSocket.Server({port:8080});

var ws_arr = []; //存儲全部已經鏈接的人的ws對象
var a = 0;

//響應客戶端的鏈接
wss.on('connection', function(ws){
    console.log("有人鏈接了");
    ws_arr.push(ws); //將當前進來的人存儲到數組

    //監聽客戶端發送的消息
    ws.on("message", function(message){
        console.log("服務端收到了消息:" + message)

        a++;
        //遍歷全部人,廣播通知全部客戶端,把消息傳送給他們
        ws_arr.forEach(item=>{
            item.send(a);
        })
    })
})
示例代碼

4、搖一搖大PK

微信沒有提供搖一搖API,必須使用加速,本身寫代碼感應xyz的變化。

加速計的座標軸如圖,是個三維的座標。咱們須要經過xyz三個軸的方向的加速度計算出搖動手機時,手機搖動方向的加速度。

 

 

 

index.js

page({
data:{
   x:0,
   y:0,
   z:0
},
onLoad(){
    var lastX = 0;
    var lastY = 0;
    wx.onAccelerometerChange((res)=>{
            //若是當前的x或y減去上一次x或y的差 大於0.5,就設定爲搖一搖成功
            if(Math.abs(res.x - lastX) > 0.5 || Math.abs(res.y - lastY) > 0.5){
                wx.showToast({
                   title: "成功"
                });
                lastX = res.x;
                lastY = res.y;

                this.setData({
                   x : res.x,
                   y : res.y,
                   z : res.z 
                })
            }
            
})
}
})
示例代碼

後端app.js

const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });

//存儲全部人的ws對象
var ws_arr = [];
//存儲全部人的分數
// var score_arr = ["nickName":"測試帳戶","avatarUrl":"x.jpg", "n":0];
var score_arr = [];
var a = 0;

wss.on('connection', function(ws){
    console.log("有人連接了");
    ws_arr.push(ws); //將每一個進來的用戶存儲到數組


    ws.on('message', function(message){
        console.log("服務器收到了推送:" + message);
        //變爲JSON對象
        var messageObj = JSON.parse(message);
        //當搖一搖時,判斷數組中有沒有這我的,有就讓這我的的n++
        var isHave = false;
        score_arr.forEach(item=>{
            if(item.nickName == messageObj.nickName){
                item.n ++;
                isHave = true;
            }
        });
        //若是沒有就添加到數組中
        if(!isHave){
            score_arr.push({
                nickName : messageObj.nickName,
                avatarUrl: messageObj.avatarUrl,
                n : 0
            })
        }

        console.log({"score_arr" : score_arr})
        //廣播發送給客戶端(前端)
        ws_arr.forEach(item=>{
            item.send(JSON.stringify({
                "score_arr" : score_arr })); });  });
});

 

用戶搖一搖案例:

<!--index.wxml-->
<view class="container">
    <view class="userinfo">
        <button open-type="getUserInfo" bindgetuserinfo="getUserInfo">獲取頭像暱稱</button>
    </view>
    
    <view wx:for="{{arr}}">
        {{item.nickName}}
        <image style="width:90px;height:90px;" src="{{item.avatarUrl}}"></image>
        {{item.n}}
    </view>
</view>

 

index.js

const app = getApp()

Page({
    data: {
        userInfo: {},
        hasUserInfo: false,
        canIUse: wx.canIUse('button.open-type.getUserInfo'),
        arr : []
    },
    onLoad: function () {
        if (app.globalData.userInfo) {
            this.setData({
                userInfo: app.globalData.userInfo,
                hasUserInfo: true
            })
        } else if (this.data.canIUse) {
            // 因爲 getUserInfo 是網絡請求,可能會在 Page.onLoad 以後才返回
            // 因此此處加入 callback 以防止這種狀況
            app.userInfoReadyCallback = res => {
                this.setData({
                    userInfo: res.userInfo,
                    hasUserInfo: true
                })
            }
        } else {
            // 在沒有 open-type=getUserInfo 版本的兼容處理
            wx.getUserInfo({
                success: res => {
                    app.globalData.userInfo = res.userInfo
                    this.setData({
                        userInfo: res.userInfo,
                        hasUserInfo: true
                    })
                }
            })
        }

        //連接socket服務器(能夠填WiFi的IP地址測試)
        wx.connectSocket({
            url: 'ws://127.0.0.1:8080'
        });

        //當socket鏈接打開後,監聽搖一搖:
        var self = this;
        var lastX = 0;
        wx.onSocketOpen(function(res){
            wx.onAccelerometerChange(function(res){
                //若是當前的x 減去上一次x的差 大於0.6,就設定爲搖一搖成功
                if(Math.abs(res.x - lastX) > 0.6){
                    wx.showToast({
                        title: "搖一搖成功"
                    });
                    //告訴服務器我是誰
 wx.sendSocketMessage({
                        data: JSON.stringify({ "nickName": self.data.userInfo.nickName,
                            "avatarUrl": self.data.userInfo.avatarUrl })
                    })
                } lastX = res.x;
            }); }); //接收到服務器廣播信息的時候作的事情
        wx.onSocketMessage(function(res){
            var obj = JSON.parse(res.data); //轉對象
            var arr = obj.score_arr;
            //按照n值大小排序
            arr.sort((a,b)=>{
                return b.n - a.n
            })
            self.setData({arr}); //存儲到本地data中的arr數組
 });
    },
    getUserInfo: function (e) {
        console.log(e)
        app.globalData.userInfo = e.detail.userInfo
        this.setData({
            userInfo: e.detail.userInfo,
            hasUserInfo: true
        })
    } 
});

5、地圖和地理位置

騰訊地理位置服務https://lbs.qq.com/ 

地圖本身是不能定位的,須要獲取地理位置定位,並且地圖API和地理位置API是分開。

<!--index.wxml-->
<view class="container">
    <view class="userinfo">
        <button open-type="getUserInfo" bindgetuserinfo="getUserInfo">獲取頭像暱稱</button>
    </view>
    
   <map markers="{{markers}}" id="map" longitude="{{longitude}}" latitude="{{latitude}}" 
  scale
="14" style="width:100%;height:300px;"></map> </view>
Page({
    data: {
        markers : []
    },
    onLoad(){
        //頁面加載進來要先定位
        var self = this;
        wx.getLocation({
            type: 'gcj02',
            success: function(res){
                self.setData({
                    latitude: res.latitude, //緯度
                    longitude: res.longitude //經度
 }); }
       }); //鏈接socket服務器
        wx.connectSocket({
            url: 'ws://192.168.1.175:8080'
        });

//接收到服務器廣播信息的時候作的事情
        wx.onSocketMessage(function (res) {
            var obj = JSON.parse(res.data);
        console.log(obj)
}
//微信沒有提供當某人地理位置改變時候的on事件,因此setInterval()。
//每3秒更新一次定位,而後發送給服務端,服務端再通知客戶端
clearInterval(timer);
var timer = setInterval(function(){ wx.getLocation({
        type: 'gcj02',
        success: function(res){
            wx.sendSocketMessage({
                data: JSON.stringify({ "nickName": self.data.userInfo.nickName,
                    "avatarUrl": self.data.userInfo.avatarUrl,
                    "latitude": res.latitude,
                    "longitude": res.longitude })
            })
        }
  }); },3000);
    }
});

 

後端app.js

const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });

var ws_arr = []; //存儲全部人的ws對象
var location_arr = []; //存儲全部人的地理位置

wss.on('connection', function (ws) {
    console.log("有人連接了");
    //放入數組
    ws_arr.push(ws);
       
    ws.on('message', function (message) {
        console.log("服務器收到了推送" + message);
        //變爲JSON對象
        var messageObj = JSON.parse(message);
        //判斷數組中有沒有我
        var isHave = false;
        location_arr.forEach(item=>{
            if(item.nickName == messageObj.nickName){
                item.latitude = messageObj.latitude;
                item.longitude = messageObj.longitude;
                isHave = true;
            }
        });
        //若是沒有
        if(!isHave){
            location_arr.push({
                nickName : messageObj.nickName,
                avatarUrl: messageObj.avatarUrl,
                latitude : messageObj.latitude, longitude: messageObj.longitude
            })
        }
        console.log({"location_arr" : location_arr})
    
        //廣播通知客戶端
        ws_arr.forEach(item=>{
            item.send(JSON.stringify({
                "location_arr" : location_arr
            }));
        });
    });
});

臨時設置大頭針markers

 

var tempPathObj = {}; //存儲這我的的暱稱,根據暱稱獲取頭像,如這個對象沒有這我的就要下載頭像
//接收到服務器廣播信息的時候作的事情
wx.onSocketMessage(function(res){
    var obj = JSON.parse(res.data);
    self.setData({ markers : [] //清空
});
//iconPath不支持網絡地址,要經過wx.download()接口下載獲得臨時地址
obj.location_arr.forEach(item=>{
    //根據暱稱,判斷這個對象中有沒有這我的,若是有直接用
        if(tempPathObj.hasOwnProperty(self.data.userInfo.nickName)){
    self.setData({
                markers: [ ...self.data.markers,
                    {   //若是對象中有這我的,就直接用這我的的頭像
                        iconPath: tempPathObj[self.data.userInfo.nickName], 
                        id: 0,
                        latitude: item.latitude,
                        longitude: item.longitude,
                        width: 50,
                        height: 50
                    }
                ]
            });
        } else {
        //若是沒有就下載頭像,而且將這我的存起來,之後能夠直接用
 wx.downloadFile({
            url: item.avatarUrl,
                success(data){
                    console.log(data.tempFilePath);
                    self.setData({
                        markers : [ ...self.data.markers,
                            {
                                iconPath: data.tempFilePath,
                                id: 0,
                                latitude: item.latitude,
                                longitude: item.longitude,
                                width: 50,
                                height: 50
                            }
                        ]
                    }); // console.log(self.data.markers);
                    //將頭像的臨時地址存儲給這我的
                    tempPathObj[self.data.userInfo.nickName] = data.tempFilePath;
               } })
        }
    });
});
相關文章
相關標籤/搜索