Skip to content

Transform Ordering

The Keyframe, Sub, and WAAPI engines expose a transformOrder function which takes a list of TransformPropertys:

  • Translate
  • Rotate
  • Skew
  • Scale

Use these to change the transform order that is applied to your animations.

Default Order

Elm Motion uses Translate → Rotate → Skew → Scale as the default order when no order is specified with the transformOrder function.

All transforms are applied simultaneously - the order controls how they compose mathematically. This default works well for most animations because:

  • Translation is unaffected by rotation or scale
  • Rotation happens around the translated position, not the origin
  • Skew distorts axes relative to the already-rotated orientation, keeping the visual shear consistent with the element's current facing
  • Scaling is relative to the already-rotated axes

For most animations, the default transform order works well and you won't need to change it. However, certain scenarios benefit from a different order — for example, a game character that rotates to face a direction and then moves forward (rotate → translate), so the movement follows the character's facing direction rather than the world axes.

So, changing the order, changes the result - as can be seen in the following example.

Example

There are 6 boxes in the center, each one is triggered with the same animation, the only difference is the transform order.

View Examples

View Source Code
module Animation.Keyframe.TransformOrder.Main exposing (main)

import Anim.Builder exposing (AnimBuilder)
import Anim.Engine.Keyframe as Keyframe
import Anim.Extra.TransformOrder as TransformProperty exposing (TransformProperty(..))
import Anim.Property.Rotate as Rotate
import Anim.Property.Scale as Scale
import Anim.Property.Skew as Skew
import Anim.Property.Translate as Translate
import Anim.Unit exposing (Unit(..))
import Browser
import Html exposing (Html, button, div, text)
import Html.Attributes exposing (class, style)
import Html.Events exposing (onClick)
import Motion.Easing as Easing exposing (Easing(..))



-- MAIN


main : Program () Model Msg
main =
    Browser.element
        { init = \_ -> init
        , view = view
        , update = update
        , subscriptions = \_ -> Sub.none
        }



-- MODEL


type alias Model =
    { animState : Keyframe.AnimState
    , animatedBoxes : List Permutation
    }


init : ( Model, Cmd Msg )
init =
    ( { animState =
            Keyframe.init <|
                List.concatMap
                    (\perm ->
                        [ Translate.initXY (permutationKey perm) 0 0 >> Translate.cssUnitX Cqw >> Translate.cssUnitY Cqh
                        , Skew.initXY (permutationKey perm) 0 0
                        ]
                    )
                    allPermutations
      , animatedBoxes = []
      }
    , Cmd.none
    )


boxSize : Float
boxSize =
    16


boxSizeCss : String
boxSizeCss =
    String.fromFloat boxSize ++ "cqmin"


type Permutation
    = TRSkS
    | TSkRS
    | RTSkS
    | SkTRS
    | STRSk
    | RSkTS


allPermutations : List Permutation
allPermutations =
    [ TRSkS, TSkRS, RTSkS, SkTRS, STRSk, RSkTS ]


permutationKey : Permutation -> String
permutationKey perm =
    case perm of
        TRSkS ->
            "t-r-sk-s"

        TSkRS ->
            "t-sk-r-s"

        RTSkS ->
            "r-t-sk-s"

        SkTRS ->
            "sk-t-r-s"

        STRSk ->
            "s-t-r-sk"

        RSkTS ->
            "r-sk-t-s"


permutationLabel : Permutation -> String
permutationLabel perm =
    case perm of
        TRSkS ->
            "T → R → Sk → S"

        TSkRS ->
            "T → Sk → R → S"

        RTSkS ->
            "R → T → Sk → S"

        SkTRS ->
            "Sk → T → R → S"

        STRSk ->
            "S → T → R → Sk"

        RSkTS ->
            "R → Sk → T → S"


permutationOrder : Permutation -> List TransformProperty
permutationOrder perm =
    case perm of
        TRSkS ->
            [ Translate, Rotate, Skew, Scale ]

        TSkRS ->
            [ Translate, Skew, Rotate, Scale ]

        RTSkS ->
            [ Rotate, Translate, Skew, Scale ]

        SkTRS ->
            [ Skew, Translate, Rotate, Scale ]

        STRSk ->
            [ Scale, Translate, Rotate, Skew ]

        RSkTS ->
            [ Rotate, Skew, Translate, Scale ]


