This is the 13th day of my participation in the August More Text Challenge.More challenges in August

preface

Love loves the infinite and fears the limit.

— Kierkegaard

introduce

This issue is about drawing an electrocardiogram animation, who said that code non does not know romance, just a special way of expression.

This is basically the above, I will be divided into basic structure, heart fluctuation, heart-shaped generation, to explain this case, ready to finish sharing to who, then go ~

Set out

1. Infrastructure

<style>
    * {
        padding: 0;
        margin: 0; {} *padding: 0;
        margin: 0;
    }

    html.body {
        width: 100%;
        height: 100vh;
        position: relative;
        background-color: # 000;
    }
    #canvas {
        width: 100%;
        height: 100%;
    }
</style>

<body>
    <canvas id="canvas"></canvas>
    <script type="module" src="./app.js"></script>
</body>
Copy the code

Basic HTML and CSS do not repeat, is to do a full screen black cloth.

/*app.js*/
class Application {
  constructor() {
    this.canvas = null;      / / the canvas
    this.ctx = null;         / / environment
    this.w = 0;              / / canvas width
    this.h = 0;              / / the canvas
    this.speed = 5;          // Draw speed
    this.lineData = [];      // Draw data
    this.maxHeight = 50;     // Range of fluctuation
    this.active = 0;         // Active state
    this.heartData = [];     // Heart data
    this.heartR = 7;         // Heart-shaped radius
    this.dt = 0;             / / cycle value
    this.x = 0;              // Current x coordinates
    this.y = 0;              // The current y coordinate
    this.startX = 0;         // Draw the x coordinates of the heart
    this.startY = 0;         // Draw the initial y coordinates of the heart
    this.lineColor = "Rgba (218,40,0,1)";        // Line segment color
    this.shadowColor = "Rgba (255255255, 5)";  / / projection color
    this.centerY = 0;        // the y axis is fixed
    this.init();
  }
  init() {
    / / initialization
    this.canvas = document.getElementById("canvas");
    this.ctx = this.canvas.getContext("2d");
    window.addEventListener("resize".this.reset.bind(this));
    this.render();
  }
  reset() {
    // Screen changes
    this.w = this.canvas.width = this.ctx.width = window.innerWidth;
    this.h = this.canvas.height = this.ctx.height = window.innerHeight;
    this.centerY = this.h / 2 + this.heartR*Math.PI*2;
    this.y = this.centerY;
    this.clear();
  }
  clear(){
      / / to empty
  }
  render() {
    / / the main rendering
    this.reset();
    this.step();
  }
  getHeart() {
      // Get the heart shape
  }
  drawTopLine() {
      // Draw white lines
  }
  drawLine() {
      // Draw a red line
  }
  step() {
    / / redraw
    requestAnimationFrame(this.step.bind(this));
    if (this.dt % 2= =0) {
      this.drawLine();
      this.drawTopLine();
    }
    if (this.x > this.w + this.speed) {
      this.clear()
    }
    this.dt++
  }
}

window.onload = new Application();
Copy the code

After looking at the structure, let’s talk about our expectations:

  1. We will first draw a white heartbeat line, which is randomly generated and has peak and valley values. After drawing a segment, its coordinates will be stored in lineData.
  2. Then draw a red heartbeat line, which is not randomly generated, but drawn according to the data in lineData, so that the white line is drawn first, followed by the red line, which is the basic animation of electrocardiogram.
  3. Heart we want to make a perfect heart (before Tanabata has a heart drawing chestnut), get the heart data, in the white line drawing cycle to find a place to plug into the execution, it is done.
  4. After drawing the heartbeat, we empty it, return it to its original state, and repeat the animation from the beginning.

2. Heart swings

