mdgriffith / elm-animator / Animator.Css

Animate with CSS keyframes.

This can be a very performant because

  1. CSS animations are executed efficiently by the browser.

  2. 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 "" ]

Properties


type Attribute state

opacity : (state -> Animator.Movement) -> Attribute state

height : (state -> Animator.Movement) -> Attribute state

width : (state -> Animator.Movement) -> Attribute state

Text

fontSize : (state -> Animator.Movement) -> Attribute state

fontColor : (state -> Color) -> Attribute state

wordSpacing : (state -> Animator.Movement) -> Attribute state

letterSpacing : (state -> Animator.Movement) -> Attribute state

Background

backgroundColor : (state -> Color) -> Attribute state

Border

borderColor : (state -> Color) -> Attribute state

borderRadius : (state -> Animator.Movement) -> Attribute state

Custom

style : String -> (Basics.Float -> String) -> (state -> Animator.Movement) -> Attribute state

Transform

explain : Basics.Bool -> Attribute state

This is a debug tool for transformations.

If you turn this on, it will show you:


type Transform

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

Transform Options

transformWith : TransformOptions -> (state -> Transform) -> Attribute state


type alias TransformOptions =
{ rotationAxis : { x : Basics.Float
, y : Basics.Float
, z : Basics.Float }
, origin : Origin 
}


type 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.

Advanced

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