92 lines
2.3 KiB
Vue
92 lines
2.3 KiB
Vue
<template>
|
|
<canvas
|
|
ref="canvasRef"
|
|
class="h-12 w-full rounded-lg opacity-60"
|
|
/>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
const canvasRef = ref<HTMLCanvasElement | null>(null)
|
|
const store = usePlayerStore()
|
|
const { getAudio } = useAudioPlayer()
|
|
|
|
let audioContext: AudioContext | null = null
|
|
let analyser: AnalyserNode | null = null
|
|
let source: MediaElementAudioSourceNode | null = null
|
|
let animId: number | null = null
|
|
let connected = false
|
|
|
|
function initAnalyser() {
|
|
if (connected || !canvasRef.value) return
|
|
|
|
try {
|
|
const audio = getAudio()
|
|
audioContext = new AudioContext()
|
|
analyser = audioContext.createAnalyser()
|
|
analyser.fftSize = 64
|
|
source = audioContext.createMediaElementSource(audio)
|
|
source.connect(analyser)
|
|
analyser.connect(audioContext.destination)
|
|
connected = true
|
|
}
|
|
catch {
|
|
// Web Audio API might not be available
|
|
}
|
|
}
|
|
|
|
function draw() {
|
|
if (!canvasRef.value || !analyser) {
|
|
animId = requestAnimationFrame(draw)
|
|
return
|
|
}
|
|
|
|
const canvas = canvasRef.value
|
|
const ctx = canvas.getContext('2d')
|
|
if (!ctx) return
|
|
|
|
const bufferLength = analyser.frequencyBinCount
|
|
const dataArray = new Uint8Array(bufferLength)
|
|
analyser.getByteFrequencyData(dataArray)
|
|
|
|
canvas.width = canvas.offsetWidth * window.devicePixelRatio
|
|
canvas.height = canvas.offsetHeight * window.devicePixelRatio
|
|
ctx.scale(window.devicePixelRatio, window.devicePixelRatio)
|
|
|
|
const width = canvas.offsetWidth
|
|
const height = canvas.offsetHeight
|
|
|
|
ctx.clearRect(0, 0, width, height)
|
|
|
|
const barWidth = width / bufferLength
|
|
const gap = 2
|
|
|
|
for (let i = 0; i < bufferLength; i++) {
|
|
const barHeight = (dataArray[i] / 255) * height
|
|
const x = i * (barWidth + gap)
|
|
|
|
const gradient = ctx.createLinearGradient(0, height, 0, height - barHeight)
|
|
gradient.addColorStop(0, 'hsl(12, 76%, 48%)')
|
|
gradient.addColorStop(1, 'hsl(36, 80%, 52%)')
|
|
|
|
ctx.fillStyle = gradient
|
|
ctx.fillRect(x, height - barHeight, barWidth, barHeight)
|
|
}
|
|
|
|
animId = requestAnimationFrame(draw)
|
|
}
|
|
|
|
watch(() => store.isPlaying, (playing) => {
|
|
if (playing) {
|
|
initAnalyser()
|
|
if (!animId) draw()
|
|
if (audioContext?.state === 'suspended') {
|
|
audioContext.resume()
|
|
}
|
|
}
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
if (animId) cancelAnimationFrame(animId)
|
|
})
|
|
</script>
|