HTML5——Server-Sent Events:基于Node.js的动态图形报表

Day11

今日份内容:使用Server-Sent Events制作基于Node.js的动态图形报表

Node.js是一种基于事件驱动、非阻塞时I/O模型,具有轻量、高效的特点,目前广泛得到Web前端开发的使用。在使用Node.js前,需要去官网下载安装包,过程比较简单,网上也有非常多的教程,这里就不多说啦。

Server-Sent Events

  • 了解Server-Sent Events消息推送机制
  • 熟悉MessageEvent、EventSource接口
  • 通过服务端向客户端推送消息
  • 在客户端使用SSE技术接收数据
  • 使用jQuery自定义图形插件

内容

  1. 配置Node.js环境
  2. 项目初始化
  3. 安装Express框架
  4. 创建服务端路由文件
  5. 客户端实现

代码

// 该文件作为服务端的路由文件,在该文件中完成服务端路由功能
var express = require('express');
var app = express();
//配置静态资源的访问方式
app.use(express.static('public'));
app.get('/', function(req, res) {
	res.type('text/html');
	//推荐使用sendFile(),该方法需要使用文件的绝对路径
	res.sendFile(__dirname + '/public/chartReport.html');
});

var clientId = 0;
var clients = {};
//记录用户连接请求
app.get('/events/', function(req, res) {
	req.socket.setTimeout(Number.MAX_VALUE);
	res.writeHead(200, {
		'Content-type': 'text/event-stream',
		'Cache-Control': 'no-cache',
		'Connection': 'keep-alive'
	});
	res.write('\n');
	(function(clientId) {
		//将连接保存到集合中
		clients[clientId] = res;
		//当连接关闭后,从连接集合中移除
		req.on("close", function() {
			delete clients[clientId]
		});
	})(++clientId);
});
//对所有客户端发送数据
setInterval(function() {
	for (clientId in clients) {
		clients[clientId].write('data:' + createRandomData() + '\n\n');
	};
}, 500);
//生成随机数据,并以JSON字符串形式返回
function createRandomData() {
	let drawData = [];
	let items = ['女装', '男装', '童装', '运动', '内衣'];
	for (let i = 0; i < 5; i++) {
		let random = parseInt(Math.random() * 100);
		let item = {
			'name': items[i],
			'amount': random
		};
		drawData.push(item);
	}
	return JSON.stringify({
		'drawData': drawData
	});
}
app.listen(process.env.PORT || 3000);

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>图形报表统计</title>
		<script type="text/javascript" src="js/jquery-1.x.js"></script>
		<script src="js/jquert.chart.js" type="text/javascript" charset="utf-8"></script>
		<link rel="stylesheet" type="text/css" href="css/layout.css" />
	</head>
	<body>
		<div class="place"><span>位置:</span>
			<ul class="placeul">
				<li><a href="#">首页</a></li>
				<li><a href="#">报表统计</a></li>
			</ul>
		</div>
		<div class="canvasbody">
			<div class="usual">
				<canvas id="myCanvas1" width="300" height="300"
					style="border: 1px solid #ccc;margin-right: 10px;"></canvas>
				<canvas id="myCanvas2" width="300" height="300"
					style="border: 1px solid #ccc;margin-right: 10px;"></canvas>
				<canvas id="myCanvas3" width="300" height="300" style="border: 1px solid #ccc;"></canvas>
			</div>
		</div>
		<script type="text/javascript">
			//创建EventSource对象
			var dataEventSource = new EventSource("/events/");
			//设置onmessage事件处理方法,当接收到服务器发送的数据时调用该事件
			dataEventSource.onmessage = function(event) {
				//获得服务端返回的销售数据
				var data = JSON.parse(event.data);
				//更新图形报表
				updateChart(data);
			};
			//设置onerror事件处理方法,当发生错误时调用该事件
			dataEventSource.onerror = function(event) {
				dataEventSource.close();
				console.log(event);
			}
			//使用指定的数据更新图形报表
			function updateChart(data) {
				//预设图形报表中各部分颜色
				var options = {
					bgColor: [{
							drawColor: "#9cc507"
						},
						{
							drawColor: "#8b86ca"
						},
						{
							drawColor: "#ff4400"
						},
						{
							drawColor: "#ffb81d"
						},
						{
							drawColor: "#00b3e3"
						}
					],
					frontColor: {
						font: "16px microsoft",
						color: "black"
					}
				};
				//绘制扇形图
				$("#myCanvas1").drawChart(data, options, "PieChart");
				//绘制柱状图
				$("#myCanvas2").drawChart(data, options, "Columnar");
				//绘制折线图
				$("#myCanvas3").drawChart(data, options, "FoldLine");
			}
		</script>
	</body>
</html>

