Search code examples
csselm

How can I add a transition when changing views in Elm?


I have a SPA written with Elm and styled with CSS. When the view changes, a different render function will be called to render the new view. This will also stop rendering the "old" view.

The next requirement is to make view changes smooth. I want to do this as following:

  • The "old" view must slide out to the left and fade out (i.e. opacity from 1 to 0)
  • The "new" view must slide in from the right and fade in (i.e. opacity from 0 to 1)
  • When the "old" has slid outside of the screen, completely stop rendering that view.

Because of these requirements, for a little moment, the browser should render 2 views at the same time (both the old and the new view). This is where I'm struggling. Adding a transition (using CSS) to the new view is not difficult. However, adding CSS to the "old" view does not really work, because that view is stopped being rendered.

How can I add a nice transition between two views, where both views will be rendered next to each other for a short time and are both sliding from right to left and the opacity of the views change?


Solution

  • This is in general fairly difficult problem in most VDom based frameworks.

    You will need to render the old view and the new view at the same time for a period of time. To be able to do that, you will need to also store a snapshot of the old model as well.

    You can make a wrapper module that looks something like this:

    
    type Animated model
        = NotAnimating model 
        | Animating String model model
    
    
    view : (model -> Html msg) -> Animated model -> Html msg
    view viewFn model =
       case model of 
           NotAnimating m -> viewFn m
           Animating transitionType old new ->
              Html.div [class "animated-transition", class transitionType]
                 [ Html.div [class "transition-out", Html.Attribute.attribute "inert" "inert" ] [ viewFn old ]
                 , Html.div [ class "transition-in" ] [ viewFn new ]
                 ]
    

    Note the inert attribute. That will make the old view not accept user events/focus.

    See also https://github.com/elm/browser/issues/131.