This commit is contained in:
2026-09-10 15:30:01 +07:00
parent 2437e044b0
commit 96926a0ae3
40 changed files with 38 additions and 93 deletions
-55
View File
@@ -31,12 +31,7 @@ const binding = ref(false)
const bindSuccess = ref(false)
const introAudioElement = ref<HTMLAudioElement>()
const conclusionVideoElement = ref<HTMLVideoElement>()
const visualizerCanvas = ref<HTMLCanvasElement>()
let appliedCommandVersion = 0
let audioContext: AudioContext | undefined
let analyser: AnalyserNode | undefined
let audioSource: MediaElementAudioSourceNode | undefined
let visualizerFrame = 0
let pollTimer: number | null = null
const bindingFlow = computed(() => Boolean(route.query.screenId && route.query.accessToken))
@@ -134,43 +129,10 @@ async function loadGames() {
}
}
function startVisualizer() {
const audio = introAudioElement.value
const canvas = visualizerCanvas.value
if (!audio || !canvas || audioSource) return
try {
audioContext = new AudioContext()
analyser = audioContext.createAnalyser()
analyser.fftSize = 64
audioSource = audioContext.createMediaElementSource(audio)
audioSource.connect(analyser)
analyser.connect(audioContext.destination)
const data = new Uint8Array(analyser.frequencyBinCount)
const draw = () => {
if (!analyser || !visualizerCanvas.value) return
const context = visualizerCanvas.value.getContext('2d')
if (!context) return
analyser.getByteFrequencyData(data)
context.clearRect(0, 0, canvas.width, canvas.height)
const barWidth = canvas.width / data.length
data.forEach((value, index) => {
const height = (value / 255) * canvas.height
context.fillStyle = `hsl(${160 + index * 3} 70% 60%)`
context.fillRect(index * barWidth, canvas.height - height, Math.max(2, barWidth - 1), height)
})
visualizerFrame = requestAnimationFrame(draw)
}
draw()
} catch {
// Браузер может запретить анализ MediaElement, воспроизведение при этом остаётся доступным.
}
}
watch(state, async (value) => {
if (!value || !value.command || value.stateVersion === appliedCommandVersion) return
appliedCommandVersion = value.stateVersion || 0
await nextTick()
startVisualizer()
const media = screenMode.value === 'SCREEN_MODE_CONCLUSION' ? conclusionVideoElement.value : introAudioElement.value
if (!media) return
media.currentTime = (value.positionMs || 0) / 1000
@@ -239,8 +201,6 @@ onMounted(async () => {
onUnmounted(() => {
if (pollTimer != null) window.clearInterval(pollTimer)
if (resultsScrollTimer != null) window.clearInterval(resultsScrollTimer)
cancelAnimationFrame(visualizerFrame)
void audioContext?.close()
})
</script>
@@ -276,9 +236,6 @@ onUnmounted(() => {
<div v-else class="screen-content" :style="screenBackground">
<p v-if="mediaError" class="media-error">{{ mediaError }}</p>
<div v-if="screenMode === 'SCREEN_MODE_INTRO' && state.introAudio" class="screen-media">
<div class="sound-track">
<canvas ref="visualizerCanvas" width="640" height="120" />
</div>
<audio ref="introAudioElement" :src="introAudio" :muted="!state.soundEnabled" autoplay controls />
</div>
<div v-else-if="screenMode === 'SCREEN_MODE_CONCLUSION' && state.conclusionVideo" class="screen-media">
@@ -460,17 +417,5 @@ onUnmounted(() => {
text-align: left;
}
.sound-track {
margin: 0 auto 20px;
max-width: 720px;
}
.sound-track canvas {
background: #161616;
display: block;
height: 120px;
max-width: 100%;
width: 640px;
}
</style>