initiation librodrome
This commit is contained in:
91
app/components/player/PlayerVisualizer.vue
Normal file
91
app/components/player/PlayerVisualizer.vue
Normal file
@@ -0,0 +1,91 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user