GithubHelp home page GithubHelp logo

confidenceman02 / elm-animate-height Goto Github PK

View Code? Open in Web Editor NEW
2.0 2.0 2.0 283 KB

Animate the height of an elm div to its contents

License: BSD 3-Clause "New" or "Revised" License

Elm 86.56% Makefile 0.76% TypeScript 12.69%

elm-animate-height's Introduction

Elm Animate Height

Lightweight Elm component for animating height using CSS transitions. Slide up/down the element to any specific height.

  • Hook in to the animationstart/animationend event lifecycles.
  • Animate the opacity of your content for slick transitions.

Usage

Set an initial state in your model.

By default, the height of the container is 0px and all content inside of it is hidden.

type alias Model = AnimateHeight.State

init : Model
init = init (identifier "my-unique-id")

Subscribe to AnimateHeight subscriptions.

type alias Model = AnimateHeight.State

subscriptions : Model -> Sub Msg
subscriptions model = 
    Sub.map AnimateMsg (AnimateHeight.subscriptions model)

Set up an update Msg in your update function.

Ignoring the transition for now, the update below shows how to persist the State and map the AnimateHeight.Msg to your programs Cmd Msg.

type alias Model = AnimateHeight.State

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
    AnimateMsg animateMsg ->
        let
            (transition, newState, cmds) = update animateMsg model
        in
        (newState, Cmd.map AnimateHeight cmds)

Respond to the Transition events in your update function.

The Transition events let you know the progress of the animation. The Float values represent the final height of the container.

TransitionStart 200 -- Animation has started and it's final height will be 200px
TransitionEnd 200 -- Animation has ended and it's final height is 200px

These Transition events are handy to find out if your content is visible or hidden.

type alias Model = AnimateHeight.State

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
    AnimateMsg animateMsg ->
        let
            (transition, newState, cmds) = update animateMsg model

            isContentVisible =
              case transition of 
                Just (TransitionEnd pxValue) ->
                  if pxValue == 0 then
                    -- Your content is hidden
                  else 
                    -- Your content is visible
              _ ->
                -- Nothing going on

        in
        (newState, Cmd.map AnimateHeight cmds)

Render your view in the AnimateHeight container.

When the container is at 0px AnimateHeight hides your content and sets the relevant Aria values for accessibility.

type alias Model = AnimateHeight.State

yourCoolView : Html Msg
yourCoolView :
    -- cool view stuff --
    
view : Model -> Msg
view model =
  container
    (make AnimateMsg
      |> content yourCoolView
      |> state model
     )

Set the height of the container in your update function.

We are using auto here which will animate to the height of the content within the container.

If the content is 300px auto will animate the container to 300px.

Check out fixed to animate to a specific height.

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
    case msg of
        ShowContainerStuff ->
            let
              newState = height auto model
            in
            (newState, Cmd.none)

        HideContainerStuff ->
            let
              newState = height (fixed 0) model
            in
            (newState, Cmd.none)

Variants

Switch

For animating height when switching between views check out the Switch variant.

Performance considerations

Animating the height of a container does mean paying a performance cost depending on how much work the browser needs to perform on layout and paint operations.

elm-animate-height uses Css transitions only, which minimises scripting time and ensures the browser is doing the animation calculations.

elm-animate-height's People

Contributors

confidenceman02 avatar dependabot[bot] avatar elm-review-bot avatar

Stargazers

 avatar  avatar

Watchers

 avatar  avatar

elm-animate-height's Issues

Move id to content rather than content container

Context

The id that is queried lives on the animate-height-container not the animate-height-content. This causes issues then the content changes and we want to animate height the the new content height, specifically when the content height is reduced.

Proposed solution

Place the Id on the content so the dynamic height of it is queried rather than the container height.

Animation stops when querying container for AnimationFrame

Issue

When querying the container the calculated height of the container freezes (intended).

For shallow DOM trees this doesn't seem to be an issue as the DOM traversal is fast. For massive DOM trees finding the node does take some time, this was tested by placing a Process.sleep before the DOM query.

Possible solution

