Building Highly Performant Animation in Elm
by Abadi Kurniawan
Pronouns: he/him/his
Elm Conf 2019
Journey Through the
Animation Lands
The Goal
- Explore ideas for ways of doing animation
- High performant animation
- Make our caterpillar happy
NOT the Goal
- Teach you best practice
- Build a finished solution
- WebGL / Canvas
The Land of Pure Elm
Single Animation in Elm
type alias Model = { x : Float }
type Msg = Tick Float
subscriptions model =
onAnimationFrameDelta Tick
update (Tick delta) model =
let
speed = 0.2
in
( { model | x = x + (delta * speed) }
, Cmd.none
)
view model =
div [] [ apple [ css [ left (Css.px model.x) ] ]
Multiple Animations in Elm
type alias Model =
{ timeElapsed: Float, x1 : Float, x2: Float }
update (Tick delta) model =
let
speed =
0.2 -- pixel per ms
timeElapsed =
model.timeElapsed + delta
in
( { model
| timeElapsed = timeElapsed
, x1 =
if timeElapsed < 1000 then
x1 + (delta * speed)
else
x1
, x2 =
if timeElapsed > 1000 then
x2 + (delta * speed)
else
x2
}
, Cmd.none
)
view model =
div []
[ apple [ css [ left (Css.px model.x1) ]
, apple [ css [ left (Css.px model.x2) ]
]
Pros
- Pure functional
Cons
- Cumbersome
- No browser/hardware optimization
- Break elm debugger
Animation as Side Effect
CSS Animation Land
CSS Keyframe Animations
@keyframes myAnimation {
0% { transform: translate(0px, 0px); }
100% { transform: translate(0px, 100px); }
}
div.apple {
animation: 1s myAnimation;
}
Workflow
Simple Animation
Sequence of Animations
Multiple Animations
CSS Animation
view model =
div []
[ apple
|> Animation.css
[ Animation.sequence
[ Animation.translate
{ x = px 500, y = px 0 }
(second 2)
, Animation.translate
{ x = px 0, y = px 300 }
(second 2)
]
]
, apple
|> Animation.css
[ Animation.translate
{ x = px 500, y = px 0 }
(second 2)
|> Animation.delay (second 2)
, Animation.opacity
{ from = percentage 100, to = percentage 0 }
(second 2)
|> Animation.delay (second 2)
]
]
Pros
- Highly performant
- Declarative
Cons
- Difficult to synchronize multiple animations
Web Animations Land
a.k.a JavaScript Land
Web Animations API
JavaScript API for accessing browser's animation engine
document.getElementById("apple").animate(
[
{ transform: 'translate(0, 0)' },
{ transform: 'translate(500, 0)' }
],
{
fill: 'forwards',
duration: 3000,
iterations: 1
}
);
https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API
JavaScript Interop
- Port
- Web Component
Port
Web Component
( Custom Element )
Learn more about Web Component
"When and how to use Web Components with elm"
by Luke Westby
Elm Europe 2018
https://youtu.be/tyFe9Pw6TVE
The Workflow
The Workflow
Single Animation
view model =
div []
[ Animation.node
[ Animation.translate { x = px 0, y = px 0 }
, Animation.translate { x = px 500, y = px 0 }
]
({ duration = millisecond 2000 }
|> Options.default
|> Options.withFill Fill.forwards
)
[]
(apple model)
]
Multiple Animations
type AnimationState = FirstAppleMoving | SecondAppleMoving
view model =
div []
[ Animation.node
keyframes
options
[ Events.onFinish FirstAppleAnimationFinish ]
(apple model)
, Animation.node
(case model.animationState of
FirstAppleMoving ->
[]
SecondAppleMoving ->
keyframes
)
options
[]
(apple model)
]
update msg model =
case msg of
FirstAppleAnimationFinish ->
( { model | animationState = SecondAppleMoving }
, Cmd.none
)
Pros
- Highly performant
- Declarative
- Simpler to synchronize multiple animations
Cons
- Browser support (only FF and Chrome)
What's Next?
- Implement all Web Animations API in Elm
( Request for collaborator )Elm Slack: @abadi199Github: @abadi199Twitter: @abadikurniawan
Links
-
https://github.com/abadi199/elm-animation-exploration