【发布时间】:2015-06-23 17:40:00
【问题描述】:
我正在 Elm 中制作一个小型应用程序。它在屏幕上显示一个计时器,当计时器达到零时,它会播放声音。我无法弄清楚如何从计时器向声音播放器发送消息(?)。
在架构上,我有三个模块:一个代表计时器的Clock 模块,一个可以播放音频的PlayAudio 模块,以及一个将Clock 模块和PlayAudio 模块联系在一起的Main 模块。
理想情况下,当时钟达到零时,我想做一些事情,比如从Clock 模块发送信号。当时钟归零时,Clock 会向Main 发送信号,Main 会将其转发给PlayAudio。
但是,通过阅读 Elm 文档,似乎不鼓励使用 Main 以外的任何东西来处理信号。所以这引出了我的第一个问题。对这种状态变化进行建模的好方法是什么? Clock 中的 update 函数是否应该返回它是否已经结束? (这就是我在下面的做法,但我非常愿意接受有关如何做得更好的建议。)
我的第二个问题是关于如何让声音播放。我将使用原始 Javascript 来播放声音,我相信这意味着我必须使用端口。但是,我不确定如何与子模块Main 中定义的端口进行交互,PlayAudio。
下面是我正在使用的代码。
Clock.elm:
module Clock (Model, init, Action, signal, update, view) where
import Html (..)
import Html.Attributes (..)
import Html.Events (..)
import LocalChannel (..)
import Signal
import Time (..)
-- MODEL
type ClockState = Running | Ended
type alias Model =
{ time: Time
, state: ClockState
}
init : Time -> Model
init initialTime =
{ time = initialTime
, state = Running
}
-- UPDATE
type Action = Tick Time
update : Action -> Model -> (Model, Bool)
update action model =
case action of
Tick tickTime ->
let hasEnded = model.time <= 1
newModel = { model | time <-
if hasEnded then 0 else model.time - tickTime
, state <-
if hasEnded then Ended else Running }
in (newModel, hasEnded)
-- VIEW
view : Model -> Html
view model =
div []
[ (toString model.time ++ toString model.state) |> text ]
signal : Signal Action
signal = Signal.map (always (1 * second) >> Tick) (every second)
PlaySound.elm:
module PlaySound (Model, init, update, view) where
import Html (..)
import Html.Attributes (..)
import Html.Events (..)
import LocalChannel (..)
import Signal
import Time (..)
-- MODEL
type alias Model =
{ playing: Bool
}
init : Model
init =
{ playing = False
}
-- UPDATE
update : Bool -> Model -> Model
update shouldPlay model =
{ model | playing <- shouldPlay }
-- VIEW
view : Model -> Html
view model =
let node = if model.playing
then audio [ src "sounds/bell.wav"
, id "audiotag" ]
[]
else text "Not Playing"
in div [] [node]
Main.elm:
module Main where
import Debug (..)
import Html (..)
import Html.Attributes (..)
import Html.Events (..)
import Html.Lazy (lazy, lazy2)
import Json.Decode as Json
import List
import LocalChannel as LC
import Maybe
import Signal
import String
import Time (..)
import Window
import Clock
import PlaySound
---- MODEL ----
-- The full application state of our todo app.
type alias Model =
{ clock : Clock.Model
, player : PlaySound.Model
}
emptyModel : Model
emptyModel =
{ clock = 10 * second |> Clock.init
, player = PlaySound.init
}
---- UPDATE ----
type Action
= NoOp
| ClockAction Clock.Action
-- How we update our Model on a given Action?
update : Action -> Model -> Model
update action model =
case action of
NoOp -> model
ClockAction clockAction ->
let (newClock, hasEnded) = Clock.update clockAction model.clock
newPlaySound = PlaySound.update hasEnded model.player
in { model | clock <- newClock
, player <- newPlaySound }
---- VIEW ----
view : Model -> Html
view model =
let context = Clock.Context (LC.create ClockAction actionChannel)
in div [ ]
[ Clock.view context model.clock
, PlaySound.view model.player
]
---- INPUTS ----
-- wire the entire application together
main : Signal Html
main = Signal.map view model
-- manage the model of our application over time
model : Signal Model
model = Signal.foldp update initialModel allSignals
allSignals : Signal Action
allSignals = Signal.mergeMany
[ Signal.map ClockAction Clock.signal
, Signal.subscribe actionChannel
]
initialModel : Model
initialModel = emptyModel
-- updates from user input
actionChannel : Signal.Channel Action
actionChannel = Signal.channel NoOp
port playSound : Signal ()
port playSound = ???
index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="js/elm.js" type="text/javascript"></script>
<link rel="stylesheet" href="style.css">
</head>
<body>
<script type="text/javascript">
var todomvc = Elm.fullscreen(Elm.Main);
todomvc.ports.playSound.subscribe(function() {
setTimeout(function() {
document.getElementById('audiotag').play();
}, 50);
});
</script>
</body>
</html>
【问题讨论】:
标签: javascript timer architecture frp elm