import {AfterViewInit, Component, effect, ElementRef, inject, ViewChild} from '@angular/core'; import {AudioService} from '../../services/audio.service'; @Component({ selector: 'analyser', templateUrl: 'analyser.html', }) export class Analyser implements AfterViewInit { public readonly height = 150; public readonly bottomOffset = 50; public readonly offsetBuffer = 20; public color = "rgb(80,0,80)" protected audio: AudioService = inject(AudioService); @ViewChild('analyserCanvas') canvasRef!: ElementRef; protected canvas!: HTMLCanvasElement; constructor() { effect(() => { if (this.audio.analyser.isRunning()) { requestAnimationFrame(this.draw.bind(this)); } }) } ngAfterViewInit(): void { this.canvas = this.canvasRef.nativeElement as HTMLCanvasElement; this.canvas.style.height = `${this.height}px`; this.canvas.style.top = `-${this.height}px`; document.onmousemove = (e: MouseEvent) => { const cursorDistancefromBottom = document.documentElement.clientHeight - (e.clientY || 0); let offset = cursorDistancefromBottom - (this.bottomOffset + this.offsetBuffer); if (cursorDistancefromBottom < this.bottomOffset || cursorDistancefromBottom > (this.bottomOffset + this.height + this.offsetBuffer)) { offset = this.height; } this.canvas.style.height = `${offset}px`; this.canvas.style.top = `-${offset}px`; } } draw() { if (!this.audio.analyser.isRunning() || !this.canvas ) return; const canvas = this.canvas; const ctx = canvas.getContext('2d'); const data = this.audio.analyser.analyserData; if (!ctx) return; ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.lineWidth = 0; ctx.fillStyle = this.color; const sliceWidth = Math.ceil(canvas.width / data.length); let x = 0; for (let i = 0; i < data.length; i++) { const v = data[i] / 128.0; const y = (v * canvas.height) / 2; ctx.fillRect(x, canvas.height, sliceWidth, -y); x += sliceWidth; } requestAnimationFrame(this.draw.bind(this)); } }