kokizzu / elm-visualization / 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 : Basics.Int -> Interpolation.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 : Basics.Int -> Easing -> Interpolation.Interpolator a -> Transition a

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

constant : a -> Transition a

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

step : Basics.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

isComplete : Transition a -> Basics.Bool

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

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.

easeLinear : Easing

Linear easing is esentially the identity function of easing.

easeCubic : Easing

Symetric cubic easing. This is quite a good default for a lot of animation.