项目app页面的开发采用mui框架来实现,支持模拟器和移动端的运行,真机运行会提示安装hbuilder基座调试程序。彷佛开发状况下只能再同一个网段下测试运行,跨网段就没法链接到资源数据。javascript
若是是将项目部署到真机上去调试能够在以下路径去找项目文件:css
内存>android>data>io.dcloud.HBuilder>apps>wwwhtml
也能够自定义部署资源的路径 前端
如:_doc/audio/ 就是指:内存>android>data>io.dcloud.HBuilder>apps>doc>audio>文件资源名java
窗口页面采用HTML5来显示。android
index.html为应用首页,通常只须要实现底部选项卡便可,其余功能能够在主页面或其余页面去完善:web
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Document</title>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<link rel="stylesheet" type="text/css" href="css/mui.min.css"/>
</head>
<body>
<!--底部选项卡-->
<nav class="mui-bar mui-bar-tab">
<a class="mui-tab-item mui-active">
<span class="mui-icon mui-icon-home"></span>
<span class="mui-tab-label">首页</span>
</a>
<a class="mui-tab-item">
<span class="mui-icon mui-icon-phone"></span>
<span class="mui-tab-label">电话</span>
</a>
<a class="mui-tab-item">
<span class="mui-icon mui-icon-email"></span>
<span class="mui-tab-label">邮件</span>
</a>
<a class="mui-tab-item">
<span class="mui-icon mui-icon-gear"></span>
<span class="mui-tab-label">设置</span>
</a>
</nav>
<script src="js/mui.min.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript"> mui.init({ subpages: [{ url: "main.html", id: "main.html", styles: { top:'0px', //mui标题栏默认高度为45px;
bottom:'50px' //默认为0px,可不定义;
} }] }); var ws=new WebSocket("ws://192.168.1.114:3721/app/app01"); document.addEventListener("send_music",function (data) { var send_str=data.detail //{to_user:"toy123",music:"sdata.album_name"}
ws.send(JSON.stringify(send_str)); }) </script>
</body>
</html>
main.html是应用的主页面,实现了数据的列表展现:数据库
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Document</title>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<link rel="stylesheet" type="text/css" href="css/mui.min.css"/>
<script src="js/mui.min.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<!--标题-->
<header class="mui-bar mui-bar-nav">
<h1 class="mui-title">音乐</h1>
</header>
<!--图片轮播-->
<div class="mui-content">
<div id="slider" class="mui-slider" >
<div class="mui-slider-group mui-slider-loop" style="height:300px; width: auto;">
<!-- 额外增长的一个节点(循环轮播:第一个节点是最后一张轮播) -->
<div class="mui-slider-item mui-slider-item-duplicate">
<a href="#">
<img src="http://192.168.1.114:9527/get_lb/1.jpg">
</a>
</div>
<!-- 第一张 -->
<div class="mui-slider-item">
<a href="#">
<img src="http://192.168.1.114:9527/get_lb/1.jpg">
</a>
</div>
<!-- 第二张 -->
<div class="mui-slider-item">
<a href="#">
<img src="http://192.168.1.114:9527/get_lb/2.jpg">
</a>
</div>
<!-- 第三张 -->
<div class="mui-slider-item">
<a href="#">
<img src="http://192.168.1.114:9527/get_lb/3.jpg">
</a>
</div>
<!-- 第四张 -->
<div class="mui-slider-item">
<a href="#">
<img src="http://192.168.1.114:9527/get_lb/4.jpg">
</a>
</div>
<!-- 额外增长的一个节点(循环轮播:最后一个节点是第一张轮播) -->
<div class="mui-slider-item mui-slider-item-duplicate">
<a href="#">
<img src="http://192.168.1.114:9527/get_lb/1.jpg">
</a>
</div>
</div>
<div class="mui-slider-indicator">
<div class="mui-indicator mui-active"></div>
<div class="mui-indicator"></div>
<div class="mui-indicator"></div>
<div class="mui-indicator"></div>
</div>
</div>
</div>
<!--图文列表-->
<ul class="mui-table-view" id="con_list">
</ul>
<script type="text/javascript"> mui.init() mui.plusReady(function () { mui.post(window.serv+'/get_list',{ },function(data){ for (var i = 0; i < data.data.length; i++) { creat_item(data.data[i]) } },'json' ); }) function creat_item (content) { var li=document.createElement("li") li.className="mui-table-view-cell mui-media"; var a=document.createElement("a") a.onclick=function () { mui.openWindow({ url:"player.html", id:"player.html", extras:content }) } var img=document.createElement("img") img.className="mui-media-object mui-pull-left"; img.src=window.serv_img+content.music_img var div=document.createElement("div") div.className="mui-media-body" div.innerText=content.album_name var p=document.createElement("p") p.className="mui-ellipsis" p.innerText=content.author_name li.appendChild(a) a.appendChild(img) a.appendChild(div) div.appendChild(p) document.getElementById("con_list").appendChild(li) } </script>
</body>
</html>
player.html歌曲的播放页面,并实现websocket链接远程控制播放曲目:json
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Document</title>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<link rel="stylesheet" type="text/css" href="css/mui.min.css"/>
<style type="text/css"> @-webkit-keyframes rotation { from { -webkit-transform: rotate(0deg);
} to { -webkit-transform: rotate(360deg);
} } .an { -webkit-transform: rotate(360deg); animation: rotation 3s linear infinite; -moz-animation: rotation 3s linear infinite; -webkit-animation: rotation 3s linear infinite; -o-animation: rotation 3s linear infinite;
}
</style>
</head>
<body>
<header class="mui-bar mui-bar-nav">
<a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>
<h1 class="mui-title" id="title">正在播放</h1>
</header>
<div class="mui-content" style="text-align:center; margin-top: 20px;">
<img class="an" src="" id="cover"/ style="height:150px; width:150px; border-radius:50%;">
</div>
<button type="button" id="zt" class="mui-btn mui-btn-yellow mui-btn-block">暂停</button>
<button type="button" id="bf" class="mui-btn mui-btn-green mui-btn-block">播放</button>
<button type="button" id="tz" class="mui-btn mui-btn-red mui-btn-block">中止</button>
<button type="button" id="fs" class="mui-btn mui-btn-blue mui-btn-block">发送</button>
<script src="js/mui.min.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript"> mui.init() var sdata=null; var myplayer=null mui.plusReady(function () { sdata=plus.webview.currentWebview() document.getElementById("title").innerText="正在播放 - "+sdata.album_name document.getElementById("cover").src=window.serv_img+sdata.music_img myplayer=plus.audio.createPlayer(window.serv_music+sdata.play_url) myplayer.play() }) document.getElementById('zt').addEventListener('tap',function () { myplayer.pause() }) document.getElementById('bf').addEventListener('tap',function () { myplayer.resume() }) document.getElementById('tz').addEventListener('tap',function () { myplayer.stop() }) document.getElementById('fs').addEventListener('tap',function () { var index=plus.webview.getWebviewById("HBuilder"); mui.fire(index,'send_music',{to_user:"toy123",music:sdata.play_url}); }) </script>
</body>
</html>
为了不hbuilder中的js频繁的给后端发送AJAX,更换ip,能够将ip设置为所有变量,在引用文件的js文件添加代码以下:flask
window.serv="http://192.168.1.114:9527";
window.serv_img=window.serv+"/get_photo/";
window.serv_music=window.serv+"/get_music/";
H5页面实现首页加载子页面main.html :
mui.init({ subpages: [{ url: "main.html", id: "main.html", styles: { top:'0px', //mui标题栏默认高度为45px;
bottom:'50px' //默认为0px,可不定义;为了避免遮住index底部选项卡
} }] });
图文列表的数据展现:
在不肯定记录条数的状况下,会经过查询数据库,对数据迭代,去显示数据列表,因此在body主体里面只会有一个空列表元素:
<!--图文列表-->
<ul class="mui-table-view" id="con_list">
</ul>
经过JavaScript给后端发送post请求,去获取记录数,并迭代生成列表元素:
mui.init() mui.plusReady(function () { mui.post(window.serv+'/get_list',{ },function(data){ for (var i = 0; i < data.data.length; i++) { creat_item(data.data[i]) } },'json' ); }) #window.serv指向js文件中配置好的ip地址
返回的数据以字典形式推送给前端JavaScript ,迭代数据传递给creat_item去生成列表元素。
function creat_item (content) { var li=document.createElement("li") li.className="mui-table-view-cell mui-media"; var a=document.createElement("a") a.onclick=function () { mui.openWindow({ url:"player.html", id:"player.html", extras:content }) } var img=document.createElement("img") img.className="mui-media-object mui-pull-left"; img.src=window.serv_img+content.music_img #去后台请求对应图片信息数据 var div=document.createElement("div") div.className="mui-media-body" div.innerText=content.album_name var p=document.createElement("p") p.className="mui-ellipsis" p.innerText=content.author_name li.appendChild(a) a.appendChild(img) a.appendChild(div) div.appendChild(p) document.getElementById("con_list").appendChild(li) }
后端会有路由去处理这个请求:
在实际项目中,为了便于扩展,一般会采用蓝图注册到app的方式去处理请求。处理请求文件放到serv目录下。
获取数据列表:
from flask import Blueprint,jsonify from setting import mongoDB,RET content=Blueprint("content",__name__) @content.route('/get_list',methods=["POST",]) def get_list(): music=list(mongoDB.music.find({})) for index,item in enumerate(music): music[index]["_id"]=str(item.get("_id")) RET["code"]=0 RET["msg"]="查询列表数据" RET["data"]=music return jsonify(RET)
列表数据显示完成,点击列表,实现跳转播放页面player.html :
a.onclick=function () { mui.openWindow({ url:"player.html", id:"player.html", extras:content }) }
将字典数据传递给新页面player.html,在新页面获取传递过来的数据字典:
并获取播放曲目图片和歌曲名,建立一个播放对象来播放歌曲
mui.init() var sdata=null; var myplayer=null mui.plusReady(function () { sdata=plus.webview.currentWebview() document.getElementById("title").innerText="正在播放 - "+sdata.album_name document.getElementById("cover").src=window.serv_img+sdata.music_img myplayer=plus.audio.createPlayer(window.serv_music+sdata.play_url) #去后台请求音乐数据 myplayer.play() })
至此已经能完成图文列表正常显示数据,点击列表,打开播放页面,播放歌曲。效果如图所示:
关于图片的旋转能够经过设置img的样式来实现:
<div class="mui-content" style="text-align:center; margin-top: 20px;">
<img class="an" src="" id="cover"/ style="height:150px; width:150px; border-radius:50%;">
</div>
style样式设置:
<style type="text/css"> @-webkit-keyframes rotation { from { -webkit-transform: rotate(0deg); } to { -webkit-transform: rotate(360deg); } } .an { -webkit-transform: rotate(360deg); animation: rotation 3s linear infinite; -moz-animation: rotation 3s linear infinite; -webkit-animation: rotation 3s linear infinite; -o-animation: rotation 3s linear infinite; } </style>
进一步实现经过websocket远程通讯,实现指定客户端播放app中指定的音乐:
即:点击app中的发送按钮,远程的客户端(玩具)会切换到当前播放歌曲。来播放新的音乐
监听“发送按钮”事件,将数据传递至index页面,并建立websocket对象,经过websocket对象将数据发送给后端:
<button type="button" id="fs" class="mui-btn mui-btn-blue mui-btn-block">发送</button>
在JavaScript中监听:
document.getElementById('fs').addEventListener('tap',function () { var index=plus.webview.getWebviewById("HBuilder"); mui.fire(index,'send_music',{to_user:"toy123",music:sdata.play_url}); })
在index中去监听send_music事件:并获取携带的数据
var ws=new WebSocket("ws://192.168.1.114:3721/app/app01"); document.addEventListener("send_music",function (data) { var send_str=data.detail //{to_user:"toy123",music:"sdata.album_name"} ws.send(JSON.stringify(send_str)); })
运行程序就建立app的websocket对象,此时,后端会经过路由去获取app_id和app的websocket对象,保存在空字典里,准备后续的使用。
后端websocket的代码实现:
from flask import Flask,request from geventwebsocket.handler import WebSocketHandler from gevent.pywsgi import WSGIServer from geventwebsocket.websocket import WebSocket import json ws_app=Flask(__name__) user_socket_dict={} @ws_app.route("/app/<app_id>") def app(app_id): user_socket= request.environ.get("wsgi.websocket") #type:WebSocket if user_socket: user_socket_dict[app_id]=user_socket # {'app01': <geventwebsocket.websocket.WebSocket object at 0x0000022989215250>} print(user_socket_dict) while 1: user_msg=user_socket.receive() # {"to_user":"toy123","music":"8155d294-0552-460d-b5f5-57afddbfdfd4.mp3"} <class 'str'> msg_dict=json.loads(user_msg) toy_socket=user_socket_dict.get(msg_dict.get("to_user")) #获取当前客户端的websocket对象 toy_socket.send(user_msg) #给对象发送以前的数据,必须是str类型 不然报错 @ws_app.route("/toy/<toy_id>") def toy(toy_id): user_socket=request.environ.get("wsgi.websocket") # type: WebSocket if user_socket: user_socket_dict[toy_id]=user_socket # 'toy123': <geventwebsocket.websocket.WebSocket object at 0x00000229892152B8> print(user_socket_dict) while 1: user_msg=user_socket.receive() print(user_msg) if __name__ == '__main__': http_serv=WSGIServer(("0.0.0.0",3721),ws_app,handler_class=WebSocketHandler) http_serv.serve_forever()
app的websocket逻辑代码写完后,还须要建立一个客户端toy的websocket对象,一样会存储到字典里去
toy页面代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>玩具</title> </head> <body> <audio autoplay="autoplay" controls id="player"></audio> <script type="application/javascript"> var addr="192.168.1.114"; var ws=new WebSocket("ws://"+addr+":3721/toy/toy123"); ws.onmessage=function (data) { console.log(data.data); var msg=JSON.parse(data.data); document.getElementById("player").src="http://"+addr+":9527/get_music/"+msg.music; } </script> </body> </html>
添加访问toy客户端的路由地址到player.py flask框架
@app.route("/") def toy(): return render_template("toy.html")
基本完成该阶段的逻辑代码,项目运行流程:
1.启动player.py 运行flask框架,启动服务器。
2.运行脚本文件play_ws.py 启动websocket服务
3.访问toy客户端页面,将toy的toy_id和websocket对象传递给后端
4.启用app项目,将app_id和app的websocket对象传递给后端。
5.后端的websocket中会将数据保存在字典中
{'toy123': <geventwebsocket.websocket.WebSocket object at 0x00000293E4655250>, 'app01': <geventwebsocket.websocket.WebSocket object at 0x00000293E46552B8>}
6.点开图文列表,音乐开始播放,点击发送,会将当前音乐的歌名和toy_id以字典方式发送给index.html页面
7.index.html页面获取数据,并将字典对象转化为str,发送给后端app路由去处理
8.后端收到数据,反序列化,获得内容,按照收件人的id去字典中获取到这个websocket对象,给这个对象发送收到的数据 未反序列化的str字典
9.toy获取数据字典中music的值,并读取到audio中播放该曲目
toy收到app经过websocket发过来的代码: