type IWaveformProps = { peak: number; fillStyle: string; progressFillStyle: string; }; const SPIKE_WIDTH = 2; const SPIKE_STEP = 4; const SPIKE_RADIUS = 1; const HEIGHT = 23; export function renderWaveformToDataUri( spikes: number[], progress: number, { peak, fillStyle, progressFillStyle, }: IWaveformProps, ) { const width = spikes.length * SPIKE_STEP; const height = HEIGHT; const canvas = document.createElement('canvas'); canvas.width = width * 2; canvas.height = height * 2; canvas.style.width = `${width}px`; canvas.style.height = `${height}px`; const ctx = canvas.getContext('2d')!; ctx.scale(2, 2); spikes.forEach((item, i) => { ctx.globalAlpha = (i / spikes.length >= progress) ? 0.5 : 1; ctx.fillStyle = progress > i / spikes.length ? progressFillStyle : fillStyle; const spikeHeight = Math.max(2, HEIGHT * (item / Math.max(1, peak))); roundedRectangle(ctx, i * SPIKE_STEP, height, SPIKE_WIDTH, spikeHeight, SPIKE_RADIUS); ctx.fill(); }); return { src: canvas.toDataURL(), width, height, }; } function roundedRectangle( ctx: CanvasRenderingContext2D, x: number, y: number, width: number, height: number, radius: number, ) { if (width < 2 * radius) { radius = width / 2; } if (height < 2 * radius) { radius = height / 2; } ctx.beginPath(); ctx.moveTo(x + radius, y); ctx.arcTo(x + width, y, x + width, y - height, radius); ctx.arcTo(x + width, y - height, x, y - height, radius); ctx.arcTo(x, y - height, x, y, radius); ctx.arcTo(x, y, x + width, y, radius); ctx.closePath(); }