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.
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 =
--- ...
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.
reverse : Transition a -> Transition a
Reverses the direction of a transition running it backwards.
repeat : Basics.Int -> Transition a -> Transition a
Repeat the transition a number of times.
repeatAlternate : Basics.Int -> Transition a -> Transition a
Repeat the transition a set number of times, but run every even run in reverse.
repeatIndefinitely : Transition a -> Transition a
Keep running the transition.
repeatAlternateIndefinitely : Transition a -> Transition a
Keep running the transition indefinitely, but alternating forward and reverse runs.
stagger : { durationEach : Basics.Int, delay : Basics.Int, easing : Easing } -> List (Interpolation.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 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. Equivalent to easePolynomial 3
easePolynomialIn : Basics.Float -> Easing
Polynomial easing; raises t to the provided exponent.
easePolynomialOut : Basics.Float -> Easing
Reverse polynomial easing; equivalent to 1 - easePolynomialIn (1 - t)
.
easePolynomial : Basics.Float -> Easing
Symmetric polynomial easing. In t [0, 0.5] equivalent to easePolynomialIn
in [0.5, 1] easePolynomialOut
easeSinusoidalIn : Easing
Sinusoidal easing; returns sin(t).
easeSinusoidalOut : Easing
Reverse sinusoidal easing; equivalent to 1 - sinIn(1 - t).
easeSinusoidal : Easing
Symmetric sinusoidal easing
easeExponentialIn : Easing
Exponential easing; raises 2 to the exponent 10 * (t - 1).
easeExponentialOut : Easing
Reverse exponential easing.
easeExponential : Easing
Symetric exponential easing.
easeCircleIn : Easing
Circular easing.
easeCircleOut : Easing
Reverse circular easing.
easeCircle : Easing
Symetric circular easing.
easeElasticIn : { amplitude : Basics.Float, period : Basics.Float } -> Easing
Elastic easing, like a rubber band. Reasonable defaults for the parameters would be { amplitude = 1, period = 0.3 }
.
easeElasticOut : { amplitude : Basics.Float, period : Basics.Float } -> Easing
Reverse elastic easing.
easeElastic : { amplitude : Basics.Float, period : Basics.Float } -> Easing
Symmetric elastic easing.
easeBackIn : Basics.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 : Basics.Float -> Easing
Reverse anticipatory easing.
easeBack : Basics.Float -> Easing
Symmetric anticipatory easing.
easeBounceIn : Easing
Bounce easing, like a rubber ball.
easeBounceOut : Easing
Reverse bounce easing.
easeBounce : Easing
Symmetric bounce easing.