Make querying the DOM non-blocking if there is a current animation in progress.

Set height to fixed value at an auto height

Context

When a container is at height auto all dynamically added content that is added to the container automatically increases the height of that container. This can be fine for some views but in general it produces a janky experience as the container snaps to the new content, shifting all content rendered below it.

Proposed solution

Create a way to recalculate the height of a fixed container so it can render to a new height when content is added.

Perhaps a fixedAtAuto could animate the height to a fixed value of the container as if it where auto. This way new content could be added and then calling fixedAtAuto would animate to the new height.

In this scenario the consumer would need to ensure fixedAtAuto be called every time they add new content to the container or else it would not animate.

sizer element position value messes with height accuracy

Background

In order to correctly animate the a height we copy the content that is passed and put it into an invisible sizer div which we poll for height info. That sizer div has its position set to absolute to keep it out the way.

Issue

Because it is position: absolute; it is prone to allow the contents height to be that of any container divs outside of a AnimateHeight container.

Possible solution

Remove the position: absolute; property and just let the sizer div be part of the DOM flow. Its height is set to 0px anyway so it shouldn't cause too many issues.

Width snaps in to place with switch variant

Internally Switch wraps the content in to an absolute positioned div to farm height information from it. If This content has flex or grid content it might shrink whilst inside the absolute positioned div then snap to its actual width when rendered on to the DOM.

Fix

Set width: 100% on the absolute positioned div to ensure content is as wide as it needs to be.

Use hidden attribute

When the content is not meant to be visible we are only setting an aria attribute, height: 0px and opacity: 0 so a consumer doesn't see the content.

Use the hidden attribute when the content is not visible.

Overflow property when height is auto

Context

When auto animating to a height, the css property height is set to auto at the end of the animation to a height > 0. This allows any new dynamic content that shows up to auto adjust the container height.

The overflow property remains hidden however which means that any absolute positioned elements that render proud of the container are cut off.

Proposed solution

Set overflow property to visible when the height property is auto.

fixedAtAuto not working when used with heightAt

Context

The heightAt function allows the user to set the initial height of the container without animating to it.

Current behaviour

Using heightAt with fixedAtAuto doesn't set the height to a fixed value, instead it sets it to auto.

Expected behaviour

When using heightAt with fixedAtAuto I would expect the container to be set at the fixed height of auto without any animations.

state |> heightAt fixedAtAuto

Interrupt/recalculation duration discrepancy between Transition and AnimationFrame

Issue

During interrupts and recalculations durations are calculated. With AnimationFrame we calculate a new duration based off of the current height. It is trickier to do this with Transition as we don't track a current height.

Because of this the interrupt/recalculation durations that are calculated are different between Transition and AnimationFrame strategies.

Possible solutions

Transition css duration values change dynamically during recalculations and interrupts. Calculating these values the same as the AnimationFrame values will populate the duration css value automatically.

Recalculating min height then firing a toMinHeight Transition/AnimationFrame

On occasion When recalculating the container to min height then calling toMinHeight during the recalculation, the container doesn't resolve to its lowest position. Instead it snaps to the container content height.

For Transition, I suspect the issue is because the time keeping does not match the css transition perfectly. May need a small time extension.

Animate height between multiple views

Context

It very common to want to render different views in the same AnimateHeight container. To ensure the user has a great experience and not have to deal with content height snapping, and assuming they do not have any reduced motion requirements, a smooth transition between heights is by far the best and smoothest experience.

For example, cross fading smoothly between any render states that might abruptly change the height of a section are great candidates for this feature.

I don't feel AnimateHeight is overreaching here as the functionality is chiefly one concerning height.

Container resizing

If the containers content can resize due to a viewport change, the height information will not be current.

For example if a container is at content height at 200px then the user resizes the viewport to make it wider. The content subsequently adjusts to fill the extra width and now takes up 100px. The container will still be set at 200px.

Possible solution

  • Make the container snap to content when it detects a viewport change. Could be expensive, may need to throttle event and only snap on last viewport change after time limit. Gizra/elm-debouncer

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.