phollyer/elm-motion/1.0.1

NOTE: the latest version is 3.0.4

Anim.Engine.ViewTimeline

Use an element's position in the viewport to drive animation progress.

Animations run automatically as the element moves through view, so you do not need an AnimState. update and subscriptions are optional and only matter when you want events.

📖 For setup, browser support, and examples, see the ViewTimeline Engine Documentation and the Engine Overview.

Types

type alias EngineBuilder =
EngineBuilder

Builder type for view-driven animations.

Use this in type annotations when a builder function should only work with ViewTimeline.

📖 See Engine Capabilities for patterns and examples.

type alias AnimGroupName =
String

Type alias for the animation group name.

Target

for : AnimGroupName -> EngineBuilder -> EngineBuilder

Select the animation group that subsequent property builders will target.

ViewTimeline.for "hero-card"
    >> Opacity.begin
    >> Opacity.from 0
    >> Opacity.to 1
    >> Opacity.end

Trigger

📖 See Triggering Animations for details.

animate : (Value -> Cmd msg) -> (EngineBuilder -> EngineBuilder) -> Cmd msg

Fire-and-forget view-driven animation using the browser's ViewTimeline.

port motionCmd : Encode.Value -> Cmd msg


ViewTimeline.animate motionCmd <|
    ViewTimeline.for "hero-card"
        >> Opacity.begin
        >> Opacity.from 0
        >> Opacity.to 1
        >> Opacity.end

Events

📖 See Event Reference for details.

= Run AnimGroupName
| Started AnimGroupName
| Ended AnimGroupName
| Cancelled AnimGroupName Float
| Iteration AnimGroupName Int
| Progress AnimGroupName Float
| AnimError String

Lifecycle events from the ViewTimeline engine.

  • Started String — the element entered the animation range. Fires every time the element re-enters range, not just on the first entry.
  • Ended String — the element scrolled past the end of the animation range
  • Cancelled String — the animation was cancelled (e.g. element removed)
  • Iteration String Int — the animation looped; the Int is the cumulative iteration count
  • Progress String Float — the timeline's current progress (0..1) while in range. Only emitted when withProgressEvents True was set on the builder.
  • AnimError String — a message arrived but could not be decoded

Progress Events

withProgressEvents : Bool -> EngineBuilder -> EngineBuilder

Opt in to per-frame Progress events.

Off by default.

Progress events can create a lot of noise in your update loop, especially when debugging the Msg flow in your app. Therefore they are suppressed by default and you need to explicitly opt in to receive them.

This is a precedence function, so it can operate as a global setting for all groups in the builder chain, or you can set it on a per-group basis which overrides any global setting for that group.

ViewTimeline.withProgressEvents True -- global setting
    >> ViewTimeline.for "box"
    >> ViewTimeline.withProgressEvents False -- overrides global for this group
    >> ... -- other builders
setUpdateThrottle : Int -> EngineBuilder -> EngineBuilder

Set the minimum interval in milliseconds between per-frame propertyUpdate emissions from the JavaScript runtime.

The visual animation is driven by the browser compositor and is never affected by this setting - it only controls how much port traffic is generated.

This is a precedence function, so it can operate as a global setting for all groups in the builder chain, or you can set it on a per-group basis which overrides any global setting for that group.

  • Pass 0 (the default) to emit on every requestAnimationFrame tick.
  • Pass a positive number of milliseconds to cap the emission rate, e.g. 16 for ~60 Hz, 33 for ~30 Hz.

Note: Higher throttle values reduce Elm-side mid-flight precision for queries and interruption bookkeeping.

ViewTimeline.setUpdateThrottle 33 -- global default
    >> ViewTimeline.for "hero"
    >> ViewTimeline.setUpdateThrottle 0 -- dense updates for interactions
    >> ViewTimeline.for "background"
    >> ViewTimeline.setUpdateThrottle 50 -- lower traffic for passive motion
    >> ... -- other builders

Update

📖 See React for details.

type alias AnimMsg =
AnimMsg

Message type used with update.

type Msg
    = GotViewMsg ViewTimeline.AnimMsg
    | ...
update : AnimMsg -> Maybe AnimEvent

Turn an engine message into an event.

Messages that do not belong to this engine return Nothing.

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        GotViewMsg animMsg ->
            case ViewTimeline.update animMsg of
                Just (ViewTimeline.Ended animGroup) ->
                    ...


                _ ->
                    ( model, Cmd.none )

