diff options
Diffstat (limited to 'src/relabi/canvas.js')
| -rw-r--r-- | src/relabi/canvas.js | 155 |
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; |
