前端笔记之微信小程序(四)WebSocket&Socket.io&摇一摇案例&地图|地理位置
一、websocket概述
workerman一款开源高性能异步php socket即时通讯框架
http是无连接的:有请求才会有响应,如果没有请求,服务器想主动推送信息给浏览器是不可能的。
比如图文直播、聊天室原理:长轮询。
setinterval(function(){ $.get() },1000)
间隔一定的时间,主动向服务器发起请求,询问是否有新消息。
websocket是一种网络通信协议,是html5中的新协议。需要服务器和浏览器共同支持,实现全双工通信。
服务器:php5.6、java1.7、nodejs 6以上。
浏览器:android 6.0及以上版本。
websocket 是 html5 开始提供的一种在单个 tcp 连接上进行全双工通讯的协议。
websocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 websocket api 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
在 websocket api 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
二、socket.io
socket.io是一个跨浏览器支持websocket的实时通讯的js。nodejs中实现socket非常好用的包。
api:
npm install --save 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);
三、微信小程序和websocket
小程序的上线版本,必须是https协议会wss协议(即websocket的安全版本)
如果结合微信小程序使用,nodejs不能使用socket.io,因为socket.io在前端需要用script引入一个js文件,但是小程序不支持这样引入。但是没有关系,因为小程序自带websocket的api。
前端开启:
page({ onload(){ wx.connectsocket({ url: 'ws://127.0.0.1:8080', }) } })
后端需要安装一个依赖:
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
nodejs的ws这个库没有广播功能,必须让开发者将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); }) }) })
四、摇一摇大pk
微信没有提供摇一摇api,必须使用加速计,自己写代码感应x、y、z的变化。
加速计的坐标轴如图,是个三维的坐标。我们需要通过x、y、z三个轴的方向的加速度计算出摇动手机时,手机摇动方向的加速度。
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 }) } });
五、地图和地理位置
腾讯地理位置服务
地图自己是不能定位的,需要获取地理位置定位,而且地图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; } }) } }); });
上一篇: Android.mk语法说明
下一篇: 网站添加百度分享插件