使用 scalajs-react 在音频元素的间隔上循环
Looping on an interval of audio element with scalajs-react
我想构建一个包含 audio
元素的小组件,该组件能够按间隔循环。间隔的两端将被定义为组件的属性。由于 timeUpdate
事件没有必要的精度(我希望至少保证 33Hz),我决定使用 TimerSupport
的后端,并将 currentTime
设置回起始一旦它通过间隔的结束点。
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
在这个小例子中,我只想打印玩家的当前位置,但出于某种原因(回调在组件安装时关闭了后端上下文的副本?)AudioRef(c)
指向音频元素的旧版本。知道如何解决这个问题吗?我也对其他设计感兴趣,因为我对 ScalaJS 和 React 都没有真正的经验。
我最终通过在 Callback
中获取基于其 id
的音频元素来设置 currentTime
属性,因此我的解决方案目前如下所示:
class PlayerBackend($: BackendScope[String, Unit]) extends TimerSupport
val AudioPlayer = ReactComponentB[String]("AudioPlayer")
.initialState(())
.backend(i => new PlayerBackend(i))
.render_P(url => {
<.audio(
^.id := "audio",
^.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({document.getElementById("audio").asInstanceOf[Audio].currentTime = 5.0}) ,
1 seconds
)
})
.configure(TimerSupport.install)
.build
问题出在 log
调用中,该调用仅对其参数求值一次,从而产生一个值,然后一遍又一遍地记录该值。正确的代码应该是这样的:
.componentDidMount({ c =>
c.backend.setInterval(CallbackTo[Double] {
if (AudioRef(c).isDefined) ({
AudioRef(c).get.currentTime
}) else 0
} >>= Callback.log, 1000 millisecond)
})
它创建一个提取 currentTime
值(或什么都不提取)的回调,然后 flatMaps 到另一个记录该值的回调。
我想构建一个包含 audio
元素的小组件,该组件能够按间隔循环。间隔的两端将被定义为组件的属性。由于 timeUpdate
事件没有必要的精度(我希望至少保证 33Hz),我决定使用 TimerSupport
的后端,并将 currentTime
设置回起始一旦它通过间隔的结束点。
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
在这个小例子中,我只想打印玩家的当前位置,但出于某种原因(回调在组件安装时关闭了后端上下文的副本?)AudioRef(c)
指向音频元素的旧版本。知道如何解决这个问题吗?我也对其他设计感兴趣,因为我对 ScalaJS 和 React 都没有真正的经验。
我最终通过在 Callback
中获取基于其 id
的音频元素来设置 currentTime
属性,因此我的解决方案目前如下所示:
class PlayerBackend($: BackendScope[String, Unit]) extends TimerSupport
val AudioPlayer = ReactComponentB[String]("AudioPlayer")
.initialState(())
.backend(i => new PlayerBackend(i))
.render_P(url => {
<.audio(
^.id := "audio",
^.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({document.getElementById("audio").asInstanceOf[Audio].currentTime = 5.0}) ,
1 seconds
)
})
.configure(TimerSupport.install)
.build
问题出在 log
调用中,该调用仅对其参数求值一次,从而产生一个值,然后一遍又一遍地记录该值。正确的代码应该是这样的:
.componentDidMount({ c =>
c.backend.setInterval(CallbackTo[Double] {
if (AudioRef(c).isDefined) ({
AudioRef(c).get.currentTime
}) else 0
} >>= Callback.log, 1000 millisecond)
})
它创建一个提取 currentTime
值(或什么都不提取)的回调,然后 flatMaps 到另一个记录该值的回调。