I want to build a little component containing an audio
element, which capable of looping on an interval. The two ends of the interval would be a defined as properties of the component. As the timeUpdate
event don't have the necessarily precision (I want at least 33Hz guaranteed), I decided to use a backend with TimerSupport
, and just set the currentTime
back to the starting point once it passes the end of the interval.
val AudioRef = Ref[Audio]("audio")
class PlayerBackend extends TimerSupport
val AudioPlayer = ReactComponentB[String]("AudioPlayer")
.initialState(0L)
.backend(i => new PlayerBackend())
.render_P(url => {
<.audio(
^.ref := AudioRef,
^.autoPlay := true,
^.controls := true,
<.source(^.src := "http://www.stephaniequinn.com/Music/Allegro%20from%20Duet%20in%20C%20Major.mp3"),
"Your browser does not support the audio element."
)
})
.componentDidMount({ c =>
c.backend.setInterval(Callback.log({
if (AudioRef(c).isDefined) ({
AudioRef(c).get.currentTime
}) else "nothing"
}), 1000 millisecond)
}).configure(TimerSupport.install)
.build
It this little example I just want to print the current position of the player, but for some reason (the callback closes over a copy of the backend context at the time when the component mounts?) the AudioRef(c)
points to an old version of the audio element. Any idea how to fix this? I'm also interested in other designs, as I'm not really experienced neither with ScalaJS nor React.
The issue is with the log
call that evaluates its parameter only once, resulting in a single value that is then logged over and over again. The correct code would be something like this:
.componentDidMount({ c =>
c.backend.setInterval(CallbackTo[Double] {
if (AudioRef(c).isDefined) ({
AudioRef(c).get.currentTime
}) else 0
} >>= Callback.log, 1000 millisecond)
})
It creates a callback that extracts the currentTime
value (or nothing) and then flatMaps to another callback that logs that value.