motion-wave
Version:
A library suitable for generating animated waveforms.
83 lines (82 loc) • 3.22 kB
JavaScript
import { animate } from 'from-to.js';
const clamp = (value, min, max) => Math.max(min, Math.min(max, value));
export const createWave = (canvas, config) => {
const waveConfigs = { ...config };
const clear = {
current: null,
};
let distance = 0;
function draw() {
stop();
const { frequency, amplitude, phase = 0, speed = 1, offset = 0, color, } = waveConfigs;
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.moveTo(0, canvas.height / 2);
const offsetY = canvas.height / 2 + offset;
const calcY = (x, offsetY, amplitude, frequency, distance, phase) => offsetY +
amplitude *
Math.sin(((2 * Math.PI * frequency) / 1000) * x + distance / 100 + phase);
const step = clamp(canvas.width / 80 / frequency, 1, canvas.width);
let lastX = 0;
let lastY = calcY(lastX, offsetY, amplitude, frequency, distance, phase);
ctx.moveTo(lastX, lastY);
for (let x = step; x < canvas.width; x += step) {
const cp1X = lastX + (x - lastX) / 3;
const cp1Y = calcY(cp1X, offsetY, amplitude, frequency, distance, phase);
const cp2X = x + (x - lastX) / 3;
const cp2Y = calcY(cp2X, offsetY, amplitude, frequency, distance, phase);
ctx.bezierCurveTo(cp1X, cp1Y, cp2X, cp2Y, x, calcY(x, offsetY, amplitude, frequency, distance, phase));
lastX = x;
lastY = calcY(x, offsetY, amplitude, frequency, distance, phase);
}
const nextX = canvas.width;
const nextY = calcY(nextX, offsetY, amplitude, frequency, distance, phase);
const cp1X = lastX + (nextX - lastX) / 3;
const cp1Y = calcY(cp1X, offsetY, amplitude, frequency, distance, phase);
const cp2X = nextX - (nextX - lastX) / 3;
const cp2Y = calcY(cp2X, offsetY, amplitude, frequency, distance, phase);
ctx.bezierCurveTo(cp1X, cp1Y, cp2X, cp2Y, nextX, nextY);
ctx.lineTo(canvas.width, canvas.height);
ctx.lineTo(0, canvas.height);
ctx.closePath();
ctx.fillStyle = color ?? getComputedStyle(canvas).fill;
ctx.fill();
distance += speed;
const handle = requestAnimationFrame(draw);
clear.current = () => cancelAnimationFrame(handle);
}
function stop() {
clear.current?.();
}
function setConfig(config) {
Object.assign(waveConfigs, config);
}
function motionTo(key, value, options) {
const currentValue = waveConfigs[key] ?? value;
return animate(currentValue, value, {
...options,
onUpdate(latest) {
setConfig({ [key]: latest });
options.onUpdate?.(latest);
},
});
}
function reset() {
Object.assign(waveConfigs, config);
distance = 0;
}
function resize(width, height) {
canvas.width = width;
canvas.height = height;
}
return {
start: () => draw(),
setConfig,
motionTo,
stop,
reset,
resize,
currentConfig: waveConfigs,
};
};