Skip to content

Instantly share code, notes, and snippets.

@m9iv
Created July 24, 2017 10:07
Show Gist options
  • Save m9iv/a3541b0f78f2aae7561aa4dceb6c78ab to your computer and use it in GitHub Desktop.
Save m9iv/a3541b0f78f2aae7561aa4dceb6c78ab to your computer and use it in GitHub Desktop.
Good example of Elm animation

From here: https://groups.google.com/forum/#!topic/elm-discuss/bg7p-6W1F6c

type alias Item = 
  { content : String
  , id : Int 
  }

type AnimState = Static | IsHidden Int | IsEntering Int

type Msg = AddItem Item | DisplayedHidden Int Time | TransitionEnded Int

type Model =
  { items : List Item
  , animState : AnimState
  }

update msg model =
  case msg of
    AddItem item ->
      { model 
      | items = model.items ++ [ item ]
      , animState = IsHidden item.id
      } ! []
    DisplayedHidden itemId _ ->
      { model
      | animState = IsEntering itemId
      } ! []
    TransitionEnded itemId ->
      { model
      | animState = Static
      } ! []

view model =
  ul []
    List.map (itemView model) model.items

itemView model item =
  let
    itemAttributes =
      case model.animState of
        Static ->
          [ class "static" ]

        IsHidden itemId ->
          if item.id == itemId then
            [ class "hidden" ]
          else
            [ class "static" ]

        IsEntering itemId ->
          if item.id == itemId then
            [ class "entering", onTransitionEnd (TransitionEnded itemId) ]
          else
            [ class "static" ]
  in
    li
      itemAttributes
      [ text item.content ]

onTransitionEnd msg = 
  Html.Events.on "transitionend" (Json.succeed msg)

subscriptions model =
  case model.animState of
    IsHidden itemId ->
      AnimationFrame.times (DisplayedHidden itemId)

    _ ->
      Sub.none

In your css, you would have the following classes for the item:

  • ".hidden" that does NOT use "display: none", but some other attributes (e.g. opacity, height, or transform: translate) to make the item invisible.
  • ".entering", which includes a "transition: xx ..ms" to do the animation from .hidden to .entering
  • ".static", which has the styling for after the animation.

Some remarks here:

  • you need a subscription for AnimationFrame, to ensure that elm has at least rendered once on screen before the next class is applied.
  • the transitionend (at least in Chrome) fires each time one element of the transition has ended, so if your css-transition has two animating elements (e.g. opacity and position), the transitionend will fire as soon as the first transition has ended
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment