Use scroll position to drive animation progress.
Animations run automatically as the user scrolls, 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 ScrollTimeline Engine Documentation and the Engine Overview.
Type alias for the animation group name.
Select the animation group that subsequent property builders will target.
ScrollTimeline.for "hero-card"
>> Opacity.begin
>> Opacity.from 0
>> Opacity.to 1
>> Opacity.end📖 See Triggering Animations for details.
Identifies the scroll surface handled by the engine.
Use Document for the document body, or Container "element-id" for a
specific scrollable element.
Fire-and-forget scroll-driven animation using the browser's ScrollTimeline.
port motionCmd : Encode.Value -> Cmd msg
ScrollTimeline.animate motionCmd (Container "scroller") <|
ScrollTimeline.for "hero-card"
>> Opacity.begin
>> Opacity.from 0
>> Opacity.to 1
>> Opacity.end📖 See Event Reference for details.
Lifecycle events from the ScrollTimeline engine.
Started String — the scroll position entered the animation range. Fires every
time the timeline re-enters range, not just on the first entry.Ended String — the scroll position reached the end of the animation rangeCancelled String — the animation was cancelled (e.g. element removed)Iteration String Int — the animation looped; the Int is the cumulative iteration countProgress 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 decodedOpt 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.
ScrollTimeline.withProgressEvents True -- global setting
>> ScrollTimeline.for "box"
>> ScrollTimeline.withProgressEvents False -- overrides global for this group
>> ... -- other buildersSet 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.
0 (the default) to emit on every requestAnimationFrame tick.16 for ~60 Hz, 33 for ~30 Hz.Note: Higher throttle values reduce Elm-side mid-flight precision for queries and interruption bookkeeping.
ScrollTimeline.setUpdateThrottle 33 -- global default
>> ScrollTimeline.for "hero"
>> ScrollTimeline.setUpdateThrottle 0 -- dense updates for interactions
>> ScrollTimeline.for "background"
>> ScrollTimeline.setUpdateThrottle 50 -- lower traffic for passive motion
>> ... -- other builders📖 See React for details.
Message type used with update.
type Msg
= GotScrollMsg ScrollTimeline.AnimMsg
| ...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
GotScrollMsg animMsg ->
case ScrollTimeline.update animMsg of
Just (ScrollTimeline.Ended animGroup) ->
...
_ ->
( model, Cmd.none )📖 See Subscriptions for details.
Subscribe to lifecycle events for this engine.
This is only required if you want to receive events.
subscriptions : Model -> Sub Msg
subscriptions _ =
ScrollTimeline.subscriptions GotScrollMsg motionMsg📖 See Render for details.
Attach the animation group identifier to an element.
div (ScrollTimeline.attributes "hero-card") [ ... ]Use horizontal scroll as the timeline source.
Vertical scroll is the default, so this is only needed when the container scrolls horizontally.
-- Animate based on horizontal scroll position in a carousel
ScrollTimeline.animate motionCmd (Container "carousel") <|
ScrollTimeline.horizontal
>> ScrollTimeline.for "slide"
>> Opacity.begin
>> Opacity.from 0
>> Opacity.to 1
>> Opacity.endThese 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.
ScrollTimeline.iterations 3 -- global setting
>> ScrollTimeline.for "box"
>> ScrollTimeline.iterations 5 -- overrides global for this group
>> ... -- other buildersSet how many times an animation should repeat.
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.
📖 See Easing for details.
Set the global easing function.
heroEntrance : EngineBuilder -> EngineBuilder
heroEntrance =
easing EaseInOut
>> fadeInHeroTitle
>> slideInHeroArtwork
>> revealPrimaryCtaSet the global spring.
draggableCardSettle : AnimBuilder { eng | withSpring : () } -> AnimBuilder { eng | withSpring : () }
draggableCardSettle =
spring Spring.wobbly
>> settleCardPosition
>> settleCardShadowSet the default length unit for all length-bearing properties.
responsivePanelMotion : AnimBuilder { eng | withCssUnit : () } -> AnimBuilder { eng | withCssUnit : () }
responsivePanelMotion =
cssUnit Unit.Vw
>> slidePanelIn
>> growPanelHeightSet the default length unit for the X axis.
responsiveDrawerMotion : AnimBuilder { eng | withCssUnit : () } -> AnimBuilder { eng | withCssUnit : () }
responsiveDrawerMotion =
cssUnitX Unit.Vw
>> slideDrawerX
>> alignDrawerLabelXSet the default length unit for the Y axis.
responsiveSheetMotion : AnimBuilder { eng | withCssUnit : () } -> AnimBuilder { eng | withCssUnit : () }
responsiveSheetMotion =
cssUnitY Unit.Vh
>> slideSheetY
>> alignSheetHeaderYSet the default length unit for the Z axis.
layeredSceneMotion : AnimBuilder { eng | withCssUnit : () } -> AnimBuilder { eng | withCssUnit : () }
layeredSceneMotion =
cssUnitZ Unit.Px
>> pushSceneBackgroundBack
>> bringFloatingCardForwardSet the default length unit used for width values in ScrollTimeline animations.
responsiveCardWidth : EngineBuilder -> EngineBuilder
responsiveCardWidth =
cssUnitWidth Unit.Vw
>> growCardWidth
>> settleCardSpacingSet the default length unit used for height values in ScrollTimeline animations.
responsivePanelHeight : EngineBuilder -> EngineBuilder
responsivePanelHeight =
cssUnitHeight Unit.Vh
>> expandPanelHeight
>> alignPanelHeaderYSet 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.
ScrollTimeline.animate motionCmd (Container "scroller") <|
ScrollTimeline.discreteEntry "display" "block"
>> ScrollTimeline.discreteEntry "visibility" "visible"
>> ScrollTimeline.for "box"
>> Opacity.begin
>> Opacity.from 0
>> Opacity.to 1
>> Opacity.endAdd 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.ScrollTimeline as ScrollTimeline
import Anim.Property.Opacity as Opacity
ScrollTimeline.animate motionCmd (Container "scroller") <|
ScrollTimeline.discreteExit "display" "block" "none"
>> ScrollTimeline.for "box"
>> Opacity.begin
>> Opacity.to 0
>> Opacity.endSet 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(..))
ScrollTimeline.animate motionCmd (Container "scroller") <|
ScrollTimeline.transformOrder [ Scale, Rotate, Translate, Skew ] -- global setting
>> ScrollTimeline.for "box"
>> ScrollTimeline.transformOrder [ Rotate, Translate ] -- overrides global for this group
>> Translate.begin
>> Translate.fromXY 0 0
>> Translate.toXY 100 0
>> Translate.end
Builder type for scroll-driven animations.