permutationColor : Permutation -> String
permutationColor perm =
    case perm of
        TRSkS ->
            "59, 130, 246"

        TSkRS ->
            "16, 185, 129"

        RTSkS ->
            "245, 158, 11"

        SkTRS ->
            "239, 68, 68"

        STRSk ->
            "139, 92, 246"

        RSkTS ->
            "236, 72, 153"



-- ANIMATION


moveOut : AnimBuilder eng -> AnimBuilder eng
moveOut =
    Translate.begin
        >> Translate.toXY 24 10
        >> Translate.end
        >> Rotate.begin
        >> Rotate.toZ 45
        >> Rotate.end
        >> Skew.begin
        >> Skew.toXY 15 9
        >> Skew.end
        >> Scale.begin
        >> Scale.toXY 1.5 0.8
        >> Scale.end


reset : AnimBuilder eng -> AnimBuilder eng
reset =
    Translate.begin
        >> Translate.toXY 0 0
        >> Translate.end
        >> Rotate.begin
        >> Rotate.toZ 0
        >> Rotate.end
        >> Skew.begin
        >> Skew.toXY 0 0
        >> Skew.end
        >> Scale.begin
        >> Scale.toXY 1 1
        >> Scale.end


engineDefaults : Permutation -> Keyframe.EngineBuilder -> Keyframe.EngineBuilder
engineDefaults perm =
    Keyframe.for (permutationKey perm)
        >> Keyframe.transformOrder (permutationOrder perm)
        >> Keyframe.duration 2000
        >> Keyframe.easing EaseInOut
        >> Keyframe.cssUnitX Cqw
        >> Keyframe.cssUnitY Cqh



-- UPDATE


type Msg
    = Animate Permutation
    | Reset Permutation
    | AnimateAll
    | ResetAll


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        Animate perm ->
            ( { model
                | animState =
                    Keyframe.animate model.animState <|
                        engineDefaults perm
                            >> moveOut
                , animatedBoxes =
                    if List.member perm model.animatedBoxes then
                        model.animatedBoxes

                    else
                        perm :: model.animatedBoxes
              }
            , Cmd.none
            )

        Reset perm ->
            ( { model
                | animState =
                    Keyframe.animate model.animState <|
                        engineDefaults perm
                            >> reset
                , animatedBoxes =
                    List.filter ((/=) perm) model.animatedBoxes
              }
            , Cmd.none
            )

        AnimateAll ->
            ( { model
                | animState =
                    Keyframe.animate model.animState <|
                        List.foldl
                            (\perm acc ->
                                engineDefaults perm
                                    >> moveOut
                                    >> acc
                            )
                            identity
                            allPermutations
                , animatedBoxes = allPermutations
              }
            , Cmd.none
            )

        ResetAll ->
            ( { model
                | animState =
                    Keyframe.animate model.animState <|
                        List.foldl
                            (\perm acc ->
                                engineDefaults perm
                                    >> reset
                                    >> acc
                            )
                            identity
                            model.animatedBoxes
                , animatedBoxes = []
              }
            , Cmd.none
            )



-- VIEW


view : Model -> Html Msg
view model =
    div
        [ class "example-stage"
        , style "container-type" "size"
        ]
        [ Keyframe.styleNode model.animState
        , div [ class "example-controls" ]
            (List.map permButton allPermutations)
        , div [ class "example-controls" ]
            [ actionButton "▶️ All" AnimateAll "#16a34a"
            , actionButton "⏮️ Reset All" ResetAll "#d97706"
            ]
        , animationArea model.animState
        ]


permButton : Permutation -> Html Msg
permButton perm =
    button
        [ onClick (Animate perm)
        , style "padding" "6px 14px"
        , style "border" "none"
        , style "border-radius" "6px"
        , style "background-color" ("rgb(" ++ permutationColor perm ++ ")")
        , style "color" "white"
        , style "font-size" "13px"
        , style "font-weight" "600"
        , style "cursor" "pointer"
        ]
        [ text (permutationLabel perm) ]


actionButton : String -> Msg -> String -> Html Msg
actionButton label msg color =
    button
        [ onClick msg
        , style "padding" "6px 14px"
        , style "border" "none"
        , style "border-radius" "6px"
        , style "background-color" color
        , style "color" "white"
        , style "font-size" "13px"
        , style "font-weight" "600"
        , style "cursor" "pointer"
        ]
        [ text label ]


animationArea : Keyframe.AnimState -> Html Msg
animationArea animState =
    div
        [ class "example-canvas"
        , style "position" "relative"
        , style "background-color" "#ffffff"
        , style "border-radius" "12px"
        , style "box-shadow" "0 4px 8px rgba(0, 0, 0, 0.1)"
        , style "overflow" "hidden"
        ]
        (List.map (animatedBox animState) allPermutations)


animatedBox : Keyframe.AnimState -> Permutation -> Html Msg
animatedBox animState perm =
    let
        rgb =
            permutationColor perm
    in
    div
        [ style "position" "absolute"
        , style "top" "clamp(10px, 2vmin, 16px)"
        , style "left" "50%"
        , style "transform" "translateX(-50%)"
        ]
        [ div
            (Keyframe.attributes (permutationKey perm) animState
                ++ [ style "width" boxSizeCss
                   , style "height" boxSizeCss
                   , style "background-color" ("rgba(" ++ rgb ++ ", 0.25)")
                   , style "border-radius" "1.6cqmin"
                   , style "border" ("0.4cqmin solid rgb(" ++ rgb ++ ")")
                   , style "font-size" "2.2cqmin"
                   , style "font-weight" "bold"
                   , style "color" ("rgb(" ++ rgb ++ ")")
                   , style "padding" "0.8cqmin"
                   , style "box-sizing" "border-box"
                   ]
            )
            [ text (permutationLabel perm) ]
        ]
module Animation.Sub.TransformOrder.Main exposing (main)

import Anim.Builder exposing (AnimBuilder)
import Anim.Engine.Sub as Sub
import Anim.Extra.TransformOrder as TransformProperty exposing (TransformProperty(..))
import Anim.Property.Rotate as Rotate
import Anim.Property.Scale as Scale
import Anim.Property.Skew as Skew
import Anim.Property.Translate as Translate
import Anim.Unit exposing (Unit(..))
import Browser
import Html exposing (Html, button, div, text)
import Html.Attributes exposing (class, style)
import Html.Events exposing (onClick)
import Motion.Easing as Easing exposing (Easing(..))



-- MAIN


main : Program () Model Msg
main =
    Browser.element
        { init = \_ -> init
        , view = view
        , update = update
        , subscriptions = subscriptions
        }



-- MODEL


type alias Model =
    { animState : Sub.AnimState
    , animatedBoxes : List Permutation
    }


init : ( Model, Cmd Msg )
init =
    ( { animState =
            Sub.init <|
                List.concatMap
                    (\perm ->
                        [ Translate.initXY (permutationKey perm) 0 0 >> Translate.cssUnitX Cqw >> Translate.cssUnitY Cqh
                        , Skew.initXY (permutationKey perm) 0 0
                        ]
                    )
                    allPermutations
      , animatedBoxes = []
      }
    , Cmd.none
    )


boxSize : Float
boxSize =
    16


boxSizeCss : String
boxSizeCss =
    String.fromFloat boxSize ++ "cqmin"


type Permutation
    = TRSkS
    | TSkRS
    | RTSkS
    | SkTRS
    | STRSk
    | RSkTS


allPermutations : List Permutation
allPermutations =
    [ TRSkS, TSkRS, RTSkS, SkTRS, STRSk, RSkTS ]


permutationKey : Permutation -> String
permutationKey perm =
    case perm of
        TRSkS ->
            "t-r-sk-s"

        TSkRS ->
            "t-sk-r-s"

        RTSkS ->
            "r-t-sk-s"

        SkTRS ->
            "sk-t-r-s"

        STRSk ->
            "s-t-r-sk"

        RSkTS ->
            "r-sk-t-s"


permutationLabel : Permutation -> String
permutationLabel perm =
    case perm of
        TRSkS ->
            "T → R → Sk → S"

        TSkRS ->
            "T → Sk → R → S"

        RTSkS ->
            "R → T → Sk → S"

        SkTRS ->
            "Sk → T → R → S"

        STRSk ->
            "S → T → R → Sk"

        RSkTS ->
            "R → Sk → T → S"


permutationOrder : Permutation -> List TransformProperty
permutationOrder perm =
    case perm of
        TRSkS ->
            [ Translate, Rotate, Skew, Scale ]

        TSkRS ->
            [ Translate, Skew, Rotate, Scale ]

        RTSkS ->
            [ Rotate, Translate, Skew, Scale ]

        SkTRS ->
            [ Skew, Translate, Rotate, Scale ]

        STRSk ->
            [ Scale, Translate, Rotate, Skew ]

        RSkTS ->
            [ Rotate, Skew, Translate, Scale ]


permutationColor : Permutation -> String
permutationColor perm =
    case perm of
        TRSkS ->
            "59, 130, 246"

        TSkRS ->
            "16, 185, 129"

        RTSkS ->
            "245, 158, 11"

        SkTRS ->
            "239, 68, 68"

        STRSk ->
            "139, 92, 246"

        RSkTS ->
            "236, 72, 153"



-- ANIMATION


moveOut : AnimBuilder eng -> AnimBuilder eng
moveOut =
    Translate.begin
        >> Translate.toXY 24 10
        >> Translate.end
        >> Rotate.begin
        >> Rotate.toZ 45
        >> Rotate.end
        >> Skew.begin
        >> Skew.toXY 15 9
        >> Skew.end
        >> Scale.begin
        >> Scale.toXY 1.5 0.8
        >> Scale.end


reset : AnimBuilder eng -> AnimBuilder eng
reset =
    Translate.begin
        >> Translate.toXY 0 0
        >> Translate.end
        >> Rotate.begin
        >> Rotate.toZ 0
        >> Rotate.end
        >> Skew.begin
        >> Skew.toXY 0 0
        >> Skew.end
        >> Scale.begin
        >> Scale.toXY 1 1
        >> Scale.end


engineDefaults : Permutation -> Sub.EngineBuilder -> Sub.EngineBuilder
engineDefaults perm =
    Sub.for (permutationKey perm)
        >> Sub.transformOrder (permutationOrder perm)
        >> Sub.duration 2000
        >> Sub.easing EaseInOut
        >> Sub.cssUnitX Cqw
        >> Sub.cssUnitY Cqh



-- UPDATE


type Msg
    = Animate Permutation
    | Reset Permutation
    | AnimateAll
    | ResetAll
    | GotSubMsg Sub.AnimMsg


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        GotSubMsg subMsg ->
            let
                ( newAnimState, _ ) =
                    Sub.update subMsg model.animState
            in
            ( { model | animState = newAnimState }
            , Cmd.none
            )

        Animate perm ->
            ( { model
                | animState =
                    Sub.animate model.animState <|
                        engineDefaults perm
                            >> moveOut
                , animatedBoxes =
                    if List.member perm model.animatedBoxes then
                        model.animatedBoxes

                    else
                        perm :: model.animatedBoxes
              }
            , Cmd.none
            )

        Reset perm ->
            ( { model
                | animState =
                    Sub.animate model.animState <|
                        engineDefaults perm
                            >> reset
                , animatedBoxes =
                    List.filter ((/=) perm) model.animatedBoxes
              }
            , Cmd.none
            )

        AnimateAll ->
            ( { model
                | animState =
                    Sub.animate model.animState <|
                        List.foldl
                            (\perm acc ->
                                engineDefaults perm
                                    >> moveOut
                                    >> acc
                            )
                            identity
                            allPermutations
                , animatedBoxes = allPermutations
              }
            , Cmd.none
            )

        ResetAll ->
            ( { model
                | animState =
                    Sub.animate model.animState <|
                        List.foldl
                            (\perm acc ->
                                engineDefaults perm
                                    >> reset
                                    >> acc
                            )
                            identity
                            model.animatedBoxes
                , animatedBoxes = []
              }
            , Cmd.none
            )



-- SUBSCRIPTIONS


subscriptions : Model -> Sub Msg
subscriptions model =
    Sub.subscriptions GotSubMsg model.animState



-- VIEW


view : Model -> Html Msg
view model =
    div
        [ class "example-stage"
        , style "container-type" "size"
        ]
        [ div [ class "example-controls" ]
            (List.map permButton allPermutations)
        , div [ class "example-controls" ]
            [ actionButton "▶️ All" AnimateAll "#16a34a"
            , actionButton "⏮️ Reset All" ResetAll "#d97706"
            ]
        , animationArea model.animState
        ]


permButton : Permutation -> Html Msg
permButton perm =
    button
        [ onClick (Animate perm)
        , style "padding" "6px 14px"
        , style "border" "none"
        , style "border-radius" "6px"
        , style "background-color" ("rgb(" ++ permutationColor perm ++ ")")
        , style "color" "white"
        , style "font-size" "13px"
        , style "font-weight" "600"
        , style "cursor" "pointer"
        ]
        [ text (permutationLabel perm) ]


