項目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發過來的代碼: