Animation Examples¶
Interactive examples demonstrating Elm Motion capabilities.
Each example uses the exact same animation for each of the Engines - quickly compare the behaviour of each Engine for different animation types.
Hello Text¶
Fades in text when the page loads. The obligatory "Hello" example.
View Example
Pulsing Dot¶
A dot in the middle of the screen that pulses by scaling and fading in and out - looping forever and alternating direction on each iteration.
View Example
Fade In/Out¶
Fade an element in and out with buttons.
View Examples
Button Hovers¶
Three different hover effects.
View Examples
Note how animating Size causes browser reflow and repaint; as the button grows and shrinks, it affects the layout of surrounding elements. In contrast, Scale and Translate have no effect on the surrounding elements. More on this later.
Interrupting Animations¶
Trigger an animation, then interrupt it mid-flight.
Single Property¶
View Examples
✅ Behaviour: Smooth redirect from current mid-flight value to new end target value
❌ Behaviour: The new @keyframes rules for the animation replace the existing rules.
📖 See: Keyframe Engine — Interrupting Animations for details.
✅ Behaviour: Smooth redirect from current mid-flight value to new end target value
✅ Behaviour: Smooth redirect from current mid-flight value to new end target value
Multiple Properties¶
View Examples
✅ Behaviour: Translate and CustomColor (BackgroundColor) run independently side by side.
❌ Behaviour: The new @keyframes rules for the animation replace the existing rules.
📖 See: Keyframe Engine — Interrupting Animations for details.
✅ Behaviour: Translate and CustomColor (BackgroundColor) run independently side by side.
✅ Behaviour: Translate and CustomColor (BackgroundColor) run independently side by side.
Multiple Axes¶
View Examples
✅ Behaviour: Smooth redirect from current position
❌ Behaviour: The new @keyframes rules for the animation replace the existing rules.
📖 See: Keyframe Engine — Interrupting Animations for details.
✅ Behaviour: Seamless interruption to new target
✅ Behaviour: Seamless interruption to new target
Freeze Axis¶
View Examples
✅ Behaviour: Frozen axis holds its current position while the other axis animates to the new target
✅ Behaviour: Frozen axis holds its current position while the other axis animates to the new target
Controlling Animations¶
Control the ball animation with the buttons.
View Examples
Discrete Properties¶
Show and hide elements with smooth fades using discrete CSS properties like display.
View Examples
3D Animations¶
Rotating cube with expanding sides.
View Examples
Transform Order¶
There are 6 boxes in the center, each one is triggered with the same animation, the only difference is the transform order.
View Examples
Custom Properties¶
Border Radius¶
Animate border-radius using the generic Anim.Property.Custom module.
View Example
Border Color¶
Animate border-color using the generic Anim.Property.CustomColor module.
View Example
Timeline Engines¶
Scroll-driven animation examples using the dedicated timeline engines.
ScrollTimeline Engine¶
Scroll the page, and the progress bar will animate in response.
View Example
View Source Code
port module Animation.ScrollTimeline.Main exposing (main)
import Anim.Builder exposing (AnimBuilder)
import Anim.Engine.ScrollTimeline as ScrollTimeline exposing (Container(..))
import Anim.Extra.Color as Color exposing (Color)
import Anim.Property.CustomColor as CustomColor exposing (ColorProperty(..))
import Anim.Property.Scale as Scale
import Browser
import Html exposing (Html, div, h2, p, span, text)
import Html.Attributes exposing (class, id, style)
import Json.Encode as Encode
import Motion.Easing as Easing exposing (Easing(..))
-- PORTS
port motionCmd : Encode.Value -> Cmd msg
-- MAIN
main : Program () () msg
main =
Browser.element
{ init = \_ -> init
, view = view
, update = \_ model -> ( model, Cmd.none )
, subscriptions = always Sub.none
}
-- ANIMATION
progressBarAnim : String
progressBarAnim =
"scrollProgress"
scrollProgress : ScrollTimeline.EngineBuilder -> ScrollTimeline.EngineBuilder
scrollProgress =
ScrollTimeline.for progressBarAnim
>> Scale.begin
>> Scale.fromX 0
>> Scale.toX 1
>> Scale.end
>> CustomColor.begin BackgroundColor
>> CustomColor.from Color.red
>> CustomColor.to Color.green
>> CustomColor.end
-- INIT
init : ( (), Cmd msg )
init =
( ()
, ScrollTimeline.animate motionCmd Document scrollProgress
)
-- VIEW
view : () -> Html msg
view _ =
div
[ style "font-family" "system-ui, sans-serif"
, style "color" "#1f2937"
, style "background" "#ffffff"
]
[ -- Fixed progress bar at top of page
div
[ style "position" "fixed"
, style "top" "0"
, style "left" "0"
, style "width" "100%"
, style "height" "5px"
, style "background" "#e5e7eb"
, style "z-index" "100"
]
[ div
(ScrollTimeline.attributes progressBarAnim
++ [ style "width" "100%"
, style "height" "100%"
, style "transform-origin" "left center"
, style "transform" "scaleX(0)"
]
)
[]
]
, div
[ style "text-align" "center"
, style "padding" "clamp(48px, 10vh, 80px) clamp(20px, 5vw, 40px) clamp(28px, 6vh, 40px)"
, style "background" "linear-gradient(135deg, #ede9fe, #ddd6fe)"
]
[ h2
[ style "font-size" "clamp(1.6rem, 4vw + 0.8rem, 2.5rem)"
, style "font-weight" "700"
, style "margin" "0 0 16px"
, style "color" "#4c1d95"
]
[ text "Scroll Timeline" ]
, p
[ style "font-size" "clamp(0.95rem, 1vw + 0.6rem, 1.1rem)"
, style "color" "#6d28d9"
, style "margin" "0"
]
[ text "Scroll down and watch the bar fill as the page moves from top to bottom." ]
]
-- Scrollable content cards
, div
[ style "max-width" "700px"
, style "margin" "0 auto"
, style "padding" "clamp(36px, 8vh, 60px) clamp(16px, 5vw, 40px)"
, style "display" "flex"
, style "flex-direction" "column"
, style "gap" "clamp(28px, 6vh, 60px)"
]
(List.map contentCard cards)
]
type alias CardData =
{ label : String
, color : String
, title : String
, body : String
}
cards : List CardData
cards =
[ { label = "01"
, color = "#6366f1"
, title = "Top to bottom"
, body = "The timeline starts at 0% at the top of the page and reaches 100% at the bottom."
}
, { label = "02"
, color = "#8b5cf6"
, title = "One scroll, two effects"
, body = "The same timeline drives both size and color, so one scroll gesture controls the whole bar."
}
, { label = "03"
, color = "#a78bfa"
, title = "Read progress at a glance"
, body = "Short red bar means early in the page, long green bar means you are near the end."
}
, { label = "04"
, color = "#7c3aed"
, title = "Simple trigger"
, body = "Call ScrollTimeline.animate once in init, then the browser keeps everything in sync while you scroll."
}
, { label = "05"
, color = "#5b21b6"
, title = "Easy to reuse"
, body = "Attach ScrollTimeline.attributes to any element and map scroll progress to the properties you want."
}
]
contentCard : CardData -> Html msg
contentCard card =
div
[ style "display" "flex"
, style "gap" "clamp(14px, 3vw, 24px)"
, style "align-items" "flex-start"
, style "padding" "clamp(20px, 4vw, 32px)"
, style "background" "white"
, style "border-radius" "16px"
, style "box-shadow" "0 4px 24px rgba(99,102,241,0.08)"
]
[ span
[ style "font-size" "clamp(1.4rem, 3vw + 0.4rem, 2rem)"
, style "font-weight" "800"
, style "color" card.color
, style "flex-shrink" "0"
, style "line-height" "1"
, style "padding-top" "4px"
]
[ text card.label ]
, div []
[ h2
[ style "font-size" "clamp(1.05rem, 1.5vw + 0.5rem, 1.3rem)"
, style "font-weight" "700"
, style "margin" "0 0 10px"
, style "color" "#111827"
]
[ text card.title ]
, p
[ style "font-size" "clamp(0.9rem, 1vw + 0.5rem, 1rem)"
, style "line-height" "1.7"
, style "color" "#6b7280"
, style "margin" "0"
]
[ text card.body ]
]
]
ViewTimeline Engine¶
Scroll the page, and the different sections will fade in and slide up as they are scrolled into view.
View Example
View Source Code
port module Animation.ViewTimeline.Main exposing (main)
import Anim.Builder exposing (AnimBuilder)
import Anim.Engine.ViewTimeline as ViewTimeline exposing (Range(..), Unit(..))
import Anim.Property.Opacity as Opacity
import Anim.Property.Translate as Translate
import Browser
import Html exposing (Html, div, h2, p, span, text)
import Html.Attributes exposing (class, id, style)
import Json.Encode as Encode
import Motion.Easing as Easing exposing (Easing(..))
-- PORTS
port motionCmd : Encode.Value -> Cmd msg
-- MAIN
main : Program () () msg
main =
Browser.element
{ init = \_ -> init
, view = view
, update = \_ model -> ( model, Cmd.none )
, subscriptions = always Sub.none
}
-- ANIMATION
revealCard : String -> ViewTimeline.EngineBuilder -> ViewTimeline.EngineBuilder
revealCard animGroupName =
ViewTimeline.for animGroupName
>> Opacity.begin
>> Opacity.from 0
>> Opacity.to 1
>> Opacity.end
>> Translate.begin
>> Translate.fromY 100
>> Translate.toY 0
>> Translate.end
-- INIT
init : ( (), Cmd msg )
init =
( ()
, cards
|> List.map
(\card ->
ViewTimeline.animate motionCmd <|
ViewTimeline.rangeStart (Entry 10 Perc)
>> ViewTimeline.rangeEnd (Cover 30 Perc)
>> ViewTimeline.easing BounceInOut
>> revealCard card.animGroupName
)
|> Cmd.batch
)
-- VIEW
view : () -> Html msg
view _ =
div
[ style "font-family" "system-ui, sans-serif"
, style "color" "#1f2937"
, style "background" "#f9fafb"
]
[ -- Page header
div
[ style "text-align" "center"
, style "padding" "80px 40px 60px"
, style "background" "linear-gradient(135deg, #ede9fe, #ddd6fe)"
]
[ h2
[ style "font-size" "2.5rem"
, style "font-weight" "700"
, style "margin" "0 0 16px"
, style "color" "#4c1d95"
]
[ text "View Timeline" ]
, p
[ style "font-size" "1.1rem"
, style "color" "#6d28d9"
, style "margin" "0"
]
[ text "Scroll down - each card animates as it enters the viewport." ]
]
-- Cards
, div
[ style "max-width" "700px"
, style "margin" "0 auto"
, style "padding" "clamp(24px, 6vmin, 60px) clamp(16px, 4.5vmin, 40px)"
, style "display" "flex"
, style "flex-direction" "column"
, style "gap" "clamp(28px, 6vmin, 60px)"
]
(List.map cardView cards)
]
type alias CardData =
{ animGroupName : String
, color : String
, label : String
, title : String
, body : String
}
cards : List CardData
cards =
[ { animGroupName = "view-card-1"
, color = "#6366f1"
, label = "01"
, title = "Enter from below"
, body = "Each card uses a ViewTimeline tied to itself as the scroll subject. As the card enters the viewport, it fades in and slides upward."
}
, { animGroupName = "view-card-2"
, color = "#8b5cf6"
, label = "02"
, title = "Independent timelines"
, body = "Every card has its own ViewTimeline. Animations are fully independent - each one triggers only when that specific card enters the viewport."
}
, { animGroupName = "view-card-3"
, color = "#a78bfa"
, label = "03"
, title = "Range control"
, body = "The rangeStart and rangeEnd functions define exactly when during the card's lifecycle the animation plays - entry, cover, contain, or exit."
}
, { animGroupName = "view-card-4"
, color = "#7c3aed"
, label = "04"
, title = "Fire and forget"
, body = "View timeline animations are fire-and-forget. No AnimState required - just trigger in your init and the browser handles the rest."
}
, { animGroupName = "view-card-5"
, color = "#5b21b6"
, label = "05"
, title = "Composable builders"
, body = "Combine opacity and translate in a single pipeline to create polished reveal effects with minimal code."
}
, { animGroupName = "view-card-6"
, color = "#4c1d95"
, label = "06"
, title = "WAAPI powered"
, body = "All animations run via the Web Animations API on the compositor thread - no JavaScript timers, no Elm subscriptions, maximum performance."
}
]
cardView : CardData -> Html msg
cardView card =
div
(ViewTimeline.attributes card.animGroupName
++ [ style "display" "flex"
, style "flex-direction" "column"
, style "gap" "24px"
, style "align-items" "flex-start"
, style "padding" "clamp(18px, 4vmin, 32px)"
, style "background" "white"
, style "border-radius" "16px"
, style "box-shadow" "0 4px 24px rgba(99,102,241,0.08)"
, style "opacity" "0"
]
)
[ div
[ style "display" "flex"
, style "flex-direction" "column"
, style "gap" "10px"
, style "align-items" "flex-start"
]
[ span
[ style "font-size" "2rem"
, style "font-weight" "800"
, style "color" card.color
, style "flex-shrink" "0"
, style "line-height" "1"
, style "padding-top" "4px"
]
[ text card.label ]
, div
[ style "flex" "1"
, style "min-width" "0"
, style "text-align" "left"
]
[ h2
[ style "font-size" "1.3rem"
, style "font-weight" "700"
, style "margin" "0"
, style "color" "#111827"
, style "overflow-wrap" "break-word"
, style "word-break" "break-word"
]
[ text card.title ]
]
]
, p
[ style "font-size" "1rem"
, style "line-height" "1.7"
, style "color" "#6b7280"
, style "margin" "0"
]
[ text card.body ]
]