actionButton : String -> Msg -> String -> Html Msg
actionButton label msg color =
    button
        [ onClick msg
        , style "padding" "6px 14px"
        , style "border" "none"
        , style "border-radius" "6px"
        , style "background-color" color
        , style "color" "white"
        , style "font-size" "13px"
        , style "font-weight" "600"
        , style "cursor" "pointer"
        ]
        [ text label ]


animationArea : Sub.AnimState -> Html Msg
animationArea animState =
    div
        [ class "example-canvas"
        , style "position" "relative"
        , style "background-color" "#ffffff"
        , style "border-radius" "12px"
        , style "box-shadow" "0 4px 8px rgba(0, 0, 0, 0.1)"
        , style "overflow" "hidden"
        ]
        (List.map (animatedBox animState) allPermutations)


animatedBox : Sub.AnimState -> Permutation -> Html Msg
animatedBox animState perm =
    let
        rgb =
            permutationColor perm
    in
    div
        [ style "position" "absolute"
        , style "top" "clamp(10px, 2vmin, 16px)"
        , style "left" "50%"
        , style "transform" "translateX(-50%)"
        ]
        [ div
            (Sub.attributes (permutationKey perm) animState
                ++ [ style "width" boxSizeCss
                   , style "height" boxSizeCss
                   , style "background-color" ("rgba(" ++ rgb ++ ", 0.25)")
                   , style "border-radius" "1.6cqmin"
                   , style "border" ("0.4cqmin solid rgb(" ++ rgb ++ ")")
                   , style "font-size" "2.2cqmin"
                   , style "font-weight" "bold"
                   , style "color" ("rgb(" ++ rgb ++ ")")
                   , style "padding" "0.8cqmin"
                   , style "box-sizing" "border-box"
                   ]
            )
            [ text (permutationLabel perm) ]
        ]
port module Animation.WAAPI.TransformOrder.Main exposing (main)

import Anim.Builder exposing (AnimBuilder)
import Anim.Engine.WAAPI as WAAPI
import Anim.Extra.TransformOrder as TransformProperty exposing (TransformProperty(..))
import Anim.Property.Rotate as Rotate
import Anim.Property.Scale as Scale
import Anim.Property.Skew as Skew
import Anim.Property.Translate as Translate
import Anim.Unit exposing (Unit(..))
import Browser
import Html exposing (Html, button, div, text)
import Html.Attributes exposing (class, style)
import Html.Events exposing (onClick)
import Json.Encode as Encode
import Motion.Easing as Easing exposing (Easing(..))



-- PORTS


port motionCmd : Encode.Value -> Cmd msg


port motionMsg : (Encode.Value -> msg) -> Sub msg



-- MAIN


main : Program () Model Msg
main =
    Browser.element
        { init = \_ -> init
        , view = view
        , update = update
        , subscriptions = subscriptions
        }



-- MODEL


type alias Model =
    { animState : WAAPI.AnimState Msg
    , animatedBoxes : List Permutation
    }


init : ( Model, Cmd Msg )
init =
    ( { animState =
            WAAPI.init motionCmd motionMsg <|
                List.concatMap
                    (\perm ->
                        [ Translate.initXY (permutationKey perm) 0 0 >> Translate.cssUnitX Cqw >> Translate.cssUnitY Cqh
                        , Skew.initXY (permutationKey perm) 0 0
                        ]
                    )
                    allPermutations
      , animatedBoxes = []
      }
    , Cmd.none
    )


boxSize : Float
boxSize =
    16


boxSizeCss : String
boxSizeCss =
    String.fromFloat boxSize ++ "cqmin"


type Permutation
    = TRSkS
    | TSkRS
    | RTSkS
    | SkTRS
    | STRSk
    | RSkTS


allPermutations : List Permutation
allPermutations =
    [ TRSkS, TSkRS, RTSkS, SkTRS, STRSk, RSkTS ]


permutationKey : Permutation -> String
permutationKey perm =
    case perm of
        TRSkS ->
            "t-r-sk-s"

        TSkRS ->
            "t-sk-r-s"

        RTSkS ->
            "r-t-sk-s"

        SkTRS ->
            "sk-t-r-s"

        STRSk ->
            "s-t-r-sk"

        RSkTS ->
            "r-sk-t-s"


