gampleman/elm-visualization/2.4.3

Transition

Transition is a module for writing animations. It does not attempt to be an animation library for every use case, it is specifically designed for the needs of data visualization apps. The main idea is that one animates data in some intermediate form and then leaves the view function to display the data as normal.

Setting up animation in an app

While there are many ways to use this module, a typical setup will look like this:

import Browser.Events
import Interpolation exposing (Interpolator)
import Transition exposing (Transition)


type alias Model =
    { transition : Transition MyThing
    }


type Msg
    = Tick Int
    | StartAnimation MyThing

First setup a default transition that doesn't actually do anything:

init : () -> ( Model, Cmd Msg )
init () =
    ( { transition = Transition.constant initialThing }
    , Cmd.none
    )

Next setup a subscription:

subscriptions : Model -> Sub Msg
subscriptions model =
    if Transition.isComplete model.transition then
        Sub.none


    else
        Browser.Events.onAnimationFrameDelta (round >> Tick)

Define an interpolator for your value:

interpolateThing : MyThing -> MyThing -> Interpolator MyThing
interpolateThing from to =
     -- ...

Then handle stuff in your update function:

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        Tick t ->
            ( { model
                | transition = Transition.step t model.transition
              }
            , Cmd.none
            )


        StartAnimation newThing ->
            let
                oldThing =
                    Transition.value model.transition
            in
            ( { model
                | transition =
                    Transition.for 600 (interpolateThing oldThing newThing)
              }
            , Cmd.none
            )

Then make your view like normal:

view : Model -> Html Msg
view model =
    viewMyThing (Transition.value model.transition)


viewMyThing : MyThing -> Html Msg
viewMyThing thing =
    --- ...

Transitions

type Transition a

A transition is a smooth interpolation between a beginning state and an end state, with a duration and easing.

for : Int -> Interpolator a -> Transition a

Create a transition that will run for a certain number of miliseconds. You need to provide an interpolation between the start and end states.

For example to fade something in for 400ms:

fadeIn : Item -> Transition Item
fadeIn item =
    Interpolation.map (\opacity -> { item | opacity = opacity)
        (Interpolation.float 0 1)
        |> Transition.for 400
easeFor : Int -> Easing -> Interpolator a -> Transition a

This is like Transition.for, but allows one to specify a custom Easing function. Transition.for defaults to Transition.easeCubic.

A transition that is already complete that will always return the value passed in.

step : Int -> Transition a -> Transition a

Updates the internal state forward by the passed number of miliseconds. You would typically do this in your update function.

value : Transition a -> a

Returns the "current" value. You would typically call this in the view and render whatever this returns.

import Interpolation


transition : Transition Int
transition =
    Transition.easeFor 500 Transition.easeLinear (Interpolation.int 0 10)


transition |> Transition.value --> 0
transition |> Transition.step 250 |> Transition.value --> 5
transition |> Transition.step 600 |> Transition.value --> 10

Allows you to check if a transition has finished running. This can be used to clean up subscriptions.

Reverses the direction of a transition running it backwards.

Repetition

repeat : Int -> Transition a -> Transition a

Repeat the transition a number of times.

Repeat the transition a set number of times, but run every even run in reverse.

Keep running the transition.

Keep running the transition indefinitely, but alternating forward and reverse runs.

Staggered animation

{ durationEach : Int
, delay : Int
, easing : Easing
}
-> List (Interpolator a)
-> Transition (List a)

Run a bunch of animations with a duration and easing, but delay each successive animation by the delay amount.

Tip: You may find the List (Interpolator a) -> Transition (List a) a little inconvenient for organizing staggered animations. However, you can create an List (Interpolator (a -> a)), where each function touches and interpolates some orthogonal property, then List.foldl (\fn val -> fn val) model.target (Transition.value model.transition). This way you can stagger updates to almost any datastructure. However, you need to be somewhat careful if there are other ways the datastructure can be changed (like user input) to make sure to interupt the animation suitably.

type Foo =
    { position: (Float, Float)
    , color : Color
    }


[ \t foo -> { foo | position = interpolatePosition t }
, \t foo -> { foo | color = Interpolation.rgb Color.blue Color.red t }
]
|> Transition.stagger { durationEach = 200, delay = 100, easing Transition.easingCubic }

Will first start animating the position, then after 100ms start animating the color, for a total duration of 300ms.

Easing

type Easing

Easing is a method of distorting time to control apparent motion in animation. It is most commonly used for slow-in, slow-out. By easing time, animated transitions are smoother and exhibit more plausible motion.

Linear easing is esentially the identity function of easing.

Symetric cubic easing. This is quite a good default for a lot of animation. Equivalent to easePolynomial 3

Polynomial easing; raises t to the provided exponent.

Reverse polynomial easing; equivalent to 1 - easePolynomialIn (1 - t).

Symmetric polynomial easing. In t [0, 0.5] equivalent to easePolynomialIn in [0.5, 1] easePolynomialOut

Sinusoidal easing; returns sin(t).

Reverse sinusoidal easing; equivalent to 1 - sinIn(1 - t).

Symmetric sinusoidal easing

Exponential easing; raises 2 to the exponent 10 * (t - 1).

Reverse exponential easing.

Symetric exponential easing.

Circular easing.

Reverse circular easing.

Symetric circular easing.

{ amplitude : Float
, period : Float
}
-> Easing

Elastic easing, like a rubber band. Reasonable defaults for the parameters would be { amplitude = 1, period = 0.3 }.

{ amplitude : Float
, period : Float
}
-> Easing

Reverse elastic easing.

{ amplitude : Float
, period : Float
}
-> Easing

Symmetric elastic easing.

easeBackIn : Float -> Easing

Anticipatory easing, like a dancer bending his knees before jumping off the floor. The degree of overshoot is configurable. A reasonable default is 1.70158. This represents about 10% more than the difference between the numbers.

easeBackOut : Float -> Easing

Reverse anticipatory easing.

easeBack : Float -> Easing

Symmetric anticipatory easing.

Bounce easing, like a rubber ball.

Reverse bounce easing.

Symmetric bounce easing.