Html5 canvas 绘制心电波形图
2020-12-16 本文已影响0人
lesliefang


仅供参考
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<style>
.boack {
position: absolute;
left: 0px;
top: 10px;
width: 800px;
height: 300px;
}
</style>
</head>
<body>
<div class="boack">
<canvas id="background" width="800" height="300" style="margin-left: 20px;">
</canvas>
</div>
<div class="boack">
<canvas id="line" width="800" height="300" style="margin-left: 20px;">
</canvas>
</div>
<script>
var canvas = document.getElementById("background");
var width = canvas.width;
var height = canvas.height;
var ctx = canvas.getContext("2d");
var grid_width = 5; // 每一个小网格的宽高
var hLineNum = parseInt(height / grid_width); // 横线个数
var vLineNum = parseInt(width / grid_width); // 竖线个数
var x_start = grid_width * 5; // X起点,从第二个大格子开始画
var lineCanvas = document.getElementById("line");
var lineCtx = lineCanvas.getContext("2d");
lineCtx.lineWidth = 2;
lineCtx.strokeStyle = "#0f0";
console.log(hLineNum);
console.log(vLineNum);
function drawGrid() {
ctx.beginPath();
ctx.lineWidth = 1;
ctx.strokeStyle = "#ffbebe";
for (let i = 0; i < hLineNum; i++) {
if (i == 0 || i % 5 != 0) {
ctx.moveTo(0, i * grid_width);
ctx.lineTo(width, i * grid_width);
ctx.stroke();
}
}
ctx.beginPath();
ctx.lineWidth = 1;
ctx.strokeStyle = "#ffbebe";
for (let i = 0; i < vLineNum; i++) {
if (i == 0 || i % 5 != 0) {
ctx.moveTo(i * grid_width, 0);
ctx.lineTo(i * grid_width, height);
ctx.stroke();
}
}
ctx.beginPath();
ctx.lineWidth = 1;
ctx.strokeStyle = "#FF7F50";
for (let i = 5; i <= vLineNum; i += 5) {
ctx.moveTo(i * grid_width, 0);
ctx.lineTo(i * grid_width, height);
ctx.stroke();
}
ctx.beginPath();
ctx.lineWidth = 1;
ctx.strokeStyle = "#FF7F50";
for (let i = 5; i <= hLineNum; i += 5) {
ctx.moveTo(0, i * grid_width);
ctx.lineTo(width, i * grid_width);
ctx.stroke();
}
}
function addfilltext() {
var waves = ["I", "RESP", "PLETH"];
ctx.font = "bold 14px";
ctx.fillText("" + waves[0] + "", 5, 50)
ctx.fillText("" + waves[1] + "", 5, 150)
ctx.fillText("" + waves[2] + "", 5, 250)
}
class WaveView {
currentX = 0;
currentY = 0;
lastX = 0;
lastY = 0;
// 每次画几个点
step = 10;
// Y值最大值
yMax = 300;
// 每个波形的高度
itemHeight = 100;
// 橡皮檫宽度
clearGap = 20;
y_offset = 0;
// 队列
queue = [];
strokeStyle = "#0f0";
/**
* @param frameSize 1秒多少个点
* @param yMax
* @param y_offset y偏移
* @param step 每次画几个点
* @param speedRatio 扫纸速度,默认 25mm/s (1秒25个小格子 每个小格子0.04s)。 0.5表示扫纸速度为 12.5mm/s。2表示扫纸速度为 50mm/s。
* @param strokeStyle 线的样式
*/
constructor(frameSize, yMax, y_offset, step, speedRatio, strokeStyle) {
this.frameSize = frameSize;
this.yMax = yMax;
this.lastY = this.itemHeight / 2;
this.y_offset = y_offset;
this.step = step;
this.speedRatio = speedRatio;
this.strokeStyle = strokeStyle;
this.drawInterval = Math.floor((1 / this.frameSize) * 1000 * this.step); // 绘制时间间隔
}
draw = () => {
lineCtx.beginPath();
lineCtx.strokeStyle = this.strokeStyle;
if (this.lastX === 0) {
lineCtx.clearRect(x_start - 2, this.y_offset, this.clearGap, this.itemHeight);
} else {
lineCtx.clearRect(x_start + this.lastX, this.y_offset, this.clearGap, this.itemHeight);
}
for (let i = 0; i < this.step; i++) {
if (this.queue.length === 0) {
this.currentY = this.itemHeight / 2;
} else {
this.currentY = (-1.0 * this.queue.shift()) / this.yMax * this.itemHeight + this.itemHeight;
}
if (this.currentY > this.itemHeight) {
this.currentY = this.itemHeight;
}
lineCtx.moveTo(x_start + this.lastX, this.y_offset + this.lastY);
lineCtx.lineTo(x_start + this.currentX, this.y_offset + this.currentY);
this.lastX = this.currentX;
this.lastY = this.currentY;
this.currentX += (grid_width * 25 * this.speedRatio) / this.frameSize;
if (x_start + this.currentX >= width - 25) {
this.currentX = 0;
this.lastX = 0;
}
}
lineCtx.stroke();
}
loop = () => {
this.draw();
setTimeout(this.loop, this.drawInterval);
// TODO for test only 添加测试数据,实际中会从后台取
this.addData(base64ToUint8Array('enp6enp6enp6enp6enp6enp7fX+ChYeJiouMjIuKiIaCf318e3p6enp6enp6enp6enp6enp6enp5d3Rxb4SXq77S5dK+q5eEcHJ1eHp6enp6enp6enp6enp6enp6enp6enp6enp6enp6ent8fH6Ag4WGiIyPkJKVlpiZmZqbnJ2cm5mZmJaVkpGOioeFgX98e3p6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6eg=='));
}
addData = (arr) => {
for (let i = 0; i < arr.length; i++) {
this.queue.push(arr[i]);
}
}
}
// function sleep(t) {
// let now = new Date().getTime();
// for (let i = 0; i < 10000000; i++) {
// if ((new Date().getTime() - now) > t) {
// break
// }
// }
// }
function base64ToUint8Array(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, '+')
.replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
let ecgWave = new WaveView(250, 250, 0, 10, 1, '#00ff00');
let respWave = new WaveView(125, 250, 100, 10, 0.5, '#00ff00');
let spo2Wave = new WaveView(60, 250, 200, 4, 1, '#00F5FF');
drawGrid();
addfilltext();
ecgWave.loop();
respWave.loop();
spo2Wave.loop();
</script>
</body>
</html>