permutationLabel : Permutation -> String
permutationLabel perm =
    case perm of
        TRSkS ->
            "T → R → Sk → S"

        TSkRS ->
            "T → Sk → R → S"

        RTSkS ->
            "R → T → Sk → S"

        SkTRS ->
            "Sk → T → R → S"

        STRSk ->
            "S → T → R → Sk"

        RSkTS ->
            "R → Sk → T → S"


permutationOrder : Permutation -> List TransformProperty
permutationOrder perm =
    case perm of
        TRSkS ->
            [ Translate, Rotate, Skew, Scale ]

        TSkRS ->
            [ Translate, Skew, Rotate, Scale ]

        RTSkS ->
            [ Rotate, Translate, Skew, Scale ]

        SkTRS ->
            [ Skew, Translate, Rotate, Scale ]

        STRSk ->
            [ Scale, Translate, Rotate, Skew ]

        RSkTS ->
            [ Rotate, Skew, Translate, Scale ]


permutationColor : Permutation -> String
permutationColor perm =
    case perm of
        TRSkS ->
            "59, 130, 246"

        TSkRS ->
            "16, 185, 129"

        RTSkS ->
            "245, 158, 11"

        SkTRS ->
            "239, 68, 68"

        STRSk ->
            "139, 92, 246"

        RSkTS ->
            "236, 72, 153"



-- ANIMATION


moveOut : AnimBuilder eng -> AnimBuilder eng
moveOut =
    Translate.begin
        >> Translate.toXY 24 10
        >> Translate.end
        >> Rotate.begin
        >> Rotate.toZ 45
        >> Rotate.end
        >> Skew.begin
        >> Skew.toXY 15 9
        >> Skew.end
        >> Scale.begin
        >> Scale.toXY 1.5 0.8
        >> Scale.end


reset : AnimBuilder eng -> AnimBuilder eng
reset =
    Translate.begin
        >> Translate.toXY 0 0
        >> Translate.end
        >> Rotate.begin
        >> Rotate.toZ 0
        >> Rotate.end
        >> Skew.begin
        >> Skew.toXY 0 0
        >> Skew.end
        >> Scale.begin
        >> Scale.toXY 1 1
        >> Scale.end


engineDefaults : Permutation -> WAAPI.EngineBuilder -> WAAPI.EngineBuilder
engineDefaults perm =
    WAAPI.for (permutationKey perm)
        >> WAAPI.transformOrder (permutationOrder perm)
        >> WAAPI.duration 2000
        >> WAAPI.easing EaseInOut
        >> WAAPI.cssUnitX Cqw
        >> WAAPI.cssUnitY Cqh



-- UPDATE


type Msg
    = Animate Permutation
    | Reset Permutation
    | AnimateAll
    | ResetAll
    | GotWaapiMsg WAAPI.AnimMsg


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        GotWaapiMsg subMsg ->
            let
                ( newAnimState, _ ) =
                    WAAPI.update subMsg model.animState
            in
            ( { model | animState = newAnimState }
            , Cmd.none
            )

        Animate perm ->
            let
                ( newAnimState, animCmd ) =
                    WAAPI.animate model.animState <|
                        engineDefaults perm
                            >> moveOut
            in
            ( { model
                | animState = newAnimState
                , animatedBoxes =
                    if List.member perm model.animatedBoxes then
                        model.animatedBoxes

                    else
                        perm :: model.animatedBoxes
              }
            , animCmd
            )

        Reset perm ->
            let
                ( newAnimState, animCmd ) =
                    WAAPI.animate model.animState <|
                        engineDefaults perm
                            >> reset
            in
            ( { model
                | animState = newAnimState
                , animatedBoxes =
                    List.filter ((/=) perm) model.animatedBoxes
              }
            , animCmd
            )

        AnimateAll ->
            let
                ( finalState, cmd ) =
                    WAAPI.animate model.animState <|
                        List.foldl
                            (\perm acc ->
                                engineDefaults perm
                                    >> moveOut
                                    >> acc
                            )
                            identity
                            allPermutations
            in
            ( { model | animState = finalState, animatedBoxes = allPermutations }
            , cmd
            )

        ResetAll ->
            let
                ( finalState, cmd ) =
                    WAAPI.animate model.animState <|
                        List.foldl
                            (\perm acc ->
                                engineDefaults perm
                                    >> reset
                                    >> acc
                            )
                            identity
                            model.animatedBoxes
            in
            ( { model | animState = finalState, animatedBoxes = [] }
            , cmd
            )



