I have this Svelte code:
Player.svelte:
<script> import PlayerControls from './PlayerControls.svelte'; let audio; </script> <audio src={...} bind:this={audio} /> <PlayerControls {audio} />
PlayerControls.svelte:
<script> import PlayIcon from '../icons/PlayIcon.svelte'; import PauseIcon from '../icons/PauseIcon.svelte'; export let audio; const onClick = () => { if (audio?.paused) audio.play(); else audio.pause(); }; </script> {#if audio?.paused} <PlayIcon {onClick} /> {:else} <PauseIcon {onClick} /> {/if}
If I press the play icon, the icon does not change, but the audio starts, if I click again, the audio stops, but the icon does not change. It seems that audio.paused only "changes" in the script, not in the html. What's the problem here, and what don't I understand about Svelte?
P粉5461798352024-03-28 21:25:26
In your given case, the most robust solution is to utilize specific <audio>
elements Events to alert svelte to recheck the status of the ref. This allows svelte to manage your listener lifecycle, and also allows the element itself to handle all edge cases of when/how the playback state changes.
sssccc
The documentation link in my comment covers a simple solution to your problem Reactivity/Updating Arrays and Objects, which is to simply reassign audio
as onClick
The last step in the handler.
Keep in mind that this will not track changes in paused
if the audio ends on its own.
const onClick = () => { if (audio?.paused) audio.play(); else audio.pause(); audio = audio; };
I initially suggested changing the paused
attribute directly:
const onClick = () => { if (audio?.paused) { audio.play(); audio.paused = false; } else { audio.pause(); audio.paused = true; } };
But ignores the fact that audio
is a ref
and paused
is a read-only property.