I am trying to make a component in Vue3 to manage audio. This component will display a button that when pressed, plays an audio (just a short word) and changes the color (to show it is playing the audio).
Currently I managed to play the audio, but I have some issues that I am not sure how to resolve, which arise because I don't really understand how the Audio object works in javascript:
.playing
is never applied to the element (isPlaying always returns false!).play()
call should not be executed. But I am not sure canplaythrough
event will work for me, since I just want to load the audio once (and only if the user presses the button).new Audio()
? Try catch or I can do something with promises?this.audio.pause(); this.audio.src = null
?<template>
<button v-on:click="play" :class="{playing: isPlaying}">Audio</button>
</template>
<script>
export default {
name: 'AudioComponent',
data() {
return {
audio: null,
}
},
props: {
url: {type: String, required: true}
},
computed: {
isPlaying(){
if(this.audio !== null){
if(!this.audio.paused && this.audio.duration > 0){
return true;
}
}
return false;
}
},
methods: {
play(){
if(this.audio === null){
this.audio = new Audio(this.url);
}
this.audio.play();
}
}
}
</script>
<style scoped>
.playing {
color: green;
}
</style>
Note that this is a simplification of the code, but should be enough to see the issue. Since I couldn't find many examples using new Audio
call I am wondering if I should use the DOM calls directly instead.
Note that I am using the options API but don't mind answers with composition API too.
this.audio
properties are not reactive, so HTMLMediaElement
events play
and pause
must be used.canplay
event.error
event as well, if that's your question.{
props: {
url: {
type: String,
default: 'https://samplelib.com/lib/preview/mp3/sample-3s.mp3'
}
},
data: () => ({
audio: null,
canPlay: false,
isPlaying: false
}),
methods: {
play: async function(){
this.audio.play();
}
},
mounted: function(){
this.audio = new Audio(this.url);
this.audio.addEventListener('canplay', () => this.canPlay = true, { once: true });
this.audio.addEventListener('play', () => this.isPlaying = true);
this.audio.addEventListener('pause', () => this.isPlaying = false);
this.$watch(() => this.audio.duration, () => console.log(this.audio.paused));
},
template: `
<button
:class="{ playing: isPlaying }"
:disabled="!canPlay"
@click="play"
>Audio</button>
`
}