// 自定义表格插件
// @author jCuckoo
;
(function($, window, document, undefined) {
	var defaults = {
		bgColor: [{
				drawColor: "red"
			},
			{
				drawColor: "green"
			},
			{
				drawColor: "yellow"
			},
			{
				drawColor: "blue"
			},
			{
				drawColor: "gray"
			}
		],
		frontColor: {
			font: "12px 宋体",
			color: "black"
		}
	};
	//构造方法
	function DataDrawer(element, data, options) {
		this.$element = element;
		this.drawType = data.drawType; //绘制类型
		this.drawData = data.drawData; //绘制数据
		this.setting = $.extend({}, defaults, options);
	};
	//添加属性或方法
	DataDrawer.prototype = {
		//绘制圆饼图
		drawPieChart: function() {
			var startPoint = 1.5 * Math.PI; //开始位置
			var endPoint = 0;
			var context = this.$element.get(0).getContext("2d");
			this.clearCanvas(context);
			for (var i = 0; i < this.drawData.length; i++) {
				context.fillStyle = this.setting.bgColor[i].drawColor;
				context.strokeStyle = this.setting.bgColor[i].drawColor;
				//开始创建路径
				context.beginPath();
				//开始创建路径(圆心)
				context.moveTo(150, 150);
				//计算弧形结束位置的角度
				endPoint = startPoint - Math.PI * 2 * (
					this.drawData[i].amount / this.allData);
				//开始创建路径 (弧形圆心)
				context.arc(150, 150, 90, startPoint, endPoint, true);
				context.fill();
				context.stroke();
				//保存状态
				context.save();
				//计算文本角度
				var textAngle = (startPoint + endPoint) / 2;
				//每部分所占比重
				var textScale = this.drawData[i].amount / this.allData;
				//将坐标原点移动到绘制文本处(根据圆心进行计算)
				context.translate(150 + 110 * Math.cos(textAngle),
					150 + 110 * Math.sin(textAngle));
				//旋转文本
				context.rotate(textAngle + Math.PI * 1 / 2);
				context.fillStyle = this.setting.frontColor.color;
				context.font = this.setting.frontColor.font;
				context.fillText(this.drawData[i].name, -20, 0);
				//恢复到保存点
				context.restore();
				startPoint -= Math.PI * 2 * (this.drawData[i].amount / this.allData);
			}

		},
		drawColumnar: function() { //绘制柱状图标
			var context = this.$element.get(0).getContext("2d");
			this.clearCanvas(context);
			//绘制坐标系
			this.drawCoordinateSystem(context);
			var width = 20;
			var margin = 20;
			for (var i = 0; i < this.drawData.length; i++) {
				context.fillStyle = this.setting.bgColor[i].drawColor;
				context.strokeStyle = this.setting.bgColor[i].drawColor;
				//计算绘制的矩形的左上角的x,y坐标(以绘制的x坐标轴为参考基准)
				var x = 40 + (width + margin) * i;
				var y = 260 - 260 * (this.drawData[i].amount / this.allData);
				//绘制圆柱
				context.fillRect(x, y, width, 260 * (
					this.drawData[i].amount / this.allData));
				//绘制文本内容
				context.fillStyle = this.setting.frontColor.color;
				context.font = this.setting.frontColor.font;
				context.fillText(this.drawData[i].name, x, 280);
			}
		},
		drawFoldLine: function() { //绘制折线
			var padding = 50;
			var context = this.$element.get(0).getContext("2d");
			this.clearCanvas(context);
			//绘制坐标系
			this.drawCoordinateSystem(context);
			context.beginPath();
			context.moveTo(20, 260);
			for (var i = 0; i < this.drawData.length; i++) {
				//计算折线点的坐标
				var x = 40 + padding * i;
				var y = 260 - 260 * (this.drawData[i].amount / this.allData);
				context.setLineDash([5, 5]); //设置绘制线段的样式为虚线
				context.lineTo(x, y);
				context.stroke();
				context.fillStyle = "gray";
				context.fillRect(x, y, 1, 260 * (
					this.drawData[i].amount / this.allData));
				context.fillStyle = this.setting.frontColor.color;
				context.font = this.setting.frontColor.font;
				context.fillText(this.drawData[i].name, x - 10, 280);
			}
		},
		drawCoordinateSystem: function(context) { //绘制坐标系
			context.setLineDash([0, 0]);
			context.beginPath();
			context.moveTo(20, 20);
			context.lineTo(20, 260);
			context.lineTo(260, 260);
			context.strokeStyle = "black";
			context.lineWidth = 2;
			context.stroke();
		},
		countData: function() { //统计数据的总量
			var allData = 0;
			for (var i = 0; i < this.drawData.length; i++) {
				allData += this.drawData[i].amount;
			}
			this.allData = allData;
		},
		clearCanvas: function(context) {
			var canvas = context.canvas;
			context.fillStyle = "white";
			context.fillRect(0, 0, canvas.width, canvas.height);
		}
	};
	//在插件中使用DataList对象
	$.fn.drawChart = function(data, options, drawType) {
		//创建DataList的实体
		var dataDrawer = new DataDrawer(this, data, options);
		//调用其方法
		dataDrawer.countData();
		if ("PieChart" == drawType) {
			return dataDrawer.drawPieChart();
		} else if ("Columnar" == drawType) {
			return dataDrawer.drawColumnar();
		} else if ("FoldLine" == drawType) {
			return dataDrawer.drawFoldLine();
		}
	}
})(jQuery, window, document);

结果

在这里插入图片描述

步骤详解

  1. 控制台使用npm init命令对项目初始化
    在这里插入图片描述
  2. 设置package.json
    在这里插入图片描述
  3. 控制台使用 npm install express --save-dev 命令来安装Express框架
    在这里插入图片描述
  4. 创建app.js文件作为服务端路由文件,该文件中完成路由功能。
  5. 在根目录下创建public目录,用于存放静态资源,项目整体目录结构如图
    在这里插入图片描述
  6. 在chartReport.html页面中完成客户端的实现
  7. 最后在控制台中使用 node app.js 命令启动Node.js服务器,在浏览器中输入 localhost:3000 网址进入测试。

PS

在编写完使用 node app.js 命令启动Node.js服务器过程中出现了这个错误:
在这里插入图片描述
这个报错也很友好,在上面提示了是标点符号使用错了(系统觉得你用了奇奇怪怪的标点),单引号之间应该使用逗号的,改完之后就可以正常运行啦!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

is_Del

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值