scroll-driven-animationScroll-driven animation
Bind animation progress to a scroll range and tune its start, end, and travel.
Preview
Scroll progress supplies the animation timeline, mapping page movement to visual state.
FullReduced
FullReduced
Preview
Scroll
Scroll-driven animation
Continue
Implement Scroll-driven animation: Bind animation progress to a scroll range and tune its start, end, and travel. Use Start 10%, End 80%, Distance 80px, Axis y. Drive it from viewport entry or scroll progress. Use semantic HTML, scope CSS to .motion-demo--scroll-driven-animation. Provide equivalent low-motion behavior for prefers-reduced-motion and .force-reduced-motion.
<div class="motion-demo motion-demo--scroll-driven-animation" data-motion="scroll-driven-animation" aria-label="Scroll-driven animation demo">
<div class="motion-scroll-track"><span>Scroll</span><article class="motion-surface">
<strong>Scroll-driven animation</strong>
<span class="motion-line"></span>
<span class="motion-line"></span>
</article><span>Continue</span></div>
</div>.motion-demo.motion-demo--scroll-driven-animation {
box-sizing: border-box;
position: relative;
display: grid;
place-items: center;
width: min(100%, 34rem);
min-height: 15rem;
padding: 2rem;
overflow: hidden;
color: #18181b;
background: #fafafa;
border: 1px solid #e4e4e7;
border-radius: 16px;
isolation: isolate;
}
.motion-demo.motion-demo--scroll-driven-animation *, .motion-demo.motion-demo--scroll-driven-animation *::before, .motion-demo.motion-demo--scroll-driven-animation *::after {
box-sizing: border-box;
}
.motion-demo.motion-demo--scroll-driven-animation .motion-surface {
position: relative;
display: grid;
gap: 0.65rem;
width: min(100%, 18rem);
padding: 1.25rem;
background: #ffffff;
border: 1px solid #e4e4e7;
border-radius: 16px;
box-shadow: none;
}
.motion-demo.motion-demo--scroll-driven-animation .motion-surface strong { font-weight: 500; }
.motion-demo.motion-demo--scroll-driven-animation .motion-line {
display: block;
width: 100%;
height: 0.5rem;
background: #e4e4e7;
border-radius: 999px;
}
.motion-demo.motion-demo--scroll-driven-animation .motion-line:last-child { width: 62%; }
.motion-demo.motion-demo--scroll-driven-animation .motion-button {
position: relative;
min-height: 2.75rem;
padding: 0.75rem 1.1rem;
color: white;
font: inherit;
font-weight: 700;
background: #18181b;
border: 0;
border-radius: 999px;
cursor: pointer;
}
.motion-demo.motion-demo--scroll-driven-animation .motion-button:focus-visible { outline: 3px solid rgb(37 99 235 / 32%); outline-offset: 3px; }
.motion-demo.motion-demo--scroll-driven-animation .motion-action-row {
display: flex;
justify-content: center;
gap: 0.5rem;
margin-top: 0.85rem;
}
.motion-demo.motion-demo--scroll-driven-animation .motion-replay-scene {
width: min(100%, 22rem);
display: grid;
place-items: center;
}
.motion-demo.motion-demo--scroll-driven-animation .motion-pausable-scene {
width: min(100%, 22rem);
display: grid;
place-items: center;
}
.motion-demo.motion-demo--scroll-driven-animation button.motion-surface {
color: inherit;
font: inherit;
text-align: start;
cursor: pointer;
}
.motion-demo.motion-demo--scroll-driven-animation .motion-replay-button,
.motion-demo.motion-demo--scroll-driven-animation .motion-undo-button,
.motion-demo.motion-demo--scroll-driven-animation .motion-dismiss-button,
.motion-demo.motion-demo--scroll-driven-animation .motion-pause-button {
min-height: 2.75rem;
padding: 0.65rem 0.9rem;
color: #18181b;
font: 650 0.78rem/1 ui-sans-serif, system-ui, sans-serif;
background: #ffffff;
border: 1px solid #d4d4d8;
border-radius: 8px;
cursor: pointer;
}
.motion-demo.motion-demo--scroll-driven-animation .motion-replay-button:focus-visible,
.motion-demo.motion-demo--scroll-driven-animation .motion-undo-button:focus-visible,
.motion-demo.motion-demo--scroll-driven-animation .motion-dismiss-button:focus-visible,
.motion-demo.motion-demo--scroll-driven-animation .motion-pause-button:focus-visible,
.motion-demo.motion-demo--scroll-driven-animation [data-reorder-item]:focus-visible,
.motion-demo.motion-demo--scroll-driven-animation [data-swipe-target]:focus-visible {
outline: 3px solid rgb(37 99 235 / 32%);
outline-offset: 3px;
}
.motion-demo.motion-demo--scroll-driven-animation .motion-status {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.motion-demo.motion-demo--scroll-driven-animation .motion-ripple-ink {
position: absolute;
width: 12px;
height: 12px;
pointer-events: none;
background: rgb(255 255 255 / 72%);
border-radius: 50%;
transform: translate(-50%, -50%) scale(0.95);
opacity: 0;
}
.motion-demo.motion-demo--scroll-driven-animation .motion-state-stack { position: relative; width: min(100%, 18rem); height: 9rem; }
.motion-demo.motion-demo--scroll-driven-animation .motion-state { position: absolute; inset: 0; display: grid; place-items: center; padding: 1rem; background: #ffffff; border: 1px solid #e4e4e7; border-radius: 16px; }
.motion-demo.motion-demo--scroll-driven-animation .motion-state--to { background: #18181b; color: white; }
.motion-demo.motion-demo--scroll-driven-animation .motion-scroll-track > span { color: #71717a; font: 600 0.72rem/1.2 ui-monospace, monospace; letter-spacing: 0.08em; text-transform: uppercase; }
.motion-demo.motion-demo--scroll-driven-animation .motion-orbit-item { display: grid; place-items: center; width: 2.5rem; height: 2.5rem; color: white; background: #2563eb; border-radius: 50%; }
.motion-demo.motion-demo--scroll-driven-animation .motion-text-stack { position: relative; width: min(100%, 18rem); min-height: 4rem; }
.motion-demo.motion-demo--scroll-driven-animation svg { width: min(100%, 20rem); height: auto; }
.motion-demo.motion-demo--scroll-driven-animation { max-height: 15rem; overflow-x: hidden; overflow-y: auto; place-items: start center; }
.motion-demo.motion-demo--scroll-driven-animation .motion-scroll-track { display: grid; place-items: center; gap: 10rem; width: 100%; min-height: 48rem; padding-block: 5rem; }
.motion-demo.motion-demo--scroll-driven-animation .motion-surface { animation: motion-scroll-driven-animation linear both; animation-timeline: scroll(nearest); animation-range: 10% 80%; }
@keyframes motion-scroll-driven-animation { from { transform: translateY(80px); } to { transform: none; } }
@supports not (animation-timeline: scroll()) { .motion-demo.motion-demo--scroll-driven-animation .motion-surface { animation: motion-scroll-driven-animation 700ms linear both; } }
@keyframes motion-scroll-driven-animation-reduced-fade {
from { opacity: 0.84; }
to { opacity: 1; }
}
.force-reduced-motion .motion-demo.motion-demo--scroll-driven-animation {
animation: motion-scroll-driven-animation-reduced-fade 160ms cubic-bezier(0.23, 1, 0.32, 1) both !important;
}
.force-reduced-motion .motion-demo.motion-demo--scroll-driven-animation *,
.force-reduced-motion .motion-demo.motion-demo--scroll-driven-animation *::before,
.force-reduced-motion .motion-demo.motion-demo--scroll-driven-animation *::after {
animation: none !important;
animation-delay: 0ms !important;
animation-iteration-count: 1 !important;
transition-property: opacity, color, background-color, border-color !important;
transition-duration: 160ms !important;
transition-delay: 0ms !important;
scroll-behavior: auto !important;
}
.force-reduced-motion .motion-demo.motion-demo--scroll-driven-animation .motion-button,
.force-reduced-motion .motion-demo.motion-demo--scroll-driven-animation [data-reorder-item],
.force-reduced-motion .motion-demo.motion-demo--scroll-driven-animation [data-swipe-target],
.force-reduced-motion .motion-demo.motion-demo--scroll-driven-animation [data-spring-target] {
transform: none !important;
}
.force-reduced-motion .motion-demo.motion-demo--scroll-driven-animation .motion-path { stroke-dashoffset: 0 !important; }
.force-reduced-motion .motion-demo.motion-demo--scroll-driven-animation .motion-word--from { opacity: 0 !important; visibility: hidden; }
.force-reduced-motion .motion-demo.motion-demo--scroll-driven-animation .motion-word:not(.motion-word--from) { opacity: 1 !important; filter: none !important; transform: none !important; }
@media (prefers-reduced-motion: reduce) {
.motion-demo.motion-demo--scroll-driven-animation {
animation: motion-scroll-driven-animation-reduced-fade 160ms cubic-bezier(0.23, 1, 0.32, 1) both !important;
}
.motion-demo.motion-demo--scroll-driven-animation *, .motion-demo.motion-demo--scroll-driven-animation *::before, .motion-demo.motion-demo--scroll-driven-animation *::after {
animation: none !important;
animation-delay: 0ms !important;
animation-iteration-count: 1 !important;
transition-property: opacity, color, background-color, border-color !important;
transition-duration: 160ms !important;
transition-delay: 0ms !important;
scroll-behavior: auto !important;
}
.motion-demo.motion-demo--scroll-driven-animation .motion-button,
.motion-demo.motion-demo--scroll-driven-animation [data-reorder-item],
.motion-demo.motion-demo--scroll-driven-animation [data-swipe-target],
.motion-demo.motion-demo--scroll-driven-animation [data-spring-target] {
transform: none !important;
}
.motion-demo.motion-demo--scroll-driven-animation .motion-path { stroke-dashoffset: 0 !important; }
.motion-demo.motion-demo--scroll-driven-animation .motion-word--from { opacity: 0 !important; visibility: hidden; }
.motion-demo.motion-demo--scroll-driven-animation .motion-word:not(.motion-word--from) { opacity: 1 !important; filter: none !important; transform: none !important; }
}Learn more
Terms and distinctions
scroll-driven-animationScroll-driven animation
滚动驱动动画English definition
Scroll progress supplies the animation timeline, mapping page movement to visual state.
Chinese translation
动画进度与滚动位置直接绑定。
Before implementation
- Purpose
- Bind visual progress continuously to a scroll range to explain position, phases, or narrative progress.
- Frequency
- Reserve for low-frequency featured narratives and data explanation; keep ordinary reading static.
- Trigger
- Drive continuously from scroll position with explicit start, end, and stable out-of-range states.
- Enter / exit
- Hold the first state before the range and the last after it, replaying exactly during reverse scroll.
- Interruptibility
- Scroll input is naturally reversible, with rendering tied directly to current progress.
- Gesture and keyboard
- Preserve native scrolling and touch momentum, with keyboard, scrollbar drag, and assistive technology reaching the same result.
- Reduced-motion strategy
- Freeze on the most informative static frame and provide textual progress or explanation.
Accessibility
Reduced motion
Freeze on the most informative static frame and provide textual progress or explanation.
Parameter reference
| Parameter | Range / options | Default | Purpose |
|---|---|---|---|
start | 0%–90% | 10% | Adjusts start. |
end | 10%–100% | 80% | Adjusts end. |
distance | 0px–240px | 80px | Controls how far the element travels. |
axis | Vertical / Horizontal | Vertical | Selects axis. |
Pre-delivery review
- Test the scroll range across viewport heights.
- Avoid per-frame layout work and large paint regions.