前端笔记之微信小程序(四)WebSocket&Socket.io&摇一摇案例&地图|地理位置
一、WebSocket概述
http://www.ruanyifeng.com/blog/2017/05/websocket.html
Workerman一款开源高性能异步PHP socket即时通讯框架https://workerman.net
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:
https://www.npmjs.com/package/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);
三、微信小程序和WebSocket
小程序的上线版本,必须是https协议会wss协议(即websocket的安全版本)
如果结合微信小程序使用,Nodejs不能使用socket.io,因为socket.io在前端需要用script引入一个js文件,但是小程序不支持这样引入。但是没有关系,因为小程序自带websocket的API。
前端开启:
- 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
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
- })
- }
- });
五、地图和地理位置
腾讯地理位置服务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
- })
- })
- }
- });
- },);
- }
- });
后端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;
- }
- })
- }
- });
- });
前端笔记之微信小程序(四)WebSocket&Socket.io&摇一摇案例&地图|地理位置的更多相关文章
- 前端笔记之微信小程序(二){{}}插值和MVVM模式&数据双向绑定&指令&API
一.双花括号{{}}插值和MVVM模式 1.1 体会{{}}插值 index.wxml的标签不是html的那些标签,这里的view就是div. {{}}这样的插值写法,叫做mustache语法.mus ...
- 前端笔记之微信小程序(一)初识微信小程序&WXSS与CSS|WXML与HTML的差异&像素和DPR
一.小程序概述 2017 年 1 月 9 日小程序正式上线,腾讯开放了个人开发者开发小程序,小程序从此就开始火爆,这一年,小程序狂揽 4 亿用户.1.7 亿的日常活跃,上线 58 万个.这是一个巨大的 ...
- 前端笔记之微信小程序(三)GET请求案例&文件上传和相册API&配置https
一.信息流小程序-GET请求案例 1.1服务端接口开发 一定要养成接口的意识,前端单打独斗出不来任何效果,必须有接口配合,写一个带有分页.关键词查询的接口: 分页接口:http://127.0.0.1 ...
- 微信小程序之WebSocket
本文版权归 OSChina jsongo0 所有,转载请标明出处,以示尊重! 原文:https://my.oschina.net/jsongo/blog/757871 为什么需要websocket?传 ...
- 对于前端,「微信小程序」其实不美好
微信小程序开放公测了,9月底我曾经写过一篇 「微信小程序」来了,其中最后一句:"谢天谢地,我居然还是个前端". 这种火爆的新事物总是令人激动,感谢这个时代. 但是,当我真作为开发者 ...
- 微信小程序开发——websocket测试
服务端 在windows下执行 node server.js 也可参照我的前一篇部署https var httpServ = require('http') var WebSocketServer ...
- mpvue学习笔记-之微信小程序数据请求封装
简介 美团出品的mpvue已经开源出来很久了,一直说要进行一次实践,这不最近一次个人小程序开发就用上了它. 看了微信官方的数据请求模块--request,对比了下get和post请求的代码,发现如果在 ...
- 微信小程序四(设置底部导航)
好了 小程序的头部标题 设置好了,我们来说说底部导航栏是如何实现的. 我们先来看个效果图 这里,我们添加了三个导航图标,因为我们有三个页面,微信小程序最多能加5个. 那他们是怎么出现怎么着色的呢?两步 ...
- 基于vs2015 SignalR开发的微信小程序使用websocket实现聊天功能
一)前言 在微信小程上实现聊天功能,大致有三种方式:1)小程序云开发 2)购买第三方IM服务 3)使用自己的服务器自己开发. 这里重要讲使用自己的服务器自己开发,并且是基于vs的开发. 网上提供的解决 ...
随机推荐
- Python连载20-偏函数&zip函数&enumerate函数
一. 偏函数 二. #先举个例子 #把字符串转换为十进制数字 int(') #help(int),int函数中有一个参数base代表把它转换某个进制的数字 #把八进制的字符串转换为十进制 eight ...
- Vue的生命周期函数
详解Vue Lifecycle 先来看看vue官网对vue生命周期的介绍 Vue实例有一个完整的生命周期,也就是从开始创建.初始化数据.编译模板.挂载Dom.渲染→更新→渲染.销毁等一系列过程,我们称 ...
- Nginx部署多个站点
Nginx部署多个站点 一,介绍与需求 1.1,介绍 详细介绍请看nginx代理部署Vue与React项目,在这儿主要介绍多个站点的配置 1.2,需求 有时候想在一台服务器上为不同的域名/不同的二级域 ...
- 利用MAT分析JVM内存问题,从入门到精通(二)
上一篇文章MAT入门到精通(一)介绍了MAT的使用场景和基本概念,这篇文章开始介绍MAT的基本功能,后面还有两篇,一篇是MAT的高级功能,另一篇是MAT实战案例分析. 三.欢迎页 使用MAT打开一个h ...
- 并发编程-concurrent指南-阻塞双端队列-链阻塞双端队列LinkedBlockingDeque
LinkedBlockingDeque是双向链表实现的阻塞队列.该阻塞队列同时支持FIFO和FILO两种操作方式,即可以从队列的头和尾同时操作(插入/删除): 在不能够插入元素时,它将阻塞住试图插入元 ...
- MySQL之基础操作
一.安装 Mysql是最流行的关系型数据库管理系统之一,由瑞典MySQL AB公司开发,目前属于Oracle公司. MySQL是一种关联数据库管理系统,关联数据库将数据保存在不同的表中,而不是将所有数 ...
- git简单使用-GitHub
本文描述window下如何使用git工具,操作GitHub远程代码库 一,准备工作: 1,安装git工具,一路默认next安装即可,下载地址 2,注册账号或者创建厂库(已有忽略) 注册账号后,创建仓库 ...
- 究竟什么是Windows句柄
图解说明——究竟什么是Windows句柄 这里需要说明: 1.这里将句柄所能标识的所有东西(如窗口.文件.画笔等)统称为“对象”. 2.图中一个小横框表示一定大小的内存区域,并不代表一个字节, ...
- Oracle数据库常用的脚本命令(一)
--连接数据库的命令connect,用于切换连接用户,简写形式conn--语法格式:conn 用户名/密码conn yanln/yanln --显示当前登录的用户show user --执行操作系统的 ...
- 微信小程序之楼层效果
今天做了一个小程序实现一个楼层效果 带大家分享下经验和api的使用吧 如图 将左边和右边各分了一个组件 目录如下 其中list页面是这个楼层效果的页面 components是组成这个页面的两个组件 ...