Subscriptions

📖 See Subscriptions for details.

subscriptions : (AnimMsg -> msg) -> ((Value -> msg) -> Sub msg) -> Sub msg

Subscribe to lifecycle events for this engine.

This is only required if you want to receive events.

subscriptions : Model -> Sub Msg
subscriptions _ =
    ViewTimeline.subscriptions GotViewMsg motionMsg

View

📖 See Render for details.

attributes : AnimGroupName -> List (Attribute msg)

Attach the animation group identifier to an element.

div (ViewTimeline.attributes "hero-card") [ ... ]

Axis

horizontal : EngineBuilder -> EngineBuilder

Use horizontal viewport tracking for the timeline.

Vertical scroll is the default, so this is only needed when the container scrolls horizontally.

-- Animate an element entering from the side in a horizontal layout
ViewTimeline.animate motionCmd <|
    ViewTimeline.horizontal
        >> ViewTimeline.for "slide"
        >> Opacity.begin
        >> Opacity.from 0
        >> Opacity.to 1
        >> Opacity.end

Range

📖 See Range for details.

type Unit
= Perc
| Px

The unit for a Range offset value.

  • Perc — percentage of the named range (Cover 20 Perc → cover 20%)
  • Px — fixed pixel offset (Cover 100 Px → cover 100px)
type Range
= Cover Float Unit
| Contain Float Unit
| Entry Float Unit
| EntryCrossing Float Unit
| Exit Float Unit
| ExitCrossing Float Unit
| Scroll Float Unit

A position along the view timeline, used to configure rangeStart and rangeEnd.

Each constructor takes a numeric value and a Unit:

rangeStart (Entry 0 Perc) -- entry 0%


rangeEnd (Exit 100 Px) -- exit 100px
rangeStart : Range -> EngineBuilder -> EngineBuilder

Set when the animation starts relative to the element's position in the viewport.

Optional — defaults to Cover 0 Perc when not called.

This is a precedence function, so it can act as a global default for the whole builder chain or be set after for to override the range for a specific group.

-- Start animating as the element enters the viewport
ViewTimeline.rangeStart (Entry 0 Perc)
    >> ViewTimeline.for "section-a"
    -- Give one section its own range inside the same pipeline
    >> ViewTimeline.rangeStart (Entry 10 Perc)
rangeEnd : Range -> EngineBuilder -> EngineBuilder

Set when the animation ends relative to the element's position in the viewport.

Optional — defaults to Cover 100 Perc when not called.

This is a precedence function, so it can act as a global default for the whole builder chain or be set after for to override the range for a specific group.

-- End animating as the element begins to leave the viewport
ViewTimeline.rangeEnd (Exit 0 Perc)
    >> ViewTimeline.for "section-a"
    -- Give one section its own range inside the same pipeline
    >> ViewTimeline.rangeEnd (Exit 60 Perc)

Playback

These functions are precedence functions, so they can operate as a global setting for all groups in the builder chain, or you can set them on a per-group basis which overrides any global setting for that group.

ViewTimeline.iterations 3 -- global setting
    >> ViewTimeline.for "box"
    >> ViewTimeline.iterations 5 -- overrides global for this group
    >> ... -- other builders
iterations : Int -> EngineBuilder -> EngineBuilder

Set how many times an animation should repeat.

alternate : EngineBuilder -> EngineBuilder

Make an animation alternate direction on each iteration.

alternate only has a visible effect when the animation runs more than once, so calling it when iterations is unset or 1 automatically bumps iterations to 2. An explicit iterations count set before or after alternate is preserved.

Easing

📖 See Easing for details.

easing : Easing -> EngineBuilder -> EngineBuilder

Set the global easing function.

heroEntrance : AnimBuilder { eng | withEasing : () } -> AnimBuilder { eng | withEasing : () }
heroEntrance =
    easing EaseInOut
        >> fadeInHeroTitle
        >> slideInHeroArtwork
        >> revealPrimaryCta

Spring

spring : Spring -> EngineBuilder -> EngineBuilder

Set the global spring.

draggableCardSettle : AnimBuilder { eng | withSpring : () } -> AnimBuilder { eng | withSpring : () }
draggableCardSettle =
    spring Spring.wobbly
        >> settleCardPosition
        >> settleCardShadow

Unit

cssUnit : Unit -> EngineBuilder -> EngineBuilder

Set the default length unit for all length-bearing properties.

