最近项目中遇到实现ECG心电图效果,此示例只是模拟效果,还不能对接真实数据。
展现的效果图
本示例使用 ES6
的 class
语法糖,结合 canvas
画布标签实现。
class ECGCanvas {}
- 首先创建类名
ECGCanvas
构造函数
constructor(options={}){
this.element = options.element || 'canvas'; // canvas元素id
this.canvas = document.getElementById(this.element); // canvas对象
this.context = this.canvas.getContext("2d"); // canvas上下文
this.context.width = options.width || 250; // canvas宽
this.context.height = options.height || 100; // canvas高
this.context.lineWidth = options.lineWidth || 1; // canvas 绘制线粗细
this.context.strokeStyle = options.strokeStyle || '#396'; // canvas 绘制线颜色
this.speed = options.speed || 20; // 绘制速度
this.point = { x : 0, y : this.context.height / 2 }; // 绘制当前坐标位置
this.counter = 0; // 计数,为累加用
this.distance = 10; // 波动间隔
this.interval = null; // 定时器
}
接着实现 ECGCanvas
类的构造函数,对应属性已给出注释,下面几个说明一下
point
是要画线的坐标位置,{ x : 0 }
表示横向从画布的最左端开始,{ y : this.context.height / 2 }
是纵向从画布的最中间开始。counter
绘制线时做累加用distance
波动间隔,数值越大,波动越不明显interval
做定时器用,绘制动画用setInterval
实现。
绘制方法draw()
draw(){
let x = (this.counter += 0.1) * this.distance;
let y = this.context.height / 2 - Math.tan((Math.cos(this.counter) * Math.random() * 2));
this.context.beginPath();
this.context.moveTo(this.point.x, this.point.y)
this.context.lineTo(x, y);
this.context.closePath();
this.context.stroke();
this.point = { x, y };
if( this.point.x >= this.context.width ){
this.reset();
}
}
实现 ECGCanvas
类的绘制线的 draw()
方法
x
算法是counter
累加0.1的结果再与distance
求乘积。y
的算法是模拟绘制图线上下波动的关键。随机数Math.random
生成0到1之间的小数,再与Math.cos
余弦函数的乘积使其波动的概率减小。与context.height / 2
取差值是为了绘制点的纵向坐标始终在纵向的中心上下波动。正切函数Math.tan
在某一个间断区间内是递增函数,它的值小概率会接近正负∞,也就是绘制曲线时波动会小概率出现陡增陡降的情况。
reset()
方法
reset(){
clearInterval(this.interval);
this.counter = 0;
this.point = { x : 0, y : this.context.height / 2 };
}
- 停止计时器
- 重置
counter
和point
clear()
方法
clear(){
this.context.clearRect(0, 0, this.context.width, this.context.height);
}
context.clearRect
清空画布
最后写一个 start()
方法
start(){
this.reset();
this.clear();
clearInterval(this.interval);
this.interval = setInterval(this.draw.bind(this),this.speed);
}
- 注意
setInterval
中的this.draw.bind(this)
。 (this
本身的坑,不然现在都推崇hooks
)
现在可以开始测试了
<canvas id="canvas" width="250" height="100"></canvas>
<button onclick="onDraw()">绘制</button>
- 准备一个
canvas
标签,和一个触发绘制功能的按钮。
let ecg = new ECGCanvas();
function onDraw(){
ecg.start();
}
- 实例化对象,并调用
start
方法绘制心电图。