-- SUBSCRIPTIONS


subscriptions : Model -> Sub Msg
subscriptions model =
    WAAPI.subscriptions GotWaapiMsg model.animState



-- VIEW


view : Model -> Html Msg
view model =
    div
        [ class "example-stage"
        , style "container-type" "size"
        ]
        [ div [ class "example-controls" ]
            (List.map permButton allPermutations)
        , div [ class "example-controls" ]
            [ actionButton "▶️ All" AnimateAll "#16a34a"
            , actionButton "⏮️ Reset All" ResetAll "#d97706"
            ]
        , animationArea model.animState
        ]


permButton : Permutation -> Html Msg
permButton perm =
    button
        [ onClick (Animate perm)
        , style "padding" "6px 14px"
        , style "border" "none"
        , style "border-radius" "6px"
        , style "background-color" ("rgb(" ++ permutationColor perm ++ ")")
        , style "color" "white"
        , style "font-size" "13px"
        , style "font-weight" "600"
        , style "cursor" "pointer"
        ]
        [ text (permutationLabel perm) ]


actionButton : String -> Msg -> String -> Html Msg
actionButton label msg color =
    button
        [ onClick msg
        , style "padding" "6px 14px"
        , style "border" "none"
        , style "border-radius" "6px"
        , style "background-color" color
        , style "color" "white"
        , style "font-size" "13px"
        , style "font-weight" "600"
        , style "cursor" "pointer"
        ]
        [ text label ]


animationArea : WAAPI.AnimState Msg -> Html Msg
animationArea animState =
    div
        [ class "example-canvas"
        , style "position" "relative"
        , style "background-color" "#ffffff"
        , style "border-radius" "12px"
        , style "box-shadow" "0 4px 8px rgba(0, 0, 0, 0.1)"
        , style "overflow" "hidden"
        ]
        (List.map (animatedBox animState) allPermutations)


animatedBox : WAAPI.AnimState Msg -> Permutation -> Html Msg
animatedBox animState perm =
    let
        rgb =
            permutationColor perm
    in
    div
        [ style "position" "absolute"
        , style "top" "clamp(10px, 2vmin, 16px)"
        , style "left" "50%"
        , style "transform" "translateX(-50%)"
        ]
        [ div
            (WAAPI.attributes (permutationKey perm) animState
                ++ [ style "width" boxSizeCss
                   , style "height" boxSizeCss
                   , style "background-color" ("rgba(" ++ rgb ++ ", 0.25)")
                   , style "border-radius" "1.6cqmin"
                   , style "border" ("0.4cqmin solid rgb(" ++ rgb ++ ")")
                   , style "font-size" "2.2cqmin"
                   , style "font-weight" "bold"
                   , style "color" ("rgb(" ++ rgb ++ ")")
                   , style "padding" "0.8cqmin"
                   , style "box-sizing" "border-box"
                   ]
            )
            [ text (permutationLabel perm) ]
        ]

Usage

View Source Code
import Anim.Engine.Keyframe as Keyframe
import Anim.Extra.TransformOrder exposing (TransformProperty(..))

Keyframe.animate model.animState <|
    Keyframe.for "box"
        >> Keyframe.transformOrder [ Rotate, Translate, Scale ]
        >> ... -- properties
import Anim.Engine.Sub as Sub
import Anim.Extra.TransformOrder exposing (TransformProperty(..))

Sub.animate model.animState <|
    Sub.for "box"
        >> Sub.transformOrder [ Rotate, Translate, Scale ]
        >> ... -- properties
import Anim.Engine.WAAPI as WAAPI
import Anim.Extra.TransformOrder exposing (TransformProperty(..))

WAAPI.animate motionCmd <|
    WAAPI.for "box"
        >> WAAPI.transformOrder [ Rotate, Translate, Scale ]
        >> ... -- properties

Autofill

The Engine will autofill any missing variants from the list in the default order; Translate, Rotate, Skew, Scale.

Examples

  • [] -> [Translate, Rotate, Skew, Scale]
  • [Rotate] -> [Rotate, Translate, Skew, Scale]
  • [Scale] -> [Scale, Translate, Rotate, Skew]
  • [Scale, Translate] -> [Scale, Translate, Rotate, Skew]

Next Steps

Dive into 3D animations.

3D Animations →