responsivePanelMotion : AnimBuilder { eng | withCssUnit : () } -> AnimBuilder { eng | withCssUnit : () }
responsivePanelMotion =
    cssUnit Unit.Vw
        >> slidePanelIn
        >> growPanelHeight
cssUnitX : Unit -> EngineBuilder -> EngineBuilder

Set the default length unit for the X axis.

responsiveDrawerMotion : AnimBuilder { eng | withCssUnit : () } -> AnimBuilder { eng | withCssUnit : () }
responsiveDrawerMotion =
    cssUnitX Unit.Vw
        >> slideDrawerX
        >> alignDrawerLabelX
cssUnitY : Unit -> EngineBuilder -> EngineBuilder

Set the default length unit for the Y axis.

responsiveSheetMotion : AnimBuilder { eng | withCssUnit : () } -> AnimBuilder { eng | withCssUnit : () }
responsiveSheetMotion =
    cssUnitY Unit.Vh
        >> slideSheetY
        >> alignSheetHeaderY
cssUnitZ : Unit -> EngineBuilder -> EngineBuilder

Set the default length unit for the Z axis.

layeredSceneMotion : AnimBuilder { eng | withCssUnit : () } -> AnimBuilder { eng | withCssUnit : () }
layeredSceneMotion =
    cssUnitZ Unit.Px
        >> pushSceneBackgroundBack
        >> bringFloatingCardForward
cssUnitWidth : Unit -> EngineBuilder -> EngineBuilder

Set the default length unit used for width values in ViewTimeline animations.

responsiveCardWidth : EngineBuilder -> EngineBuilder
responsiveCardWidth =
    cssUnitWidth LengthUnit.Vw
        >> growCardWidth
        >> settleCardSpacing
cssUnitHeight : Unit -> EngineBuilder -> EngineBuilder

Set the default length unit used for height values in ViewTimeline animations.

responsivePanelHeight : EngineBuilder -> EngineBuilder
responsivePanelHeight =
    cssUnitHeight LengthUnit.Vh
        >> expandPanelHeight
        >> alignPanelHeaderY

Discrete Properties

discreteEntry : String -> String -> EngineBuilder -> EngineBuilder

Set a discrete CSS property to hold during the animation.

Used for non-interpolatable properties like display or visibility that need to be set to a specific value while the animation is active.

This function is a precedence function, so it can operate as a global setting for all groups in the builder chain, or you can set it on a per-group basis which overrides any global setting for that group.

ViewTimeline.animate motionCmd <|
    ViewTimeline.discreteEntry "display" "block"
        >> ViewTimeline.for "box"
        >> ViewTimeline.discreteEntry "visibility" "visible"
        >> Opacity.begin
        >> Opacity.from 0
        >> Opacity.to 1
        >> Opacity.end
discreteExit : String -> String -> String -> EngineBuilder -> EngineBuilder

Add a discrete CSS property for exit animations.

Exit animations need to hold their initial state until the very end of the animation, at which point they flip to the final state. Therefore you need to set both entry and exit values for the property.

This function is a precedence function, so it can operate as a global setting for all groups in the builder chain, or you can set it on a per-group basis which overrides any global setting for that group.

Use when an element is disappearing (e.g., going from display: block to display: none).

import Anim.Engine.ViewTimeline as ViewTimeline
import Anim.Property.Opacity as Opacity


ViewTimeline.animate motionCmd <|
    ViewTimeline.discreteExit "display" "block" "none"
        >> ViewTimeline.for "box"
        >> Opacity.begin
        >> Opacity.to 0
        >> Opacity.end

Transform Order

transformOrder : List TransformProperty -> EngineBuilder -> EngineBuilder

Set the transform order.

The transform order specifies how translate, rotate, skew and scale transforms are combined. Start the list with the transform to apply first.

This is a precedence function, so it can operate as a global setting for all groups in the builder chain, or you can set it on a per-group basis which overrides any global setting for that group.

Any missing transforms are automatically appended in the default order (Translate → Rotate → Skew → Scale).

import Anim.Extra.TransformOrder exposing (TransformProperty(..))


ViewTimeline.animate motionCmd <|
    ViewTimeline.transformOrder [ Scale, Rotate, Translate, Skew ] -- global setting
        >> ViewTimeline.for "box"
        >> ViewTimeline.transformOrder [ Rotate, Translate ] -- overrides global for this group
        >> Translate.begin
        >> Translate.fromXY 0 0
        >> Translate.toXY 100 0
        >> Translate.end