summaryrefslogtreecommitdiff
path: root/src/relabi/canvas.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/relabi/canvas.js')
-rw-r--r--src/relabi/canvas.js155
1 files changed, 155 insertions, 0 deletions
diff --git a/src/relabi/canvas.js b/src/relabi/canvas.js
new file mode 100644
index 0000000..dfbffa0
--- /dev/null
+++ b/src/relabi/canvas.js
@@ -0,0 +1,155 @@
+/**
+ * Relabi waveform display
+ * @module src/relabi/canvas.js;
+ */
+
+export default class RelabiCanvas {
+ /**
+ * Initialize relabi wave renderer
+ */
+ constructor({ relabi, parent }) {
+ this.relabi = relabi;
+ this.height = 400;
+ this.lastFrame = 0;
+ this.lastAppendTime = 0;
+ this.lastAppendFrame = 0;
+
+ // Speed of the wave in pixels per second
+ this.speed = 1 / 5;
+
+ // Attach to the DOM
+ this.parent = parent = document.body;
+ this.canvas = document.createElement("canvas");
+ this.ctx = this.canvas.getContext("2d");
+ this.parent.appendChild(this.canvas);
+
+ // Initialize array
+ this.values = new Array(window.innerWidth).fill(0);
+ this.append([]);
+
+ // Clear the canvas
+ this.resize();
+
+ // Start drawing
+ this.requestAnimationFrame(0);
+ }
+
+ /**
+ * Draw the next frame
+ */
+ requestAnimationFrame(frame) {
+ requestAnimationFrame((frame) => {
+ this.requestAnimationFrame(frame);
+ });
+ this.paint(frame);
+ this.lastFrame = frame;
+ }
+
+ /**
+ * Handle window resize
+ */
+ resize() {
+ this.canvas.width = window.innerWidth;
+ this.canvas.height = this.height;
+ }
+
+ /**
+ * Clear the canvas
+ */
+ clear() {
+ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
+ }
+
+ /**
+ * Append values
+ */
+ append(time, values) {
+ this.lastAppendTime = time;
+ this.lastAppendFrame = this.lastFrame;
+ this.values = this.values.concat(values).slice(-this.canvas.width);
+ }
+
+ /**
+ * Paint the canvas
+ */
+ paint(frame) {
+ this.clear();
+ this.drawRelabiWave(frame);
+ this.drawBounds();
+ }
+
+ /**
+ * Draw the bounds
+ */
+ drawBounds() {
+ const { canvas, ctx } = this;
+ const { width, height } = canvas;
+
+ // Draw dashed lines for all bounds
+ for (const bound of this.relabi.bounds) {
+ const y = getWaveHeight(bound.level, height);
+
+ ctx.beginPath();
+ ctx.setLineDash([10, 5]);
+ ctx.strokeStyle = bound.color || "#888";
+ ctx.moveTo(0, y);
+ ctx.lineTo(width, y);
+ ctx.stroke();
+ }
+ }
+
+ /**
+ * Draw the relabi wave
+ */
+ drawRelabiWave(frame) {
+ const { canvas, ctx } = this;
+ const { width, height } = canvas;
+
+ const pointCount = this.values.length;
+ let index = 0;
+
+ // Start the path
+ ctx.beginPath();
+ ctx.strokeStyle = "white";
+ ctx.setLineDash([]);
+
+ // This is the offset in seconds from the last frame we computed
+ const frameOffset = (frame - this.lastAppendFrame) / 1000;
+
+ // Make a path connecting all values
+ for (const point of this.values) {
+ if (!point) {
+ index += 1;
+ continue;
+ }
+
+ const [time, value] = point;
+
+ // This is the offset of this time from current time
+ // If this is in the future, it will be positive.
+ // Subtracting the frame offset pulls it negative.
+ const timeOffset = this.lastAppendTime + frameOffset - time;
+
+ const x = width - timeOffset * this.speed * width - 10;
+
+ if (x < 0) {
+ continue;
+ }
+
+ const y = getWaveHeight(value, height);
+ if (index === 0) {
+ ctx.moveTo(x, y);
+ }
+ ctx.lineTo(x, y);
+ index += 1;
+ }
+
+ // Paint the line
+ ctx.stroke();
+ }
+}
+
+/**
+ * Get the height of the wave at a given value
+ */
+const getWaveHeight = (value, height) => ((value + 1) / 2) * height;