Animate with CSS keyframes.
This can be a very performant because
CSS animations are executed efficiently by the browser.
We only have to ask for one AnimationFrame
from the browser when an animation starts and we can render the entire animation.
This means Elm
only needs to run your view code once instead of 60 times a second.
Elm
is generally pretty fast and efficient at all this! But it can be even faster to skip the work altogether.
watching : (model -> Animator.Timeline state) -> (Animator.Timeline state -> model -> model) -> Animator model -> Animator model
Animator.Css.watching
is different from Animator.watching
in that it will only ask for one frame when an animation is updated.
In that one frame, we render the entire CSS animation, which can run without Elm
needing to do a full rerender.
div : Animator.Timeline state -> List (Attribute state) -> List (Html.Attribute msg) -> List (Html msg) -> Html msg
This is a single div element.
It's just like a normal Html
node, except it also takes a Timeline
and a list of attributes you want to animate.
Here's a checkbox that changes backgrounds as a brief example:
Animator.Css.div model.checked
[ Animator.Css.backgroundColor <|
\checked ->
if checked then
Color.rgb255 255 96 96
else
Color.white
]
[ Attr.style "height" "30px"
, Attr.style "width" "30px"
]
[ Html.text "" ]
node : String -> Animator.Timeline state -> List (Attribute state) -> List (Html.Attribute msg) -> List (Html msg) -> Html msg
Specify a node name that's not a div. Here's an <a>
.
Animator.Css.node "a"
model.checked
[ Animator.Css.backgroundColor <|
\checked ->
if checked then
Color.rgb255 255 96 96
else
Color.white
]
[ Attr.style "height" "30px"
, Attr.style "width" "30px"
]
[ Html.text "" ]
opacity : (state -> Animator.Movement) -> Attribute state
height : (state -> Animator.Movement) -> Attribute state
width : (state -> Animator.Movement) -> Attribute state
fontSize : (state -> Animator.Movement) -> Attribute state
fontColor : (state -> Color) -> Attribute state
wordSpacing : (state -> Animator.Movement) -> Attribute state
letterSpacing : (state -> Animator.Movement) -> Attribute state
backgroundColor : (state -> Color) -> Attribute state
borderColor : (state -> Color) -> Attribute state
borderRadius : (state -> Animator.Movement) -> Attribute state
style : String -> (Basics.Float -> String) -> (state -> Animator.Movement) -> Attribute state
explain : Basics.Bool -> Attribute state
This is a debug tool for transformations.
If you turn this on, it will show you:
A grey bounding box so you get a sense of the element you're animating.
A red dot at the center of the element. Any rotation will be around this point.
You can adjust the dot's position using transformWith
and setting a new origin.
The coordinate axes. Yes, in CSS, y points down.
This represents moving something around in 3d.
This includes:
transform : (state -> Transform) -> Attribute state
Animator.Css.transform <|
\state ->
case state of
Stationary ->
Animator.Css.xy
{ x = 0
, y = 0
}
Rotating ->
-- do a full rotation every 5 seconds
Animator.Css.rotating
(Animator.seconds 5)
Note - If you're doing 3d transformations, you should set a CSS perspective
property either on this element or on a parent. If it's on a parent, then all elements will share a common perspective
origin.
So, add something like this to the parent element:
perspective: 500px;
perspective-origin: center;
rotateTo : Basics.Float -> Transform
Rotate to a specific angle, where increasing numbers move clockwise.
The actual number provided should be in Elm standard angles (radians).
Alternatively you could use turns or degrees instead.
lookAt : { x : Basics.Float, y : Basics.Float, z : Basics.Float } -> Transform -> Transform
Have this element "look at" a specifc point.
The coordinates provided are relative to this element.
The default, which is where the element is looking directly outwards from the screen, is:
Animator.Css.lookAt
{ x = 0
, y = 0
, z = 1
}
rotating : Animator.Duration -> Transform
Provide the duration it should take for one full rotation.
scale : Basics.Float -> Transform
xy : { x : Basics.Float, y : Basics.Float } -> Transform
xyz : { x : Basics.Float, y : Basics.Float, z : Basics.Float } -> Transform
transformWith : TransformOptions -> (state -> Transform) -> Attribute state
{ rotationAxis : { x : Basics.Float
, y : Basics.Float
, z : Basics.Float }
, origin : Origin
}
The origin for rotation. Defaults to the center of the element.
center : Origin
offset : Basics.Float -> Basics.Float -> Origin
This is the x and y offset, in pixels from the center of the object.
Generally I found the most common case for adjusting the origin was when you want it mostly centered, but aligned to the visual center of an icon or image, which is offset by a number of pixels.
Note - One of the difficulties in making an animation library for CSS
is that operations such as translate
and rotate
need to be rendered as one property called transform
.
While normally this isn't that big of a deal, one consequence for generating CSS
keyframes is that all resting states for all transformations need to share the same period
.
Because of that, in2d
and in3d
are constructed a little differently instead of just using Movement
like everything else.
resting : Basics.Float -> Animator.Oscillator
in2d : Period -> { x : Animator.Oscillator, y : Animator.Oscillator, rotate : Animator.Oscillator, scaleX : Animator.Oscillator, scaleY : Animator.Oscillator } -> Transform
once : Animator.Duration -> Period
repeat : Basics.Int -> Animator.Duration -> Period
loop : Animator.Duration -> Period