近年來,資料可視化大屏的出現,掀起一番又一番的浪潮,眾多企業主紛紛想要打造屬于自己的 “酷炫吊炸天” 的霸道總裁大屏駕駛艙,今天為大家分享的是 【運動健康資料可視大屏案例】,
話不多說,開始分享干貨,歡迎討論!QQ微信同號: 6550523
??效果展示??
1、首先看動態效果圖

2、再看實時分片資料圖

3、豐富的主題樣式





一、 確定需求方案
1、確定產品上線部署的螢屏LED解析度
1280px*768px,F11全屏后占滿整屏無滾動條;其它解析度螢屏可自適應顯示,
2、功能模塊
- 實時步數
- 實時心率
- 實時血氧飽和度
- 實時壓力
- 運動卡路里等級
心率&血氧飽和度
血壓
每日步數
體重
卡路里
壓力
3、部署方式
- 基于免安裝可執行程式:支持Windows、Linux、Mac等各種主流作業系統;將可執行程式exe復制到服務器上即可,無需其它環境依賴;
- 觀看方式:既可在服務器上直接觀看程式界面,也可遠程使用瀏覽器打開播放,支持Chrome瀏覽器、360瀏覽器等主流瀏覽器,
二、整體架構設計
- 前端基于Echarts開源庫設計,使用WebStorm編輯器;
- 后端基于Python Web實作,使用Pycharm編輯器;
- 資料傳輸格式:JSON;
- 資料源型別:目前已支持PostgreSQL、MySQL、Oracle、Microsoft SQL Server、SQLite、Excel表格等,還可以定制HTTP API介面方式或其它型別資料庫,
- 資料更新方式:摒棄了前端頁面定時拉取的方式(這種方式帶來嚴重的資源浪費),采用后端資料實時更新,實時推送到前端展示;
三、編碼實作 (基于篇幅及可讀性考慮,此處展示部分關鍵代碼)
1、前端頁面布局html代碼
<body style="background-color:rgba(10, 2, 39, 0.796)">
<div class="container_fluid">
<div class="row_fluid" id="vue_app">
<div class="col-xs-12 col-md-12" style="padding:0 0;">
<h3 style="color:rgba(11, 240, 125, 0.796); text-align: center" id="container_0">
<strong></strong>
</h3>
<dv-decoration-5 style="width=100%;height:4%;">
</dv-decoration-5>
</div>
<div class="col-xs-12 col-md-3" style="padding:0 0;">
<div style="height:15%; padding:0%;">
<dv-border-box-7>
<p style='padding:2%; color:rgba(11, 240, 125, 0.796); font-size: 12; font-weight: bolder;'>實時步數
<img src="myimg/1.png" style="height: 25%; ">
</p>
<p id="container_1"
style='padding:0%; color:rgba(255,255,255,.5); font-size: 28; font-weight: bolder; text-align: center;'>
112222</p>
</dv-border-box-7>
</div>
</div>
<div class="col-xs-12 col-md-3" style="padding:0 0;">
<div style="height:15%; padding:1%;">
<dv-border-box-7>
<p style='padding:2%; color:rgba(11, 240, 125, 0.796); font-size: 12; font-weight: bolder;'>心率
<img src="myimg/2.png" style="height: 25%; ">
</p>
<p id="container_2"
style='padding:0%; color:rgba(255,255,255,.5); font-size: 28; font-weight: bolder; text-align: center;'>
112222</p>
</dv-border-box-7>
</div>
</div>
<div class="col-xs-12 col-md-3" style="padding:0 0;">
<div style="height:15%; padding:1%;">
<dv-border-box-7>
<p style='padding:2%; color:rgba(11, 240, 125, 0.796); font-size: 12; font-weight: bolder;'>
血氧飽和度
<img src="myimg/3.png" style="height: 25%; ">
</p>
<p id="container_3"
style='padding:0%; color:rgba(255,255,255,.5); font-size: 28; font-weight: bolder; text-align: center;'>
112222</p>
</dv-border-box-7>
</div>
</div>
<div class="col-xs-12 col-md-3" style="padding:0 0;">
<div style="height:15%; padding:1%;">
<dv-border-box-7>
<p style='padding:2%; color:rgba(11, 240, 125, 0.796); font-size: 12; font-weight: bolder;'>壓力
<img src="myimg/4.png" style="height: 25%; ">
</p>
<p id="container_4"
style='padding:0%; color:rgba(255,255,255,.5); font-size: 28; font-weight: bolder; text-align: center;'>
112222</p>
</dv-border-box-7>
</div>
</div>
<div class="col-xs-12 col-md-5" style="padding:0 0;">
<div style="height:75%;" id="container_5"></div>
</div>
<div class="col-xs-12 col-md-4" style="padding:0 0.3%;height:25%;">
<dv-border-box-6>
<div style="padding:3%; height:100%" id="container_6">
</div>
</dv-border-box-6>
</div>
<div class="col-xs-12 col-md-3" style="padding:0 0.3%;height:25%;">
<dv-border-box-6>
<div style="padding:3%; height:100%" id="container_7">
</div>
</dv-border-box-6>
</div>
<div class="col-xs-12 col-md-4" style="padding:0 0.3%;height:25%;">
<dv-border-box-6>
<div style="padding:3%; height:100%" id="container_8">
</div>
</dv-border-box-6>
</div>
<div class="col-xs-12 col-md-3" style="padding:0 0.3%;height:25%;">
<dv-border-box-6>
<div style="padding:3%; height:100%" id="container_9">
</div>
</dv-border-box-6>
</div>
<div class="col-xs-12 col-md-4" style="padding:0 0.3%;height:25%;">
<dv-border-box-6>
<div style="padding:3%; height:100%" id="container_10">
</div>
</dv-border-box-6>
</div>
<div class="col-xs-12 col-md-3" style="padding:0 0.3%;height:25%;">
<dv-border-box-6>
<div style="padding:3%; height:100%" id="container_11">
</div>
</dv-border-box-6>
</div>
</div>
</div>
</body>
2、前端JS模塊代碼
運動卡路里 - 旭日圖
var idContainer_5 = "container_5";
var chartDom = document.getElementById(idContainer_5);
var myChart = echarts.init(chartDom, gTheme);
option = {
title: {
text: "運動卡路里等級",
left: "left",
textStyle: {
color: "rgba(11, 240, 125, 0.796)",
fontSize: "12",
},
},
series: {
type: "sunburst",
data: [],
radius: [3, "90%"],
itemStyle: {
borderRadius: 15,
borderWidth: 1,
borderColor: "rgba(255,255,255,.1)",
},
label: {
textStyle:{
color: "rgba(0,0,0,.8)",
}
},
},
};
// 使用剛指定的配置項和資料顯示圖表,
myChart.setOption(option);
window.addEventListener("resize", function () {
myChart.resize();
});
function asyncData_5() {
$.getJSON("json/sunburst.json").done(function (data) {
var myChart = echarts.init(document.getElementById(idContainer_5));
myChart.setOption({
series: [{ data: data }],
});
}); //end $.getJSON
}
asyncData_5();
心率&血氧飽和度 - 雙折線面積圖
var idContainer_6 = "container_6";
function initEchart_6() {
var myChart = echarts.init(
document.getElementById(idContainer_6),
window.gTheme
);
option = {
title: {
text: "心率&血氧飽和度",
left: "left",
textStyle: {
color: "rgba(11, 240, 125, 0.796)",
fontSize: "10",
},
},
tooltip: {
trigger: "axis",
axisPointer: {
lineStyle: {
color: "#dddc6b",
},
},
},
legend: {
top: "0%",
data: ["心率", "血氧飽和度"],
textStyle: {
color: "rgba(255,255,255,.5)",
fontSize: 10,
},
},
grid: {
left: "5%",
top: "30%",
right: "5%",
bottom: "5%",
containLabel: true,
},
xAxis: [
{
type: "category",
boundaryGap: false,
axisLabel: {
textStyle: {
color: "rgba(255,255,255,.5)",
fontSize: 10,
},
},
axisLine: {
lineStyle: {
color: "rgba(255,255,255,.2)",
},
},
splitLine: {
lineStyle: {
color: "rgba(255,255,255,.1)",
},
},
},
],
yAxis: [
{
type: "value",
min: 0,
max: 200,
axisTick: { show: false },
axisLabel: {
textStyle: {
color: "rgba(255,255,255,.5)",
fontSize: 10,
},
// formatter: '{value} KM',
},
axisLine: {
lineStyle: {
color: "rgba(255,255,255,.1)",
},
},
splitLine: {
lineStyle: {
color: "rgba(255,255,255,.1)",
},
},
},
{
type: "value",
min: 1,
max: 100,
axisTick: { show: false },
axisLine: {
lineStyle: {
color: "rgba(255,255,255,.1)",
},
},
axisLabel: {
textStyle: {
color: "rgba(255,255,255,.5)",
fontSize: 10,
},
formatter: '{value}%',
},
splitLine: {
lineStyle: {
color: "rgba(255,255,255,.1)",
},
},
},
],
series: [
{
name: "心率",
type: "line",
// 平滑曲線 或 折線
// smooth: true,
// 面接圖
areaStyle: {},
symbol: "circle",
symbolSize: 5,
lineStyle: {
normal: {
// color: '#0184d5',
width: 2,
},
},
data: [],
},
{
name: "血氧飽和度",
type: "line",
yAxisIndex: 1,
// 平滑曲線 或 折線
smooth: true,
symbol: "circle",
symbolSize: 5,
lineStyle: {
normal: {
// color: '#0184d5',
width: 2,
},
},
data: [],
},
],
};
// 使用剛指定的配置項和資料顯示圖表,
myChart.setOption(option);
window.addEventListener("resize", function () {
myChart.resize();
});
}
function asyncData_6() {
$.getJSON("json/line.json").done(function (data) {
var myChart = echarts.init(document.getElementById(idContainer_6));
myChart.setOption({
xAxis: data["xAxis"],
series: data["series"],
});
}); //end $.getJSON
}
initEchart_6();
asyncData_6();
血壓 - 雙折線圖
var idContainer_7 = "container_7";
function initEchart_7() {
var myChart = echarts.init(
document.getElementById(idContainer_7),
window.gTheme
);
option = {
title: {
text: "血壓",
left: "left",
textStyle: {
color: "rgba(11, 240, 125, 0.796)",
fontSize: "10",
},
},
tooltip: {
trigger: "axis",
axisPointer: {
lineStyle: {
color: "#dddc6b",
},
},
},
legend: {
top: "0%",
data: ["高壓", "低壓"],
textStyle: {
color: "rgba(255,255,255,.5)",
fontSize: 10,
},
},
grid: {
left: "5%",
top: "30%",
right: "5%",
bottom: "5%",
containLabel: true,
},
xAxis: [
{
type: "category",
boundaryGap: false,
axisLabel: {
textStyle: {
color: "rgba(255,255,255,.5)",
fontSize: 10,
},
},
axisLine: {
lineStyle: {
color: "rgba(255,255,255,.2)",
},
},
splitLine: {
lineStyle: {
color: "rgba(255,255,255,.1)",
},
},
},
],
yAxis: [
{
type: "value",
name: "mmHg",
min: 0,
max: 300,
axisTick: { show: false },
axisLabel: {
textStyle: {
color: "rgba(255,255,255,.5)",
fontSize: 10,
},
// formatter: '{value} KM',
},
axisLine: {
lineStyle: {
color: "rgba(255,255,255,.1)",
},
},
splitLine: {
lineStyle: {
color: "rgba(255,255,255,.1)",
},
},
},
],
series: [
{
name: "高壓",
type: "line",
// 平滑曲線 或 折線
smooth: true,
// 面接圖
// areaStyle: {},
symbol: "circle",
symbolSize: 5,
lineStyle: {
normal: {
// color: '#0184d5',
width: 2,
},
},
data: [],
},
{
name: "低壓",
type: "line",
smooth: true,
symbol: "circle",
symbolSize: 5,
lineStyle: {
normal: {
// color: '#0184d5',
width: 2,
},
},
data: [],
},
],
};
// 使用剛指定的配置項和資料顯示圖表,
myChart.setOption(option);
window.addEventListener("resize", function () {
myChart.resize();
});
}
function asyncData_7() {
$.getJSON("json/line_7.json").done(function (data) {
var myChart = echarts.init(document.getElementById(idContainer_7));
myChart.setOption({
xAxis: data["xAxis"],
series: data["series"],
});
}); //end $.getJSON
}
initEchart_7();
asyncData_7();
每日步數 - 日歷圖
var idContainer_8 = "container_8";
var chartDom = document.getElementById(idContainer_8);
var myChart = echarts.init(chartDom, window.gTheme);
var option;
function getVirtulData(year) {
year = year || "2021";
const timeElapsed = Date.now();
var date = +echarts.number.parseDate(year + "-01-01");
var end = +echarts.number.parseDate(+year + 1 + "-01-01");
var dayTime = 3600 * 24 * 1000;
var data = [];
for (var time = date; time < end; time += dayTime) {
data.push([
echarts.format.formatTime("yyyy-MM-dd", time),
Math.floor(Math.random() * 10000),
]);
}
return data;
}
option = {
title: [
{
text: "每日步數",
left: "left",
textStyle: {
color: "rgba(11, 240, 125, 0.796)",
fontSize: "10",
},
},
],
tooltip: {},
visualMap: {
min: 0,
max: 10000,
splitNumber: 2,
type: "piecewise",
orient: "horizontal",
left: "center",
top: 20,
textStyle: {
color: "rgba(255,255,255,.5)",
fontSize: 10,
},
},
calendar: {
top: 65,
bottom: 10,
left: 30,
right: 30,
cellSize: ["auto", 13],
// 顯示坐標系的區段
range: ['2021-01-01','2021-8-18'],
itemStyle: {
borderWidth: 0.5,
},
monthLabel: {
textStyle: {
color: "rgba(255,255,255,.5)",
fontSize: 10,
},
},
dayLabel: {
textStyle: {
color: "rgba(255,255,255,.5)",
fontSize: 10,
},
},
Label: {
textStyle: {
color: "rgba(255,255,255,.5)",
fontSize: 10,
},
},
yearLabel: { show: false },
},
series: {
type: "heatmap",
coordinateSystem: "calendar",
data: getVirtulData(2021),
},
};
option && myChart.setOption(option);
window.addEventListener("resize", function () {
myChart.resize();
});
function asyncData_8() {
$.getJSON("json/calendar.json").done(function (data) {
var myChart = echarts.init(document.getElementById(idContainer_8));
myChart.setOption(
data
);
}); //end $.getJSON
}
asyncData_8();
體重 - 柱狀圖
var idContainer_9 = "container_9";
var chartDom = document.getElementById(idContainer_9);
var myChart = echarts.init(chartDom, window.gTheme);
var option;
option = {
title: {
text: "體重",
left: "left",
textStyle: {
color: "rgba(11, 240, 125, 0.8)",
fontSize: "10",
},
},
grid: {
left: "3%",
right: "4%",
bottom: "3%",
top: "30%",
containLabel: true,
},
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow",
},
},
xAxis: {
type: "category",
data: [],
axisLabel: {
textStyle: {
color: "rgba(255,255,255,.5)",
fontSize: 10,
},
},
axisLine: {
lineStyle: {
color: "rgba(255,255,255,.2)",
},
},
splitLine: {
lineStyle: {
color: "rgba(255,255,255,.1)",
},
},
},
yAxis: {
type: "value",
min: 0,
max: 100,
axisLabel: {
textStyle: {
color: "rgba(255,255,255,.5)",
fontSize: 10,
},
formatter: '{value}%',
},
axisLine: {
lineStyle: {
color: "rgba(255,255,255,.2)",
},
},
splitLine: {
lineStyle: {
color: "rgba(255,255,255,.1)",
},
},
},
series: [
{
data: [],
type: "bar",
showBackground: true,
backgroundStyle: {
color: "rgba(180, 180, 180, 0.1)",
},
},
],
};
option && myChart.setOption(option);
myChart.setOption(option);
window.addEventListener("resize", function () {
myChart.resize();
});
function asyncData_9() {
$.getJSON("json/bar_background.json").done(function (data) {
var myChart = echarts.init(document.getElementById(idContainer_9));
myChart.setOption({
xAxis: data["xAxis"],
series: data["series"],
});
});
}
asyncData_9();
卡路里 - 面積折線圖
var idContainer_10 = "container_10";
var chartDom = document.getElementById(idContainer_10);
var myChart = echarts.init(chartDom, window.gTheme);
var option;
option = {
title: {
text: "卡路里",
left: "left",
textStyle: {
color: "rgba(11, 240, 125, 0.796)",
fontSize: "10",
},
},
grid: {
left: "3%",
right: "5%",
bottom: "5%",
// 距離頂部邊框的相對距離,太近壓到了legend
// top:'30%',
containLabel: true,
},
tooltip: {
trigger: "axis",
axisPointer: {
// Use axis to trigger tooltip
type: "shadow", // 'shadow' as default; can also be 'line' or 'shadow'
},
},
xAxis: {
type: "category",
boundaryGap: false,
axisLabel: {
textStyle: {
color: "rgba(255,255,255,.5)",
fontSize: 10,
},
// formatter: '{value}%',
},
axisLine: {
lineStyle: {
color: "rgba(255,255,255,.2)",
},
},
splitLine: {
lineStyle: {
color: "rgba(255,255,255,.1)",
},
},
data: [],
},
yAxis: {
type: "value",
name: "千卡",
axisLabel: {
textStyle: {
color: "rgba(255,255,255,.5)",
fontSize: 10,
},
// formatter: '{value}%',
},
axisLine: {
lineStyle: {
color: "rgba(255,255,255,.2)",
},
},
splitLine: {
lineStyle: {
color: "rgba(255,255,255,.1)",
},
},
},
series: [
{
data: [],
type: "line",
areaStyle: {},
},
],
};
option && myChart.setOption(option);
window.addEventListener("resize", function () {
myChart.resize();
});
function asyncData_10() {
$.getJSON("json/area_chart.json").done(function (data) {
var myChart = echarts.init(document.getElementById(idContainer_10));
// console.log(JSON.stringify(data));
myChart.setOption(data);
});
}
asyncData_10();
壓力 - 柱狀圖
var idContainer_11 = "container_11";
var chartDom = document.getElementById(idContainer_11);
var myChart = echarts.init(chartDom, window.gTheme);
var option;
option = {
title: {
text: "壓力",
left: "left",
textStyle: {
color: "rgba(11, 240, 125, 0.8)",
fontSize: "10",
},
},
grid: {
left: "3%",
right: "4%",
bottom: "3%",
top: "30%",
containLabel: true,
},
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow",
},
},
xAxis: {
type: "category",
data: [],
axisLabel: {
textStyle: {
color: "rgba(255,255,255,.5)",
fontSize: 10,
},
},
axisLine: {
lineStyle: {
color: "rgba(255,255,255,.2)",
},
},
splitLine: {
lineStyle: {
color: "rgba(255,255,255,.1)",
},
},
},
yAxis: {
type: "value",
min: 0,
max: 200,
axisLabel: {
textStyle: {
color: "rgba(255,255,255,.5)",
fontSize: 10,
},
// formatter: '{value}%',
},
axisLine: {
lineStyle: {
color: "rgba(255,255,255,.2)",
},
},
splitLine: {
lineStyle: {
color: "rgba(255,255,255,.1)",
},
},
},
series: [
{
data: [],
type: "bar",
showBackground: true,
backgroundStyle: {
color: "rgba(180, 180, 180, 0.1)",
},
},
],
};
option && myChart.setOption(option);
myChart.setOption(option);
window.addEventListener("resize", function () {
myChart.resize();
});
function asyncData_11() {
$.getJSON("json/bar_background.json").done(function (data) {
var myChart = echarts.init(document.getElementById(idContainer_11));
myChart.setOption({
xAxis: data["xAxis"],
series: data["series"],
});
});
}
asyncData_11();
3、后端python服務器代碼
# -*- coding:utf-8 -*-
import io
import os
import sys
import urllib
import json
from http.server import HTTPServer, SimpleHTTPRequestHandler, ThreadingHTTPServer
ip = "localhost" # 監聽IP,配置項
port = 8815 # 監聽埠,配置項
index_url = "http://%s:%d/index.html" %(ip, port) # 監聽主頁url,配置項
class MyRequestHandler(SimpleHTTPRequestHandler):
protocol_version = "HTTP/1.0"
server_version = "PSHS/0.1"
sys_version = "Python/3.7.x"
target = "./" # 監聽目錄,配置項
def do_GET(self):
if self.path.find("/json/") > 0:
print(self.path)
req = {"success": "true"}
self.send_response(200)
self.send_header("Content-type", "json")
self.end_headers()
with open(self.path, 'r', encoding="utf-8") as f:
data = json.load(f)
rspstr = json.dumps(data)
self.wfile.write(rspstr.encode("utf-8"))
else:
SimpleHTTPRequestHandler.do_GET(self);
def do_POST(self):
data = self.rfile.read(int(self.headers["content-length"]))
data = json.loads(data)
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
rspstr = "recv ok, data = "
rspstr += json.dumps(data, ensure_ascii=False)
self.wfile.write(rspstr.encode("utf-8"))
def HttpServer():
try:
server = HTTPServer((ip, port), MyRequestHandler)
listen = "http://%s:%d" %(ip, port)
print("服務器監聽地址: ", listen)
server.serve_forever()
except ValueError as e:
print("Exception", e)
server.socket.close()
if __name__ == "__main__":
HttpServer()
四、上線運行

本次分享結束,歡迎討論!QQ微信同號: 6550523
??系列文章??
資料可視化:基于 Echarts + Python 實作的動態實時大屏范例【十】
資料可視化:基于 Echarts + Python 實作的動態實時大屏范例【九】
資料可視化:基于 Echarts + Python 實作的動態實時大屏范例【八】
資料可視化:基于 Echarts + Python 實作的動態實時大屏范例【七】
資料可視化:基于 Echarts + Python 實作的動態實時大屏范例【六】
資料可視化:基于 Echarts + Python 實作的動態實時大屏范例【五】
資料可視化:基于 Echarts + Python 實作的動態實時大屏范例【四】
資料可視化:基于 Echarts + Python 實作的動態實時大屏范例【三】
資料可視化:基于 Echarts + Python 實作的動態實時大屏范例【二】
資料可視化:基于 Echarts + Python 實作的動態實時大屏范例【一】
資料可視化:基于 Pyecharts + PyQT 實作的動態實時拖拉拽大屏范例【一】
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/295184.html
標籤:python
