Triggering Animations
Once you've built , initialized and setup your view ready to render your animation - you next need to trigger it.
Using animate
The animate function is what does all the heavy lifting. It processes your animation configuration and merges the computed data into your existing AnimState. The call-site pattern is the same across all engines - the only difference are some return types:
View Source Code
Transition Keyframe Sub WAAPI ScrollTimeline ViewTimeline
( { model | animState = Transition . animate model . animState fadeIn }
, Cmd . none
)
( { model | animState = Keyframe . animate model . animState fadeIn }
, Cmd . none
)
( { model | animState = Sub . animate model . animState fadeIn }
, Cmd . none
)
let
( animState , cmd ) =
WAAPI . animate model . animState fadeIn
in
( { model | animState = animState }
, cmd
)
WAAPI uses JavaScript ports, so animate returns both the updated state and a cmd that sends the animation data to JS.
( model
, ScrollTimeline . animate motionCmd fadeIn
)
ScrollTimeline uses JavaScript ports, and no AnimState, so animate simply returns a cmd that sends the animation data to JS.
( model
, ViewTimeline . animate motionCmd fadeIn
)
ViewTimeline uses JavaScript ports, and no AnimState, so animate simply returns a cmd that sends the animation data to JS.
When to Trigger
In Response to Events
Most animations trigger in response to user action events or application events. Trigger these in update:
View Source Code
Transition Keyframe Sub WAAPI ScrollTimeline ViewTimeline
update msg model =
case msg of
GotButtonClick ->
( { model | animState = Transition . animate model . animState buttonPress }
, Cmd . none
)
GotDataReceived data ->
( { model
| data = data
, dataAnimState = Transition . animate model . dataAnimState dataFadeIn
}
, Cmd . none
)
update msg model =
case msg of
GotButtonClick ->
( { model | animState = Keyframe . animate model . animState buttonPress }
, Cmd . none
)
GotDataReceived data ->
( { model
| data = data
, dataAnimState = Keyframe . animate model . dataAnimState dataFadeIn
}
, Cmd . none
)
update msg model =
case msg of
GotButtonClick ->
( { model | animState = Sub . animate model . animState buttonPress }
, Cmd . none
)
GotDataReceived data ->
( { model
| data = data
, animState = Sub . animate model . animState dataFadeIn
}
, Cmd . none
)
update msg model =
case msg of
GotButtonClick ->
let
( animState , cmd ) =
WAAPI . animate model . animState buttonPress
in
( { model | animState = animState }
, cmd
)
GotDataReceived data ->
let
( animState , cmd ) =
WAAPI . animate model . animState dataFadeIn
in
( { model | animState = animState }
, cmd
)
update msg model =
case msg of
GotButtonClick ->
( model
, ScrollTimeline . animate motionCmd graphAnim
)
GotDataReceived data ->
( { model | data = data }
, ScrollTimeline . animate motionCmd dataPresentationAnim
)
update msg model =
case msg of
GotButtonClick ->
( model
, ViewTimeline . animate motionCmd graphAnim
)
GotDataReceived data ->
( { model | data = data }
, ViewTimeline . animate motionCmd dataPresentationAnim
)
On Page Load
To animate immediately when the page loads, you need to trigger in init. For most engines this is a simple process, for the Transition Engine not so:
View Source Code
Transition Keyframe Sub WAAPI ScrollTimeline ViewTimeline
❌ Behaviour : The element appears at the final state, with no animation.
📖 See Transition Engine - How CSS Transitions Work for more info.
init =
let
animState =
Transition . init [ Opacity . init "boxAnim" 0 ]
in
( { animState = Transition . animate animState fadeIn }
, Cmd . none
)
✅ Behaviour : The @keyframes rules are added to the DOM on first render when you apply the styleNode function in your view, and connect to your element when you add the attributes to it; the browser will then run them immediately.
init =
let
animState =
Keyframe . init [ Opacity . init "boxAnim" 0 ]
in
( { animState = Keyframe . animate animState fadeIn }
, Cmd . none
)
✅ Behaviour : The initial property values are used for first render, and the animation starts in the first update loop.
init =
let
animState =
Sub . init [ Opacity . init "boxAnim" 0 ]
in
( { animState = Sub . animate animState fadeIn }
, Cmd . none
)
✅ Behaviour : The initial property values are used for first render, and the animation starts in the first update loop after JS receives the data in cmd.
init =
let
animState =
WAAPI . init motionCmd motionMsg <|
[ Opacity . init "box" 0 ]
( animState , cmd ) =
WAAPI . animate animState fadeIn
in
( { animState = animState }
, cmd
)
✅ Behaviour : The animation is registered in the first update loop after JS receives the animation data, and then plays in relation to user scroll position.
init =
( { model | ... }
, ScrollTimeline . animate motionCmd fadeIn
)
✅ Behaviour : The animation is registered in the first update loop after JS receives the animation data, and then plays in relation to it's position in the viewport.
init =
( { model | ... }
, ViewTimeline . animate motionCmd fadeIn
)
Not in view
You could do this:
View Source Code
view model =
let
boxAnimState = Transition . animate ( Transition . init []) fadeIn
in
div
[]
[ div
( Transition . attributes "boxAnim" boxAnimState )
[ text "I'm animated wrongly" ]
]
It works, but the builder functions run on every render , creating unnecessary GC pressure. The view should be a pure function of model state. Prefer triggering in update or init.
Triggering Mid-Flight
What happens when you trigger an animation while another animation is already running on the same element?
📖 See Mid-Flight Interruptions for how each engine handles this.
Next Steps
Now that you can trigger animations, learn how to react to their lifecycle events.
React →