drawTopLine() {
    / / white line
    const { ctx, w, h, x, y, shadowColor, maxHeight, lineData, speed, active,centerY } = this;
    lineData.unshift({ x, y })
    let x1 = x + Math.random() * speed + speed;
    let y1 = centerY;
    if (x1 > w * 0.05 && x1 < w * 0.95) {
        if (Math.random() > 0.8 && active == 0) {
            y1 += Math.random() * maxHeight * 2 - maxHeight
        }
    }
    ctx.lineWidth = 3;
    ctx.strokeStyle = "Rgba (255255255, 5)";
    ctx.lineJoin = "round";
    ctx.lineCap = "round";
    ctx.shadowBlur = 20;
    ctx.shadowColor = shadowColor;
    ctx.beginPath();
    ctx.moveTo(x, y);
    ctx.lineTo(x1, y1);
    ctx.stroke();
    ctx.closePath();
    this.x = x1;
    this.y = y1;
}
drawLine() {
    / / the red line
    const { ctx,shadowColor, lineColor, maxHeight, lineData } = this;
    if (lineData.length < 2) return;
    ctx.lineWidth = 3;
    ctx.strokeStyle = lineColor;
    ctx.lineJoin = "round";
    ctx.lineCap = "round";
    ctx.shadowBlur = 20;
    ctx.shadowColor = shadowColor;
    ctx.beginPath();
    ctx.moveTo(lineData[1].x, lineData[1].y);
    ctx.lineTo(lineData[0].x, lineData[0].y);
    ctx.stroke();
    ctx.closePath();
}
Copy the code

To draw a line segment, we will increase a value in the X-axis direction every other period, and the Y-axis is fixed. However, when the Y-axis appears at a random moment, a random variable will be added to change it. However, the next drawing of the Y-axis will still be based on the fixed value. Save the drawing record for each drawing, and use it for the red line. It is worth noting that since there are two coordinate points to draw a line segment, the red line should be drawn more than two records before starting to draw.

Now that we have the basic ECG, we are ready to draw the heart.

3. Create a heart

getHeart() {
    let t = Math.PI + 0.5;
    let maxt = 2 * Math.PI - 1;
    let vt = this.speed/100;
    let x = 0;
    let y = 0;
    let r = this.heartR;
    for (let i = 0; i < Math.ceil(maxt / vt); i++) {
        x = 16 * Math.pow(Math.sin(t), 3);
        y = 13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t);
        t += vt;
        x *= r;
        y = -y * r - r * Math.PI * 4;
        if (y < 0) {
            this.heartData.push({ x, y }); }}}Copy the code

We’re going to make the heart value not a variation so we’re going to calculate it and save it for later drawing.

drawTopLine() {
    / / white line
    // ...
    if (x1 > w * 0.05 && x1 < w * 0.95) {
      if (Math.random() > 0.8 && active == 0) {
        y1 += Math.random() * maxHeight * 2 - maxHeight
      }
      if (x1 > w * 0.25 && this.active == 0) {
        this.active = 1;
      }
      if (x1 > w * 0.38 && this.active == 1) {
        this.active = 2;
        this.startX = x1 + speed * 3;
        this.startY = centerY;
      }
      if (this.heartData.length > 0 && this.active == 2) {
        let _pos = this.heartData.shift();
        x1 = this.startX + _pos.x;
        y1 = this.startY + _pos.y;
        if (y1 > this.startY) {
          y1 = this.startY; }}if (x1 > 0.55 * w && this.heartData.length == 0 && this.active == 2) {
        this.active = 0; }}// ...
  }
Copy the code

We must draw in the white line, control when he draws, how much to draw back, belongs to your own needs logic, here is not too verbose. The only thing to be said is to save the starting point and draw hearts based on this coordinate, rather than changing values. Draw a horizontal line before and after so that peaks and valleys are not too close to the heart.

4. Redraw

clear(){
    this.heartData.length = this.lineData.length = 0;
    this.active = 0;
    this.x = 0;
    this.getHeart();
    this.ctx.clearRect(0.0.this.w, this.h);
}
Copy the code

We’ll eventually have to redraw it after the screen has changed or is completely drawn. So we’re going to clear out the canvas, clear out the data, state, go to 0. So you can generate it over and over again.


So that’s it, that’s easy, online demo

Extension & Extension

In fact, we are not perfect, there are a lot of things to deal with, for example, it is not good to judge the existence of a lot of logic and white lines, if there is a script, too much logic will explode, and some boundary limits are not done. Of course it is necessary to deal with this if you want to extend the effects. You can not only draw hearts, you can generate dot matrix text to connect them, and a lot of creative things can be done on this ecg.


I hope we didn’t leave as empty as we came.