tool / scroll triggerstart · end · progress
Scroll trigger visualizer
start: 'top 85%' is two words: an edge of the element, and a line across the viewport. Pick both for start and end, scroll the preview, and watch where progress is 0, where it is 1, and the arithmetic in between. Then copy the trigger.
- Element edges
- 3
- Viewport lines
- 3 + %
- Defaults compared
- 8
- Library
- 2.23.3
Set a start and an end
The box below is a small viewport. Scroll it, drag the slider or press Play: the element moves past the two lines, and the drawing inside it is scrollDraw itself, given your trigger and told to watch the box instead of the window.
Starts when its top reaches 85% down the viewport; done when its top reaches 40% down the viewport.
Viewport · 300px tall · scroll it
Before the start line: nothing has happened yet.
- Viewport
- 300px tall: the scroll box
- Element
- top 375px down the content, 120px tall
- Start
- 'top 85%': 375 − 255 = 120pxelement top at 375 · 85% of 300 = 255
- End
- 'top 40%': 375 − 120 = 255pxelement top at 375 · 40% of 300 = 120
- Scroll
- 0px
- Progress
- (0 − 120) ÷ (255 − 120) = -0.89, clamped to 0
import { scrollDraw } from 'svg-scroll-draw';
// starts when its top reaches 85% down the viewport,
// done when its top reaches 40% down the viewport.
scrollDraw('#drawing', {
trigger: { start: 'top 85%', end: 'top 40%' },
});Anything that matches scrollDraw’s own default window (top bottom → bottom top) is left out of the code.
How start and end become progress
Each pair becomes one scroll position: the place on the page where the element’s edge sits on the viewport’s line. That is the edge’s distance down the page minus the line’s distance down the viewport (fig. 1). An edge other than top adds part of the element’s height, half for center and all of it for bottom, so bottom bottom is the moment the element’s bottom edge comes into view: for an element shorter than the viewport, the moment all of it is on screen.
Progress is how far the scroll has got from the start position to the end one, clamped to 0–1. Scroll back and it goes back down, unless the function keeps what it has reached (once).
// One element, worked by hand: a 900px-tall window,
// and the element's top 2000px down the page.
const vh = 900;
const top = 2000;
// start: 'top 85%' — its top meets the line 85% down the window.
const startAt = top - 0.85 * vh; // 1235
// end: 'top 40%' — the same edge meets the line 40% down.
const endAt = top - 0.40 * vh; // 1640
// In between, progress follows the scroll in a straight line.
const progress = Math.min(1, Math.max(0, (scrollY - startAt) / (endAt - startAt)));speed before clamping, 1 by default: at speed: 2 the animation is done half way through the window. scrollText, scrollReveal, scrollCounter and scrollVideo have no speed.refresh() on a scrollDraw instance.top bottom to bottom top, lets scrollDraw and scrollAnimate hand the work to the browser’s own scroll timeline. Any other trigger keeps them on their JavaScript loop, which follows the same arithmetic.Every form the trigger accepts
The parser reads the first two words, split on spaces. The element’s word is top, center or bottom, and anything else is read as top. The viewport’s word is one of the same three or digits followed by %, decimals allowed (85.5%, not .5% or -10%), and anything else is read as bottom. A percentage on its own is the viewport’s line, for the element’s top. The parser does not warn when a word falls back, so the “read as” column is worth a look.
| Written | Read as | Why |
|---|---|---|
| 'top 85%' | 'top 85%' | An element edge, then a line across the viewport. |
| 'center center' | 'center center' | The element’s middle on the viewport’s middle. |
| 'bottom top' | 'bottom top' | The element’s bottom on the viewport’s top: it has just left. |
| '85.5%' | 'top 85.5%' | Decimals are fine. A percentage on its own is the viewport line; the element edge is top. |
| '85%' | 'top 85%' | A percentage on its own is the viewport line; the element edge is top. |
| 'center' | 'center bottom' | One word: the viewport line defaults to bottom. |
| '' (empty) | 'top bottom' | Empty, so both words take their defaults: top, bottom. |
| 'top 100px' | 'top bottom' | Pixels are not read. “100px” is not top, center, bottom or a percentage, so the line reads as bottom. |
| 'top bottom-=100px' | 'top bottom' | Relative offsets are not read. “bottom-=100px” is not top, center, bottom or a percentage, so the line reads as bottom. |
| 'top+=100 center' | 'top center' | “top+=100” is not top, center or bottom, so the element edge reads as top. |
| '50% center' | 'top center' | The element word cannot be a percentage: “50%” reads as top. |
| 'Top Center' | 'top bottom' | Words are case-sensitive. “Top” is not top, center or bottom, so the element edge reads as top. “Center” is not top, center, bottom or a percentage, so the line reads as bottom. |
| '120%' | 'top 120%' | A percentage on its own is the viewport line; the element edge is top. Over 100%, the line is below the bottom of the viewport. |
axis: 'x' the same words measure across: top is the left edge, bottom the right. left and right are not words the parser knows, so 'left left' reads as 'top bottom': the element’s left edge on the viewport’s right.The default window of each function
Leave trigger out and each function uses its own window. scrollDraw, scrollAnimate and the progress ones run the whole time the element is on screen; text, reveals and counters finish while it is still well in view, where a reader is looking; scrollVideo scrubs from the moment its top reaches the top of the viewport.
| Function | Import | start | end | In words |
|---|---|---|---|---|
| scrollDraw | svg-scroll-draw | 'top bottom' | 'bottom top' | Starts when its top reaches the bottom of the viewport; done when its bottom reaches the top of the viewport. |
| scrollAnimate | svg-scroll-draw | 'top bottom' | 'bottom top' | Starts when its top reaches the bottom of the viewport; done when its bottom reaches the top of the viewport. |
| scrollProgress | svg-scroll-draw/progress | 'top bottom' | 'bottom top' | Starts when its top reaches the bottom of the viewport; done when its bottom reaches the top of the viewport. |
| useScrollDrawProgress | svg-scroll-draw/react | 'top bottom' | 'bottom top' | Starts when its top reaches the bottom of the viewport; done when its bottom reaches the top of the viewport. |
| scrollText | svg-scroll-draw/text | 'top 85%' | 'top 40%' | Starts when its top reaches 85% down the viewport; done when its top reaches 40% down the viewport. |
| scrollReveal | svg-scroll-draw/reveal | 'top 88%' | 'top 53%' | Starts when its top reaches 88% down the viewport; done when its top reaches 53% down the viewport. |
| scrollCounter | svg-scroll-draw | 'top 80%' | 'top 20%' | Starts when its top reaches 80% down the viewport; done when its top reaches 20% down the viewport. |
| scrollVideo | svg-scroll-draw/video | 'top top' | 'bottom top' | Starts when its top reaches the top of the viewport; done when its bottom reaches the top of the viewport. |
stagger (0.15 by default) of the gap between the start and end lines up the viewport, never more than that whole gap, so a row of cards arrives one after another.Compared with GSAP ScrollTrigger
The two-word form is ScrollTrigger’s, so a trigger written with keywords and viewport percentages means the same in both: 'top center' is 'top center'. They part over everything else ScrollTrigger accepts. Described against GSAP 3.15.0.
| GSAP ScrollTrigger | svg-scroll-draw | |
|---|---|---|
| Order of the words | The trigger element’s edge, then the scroller’s line. | The same: the element’s edge, then the viewport’s line. |
| Keywords | top, center, bottom, left, right. | top, center, bottom. On axis: ‘x’ they measure across: top is the left edge, bottom the right. |
| Percentages | On either word: ‘80% bottom’ is 80% down the element. | On the viewport word only. An element percentage reads as top. |
| Pixels, += and -= | ‘top 100px’, ‘top bottom-=100px’, and an end of ‘+=500’ counted from the start. | Not read: such a viewport word falls back to bottom, such an element word to top. |
| Other values | A number (an exact scroll position), a function, or clamp(). | Strings only. |
| Default window | ‘top bottom’ → ‘bottom top’; ‘top top’ when pinning. | The same for scrollDraw, scrollAnimate and scrollProgress; scrollText, scrollReveal, scrollCounter and scrollVideo have their own (section 04). |
| End on another element | endTrigger measures the end on a second element. | No: start and end measure one element. scrollAnimate and scrollVideo can measure another with triggerElement. |
| What progress does | With scrub, follows the scroll; without, start and end toggle a tween that plays on time. | Always follows the scroll. scrollDraw’s autoplay: true is the timed option: it plays over its duration when the drawing comes into view, whatever the window. In 2.23.2 and earlier, autoplay left on the default window could take the browser’s native scroll timeline and follow the scroll after all (native: false avoided it); fixed in 2.23.3. |
| Seeing the lines | markers: true. | scrollDraw’s debug: true, in development builds. In 2.23.2 and earlier, with the default window in a browser that has native scroll timelines, it could take the native path and draw no lines (native: false avoided it); fixed in 2.23.3. |
// GSAP ScrollTrigger
gsap.to('#chart', {
opacity: 1,
scrollTrigger: {
trigger: '#chart',
start: 'top center',
end: 'bottom center',
scrub: true,
},
});
// svg-scroll-draw: the same two words, on the element itself
import { scrollAnimate } from 'svg-scroll-draw';
scrollAnimate('#chart', {
props: { opacity: [0, 1] },
trigger: { start: 'top center', end: 'bottom center' },
});'top 85%' is 135px above the bottom, on a 600px one 90px.Scroll trigger questions
What do start and end mean in a scroll trigger?
Each is two words: an edge of the element (top, center or bottom), then a line across the viewport (top, center, bottom or a percentage, counted from the viewport's top). start: 'top 85%' is the scroll position at which the element's top reaches 85% of the way down the viewport, and there the animation's progress is 0. end names the position where progress is 1. In between, progress follows the scroll in a straight line, and the easing shapes the motion on top.
What does start: 'top center' mean?
The animation begins when the top of the element reaches the middle of the viewport. It means the same in GSAP ScrollTrigger and in svg-scroll-draw, because both read the element's word first and the viewport's second. Paired with end: 'bottom center', the animation runs while the element passes through the middle of the screen and finishes as its bottom crosses it.
What are the default start and end of a scroll trigger?
In GSAP ScrollTrigger, start is 'top bottom' ('top top' when pinning) and end is 'bottom top'. In svg-scroll-draw it depends on the function: scrollDraw, scrollAnimate and scrollProgress use 'top bottom' to 'bottom top', the whole time the element is on screen, while scrollText uses 'top 85%' to 'top 40%' and scrollReveal 'top 88%' to 'top 53%', so text and cards have arrived while they are still well in view.
Can I use pixel offsets like 'top 100px' or 'top+=100'?
Not in svg-scroll-draw. The viewport word must be top, center, bottom or a percentage such as 85%; anything else, 100px or bottom-=100px, reads as bottom. An element word other than top, center or bottom, such as top+=100 or 50%, reads as top. Use a percentage of the viewport instead, which also keeps the timing the same on every screen height. GSAP ScrollTrigger does accept pixels and += or -= offsets.
Why is my scroll animation already part way through when the page loads?
Progress comes from how far the page is scrolled, not from when the animation was created. An element on the first screen is often past its start position before anyone scrolls: with start: 'top bottom', any element whose top is above the bottom of the window at load begins part way. Move the start line up the viewport, to 'top center' or 'top top', so the element's top is still below it when the page loads, or for something on the first screen use scrollDraw's autoplay: true, which plays on a clock when it comes into view instead of following the scroll. In 2.23.2 and earlier, autoplay on the default window could take the browser's native scroll timeline and follow the scroll after all, unless you also set native: false; 2.23.3 fixes that.
Why does my scroll animation never finish?
Its end position is further down than the page can scroll. Progress reaches 1 only when the scroll gets to the end position, so an element near the bottom of the page with end: 'bottom top' would need the page to carry it right off the top of the screen, and the page runs out first. Choose an end the page can always reach, such as 'bottom bottom' (the element's bottom on the window's bottom), or leave more room below the element.
What happens if end comes before start?
The animation runs backwards. Progress is (scroll − start) ÷ (end − start), clamped to 0–1, so when the end position is the smaller one progress is 1 before the element arrives and falls to 0 as you scroll on. With once on, as it is by default for scrollText, scrollReveal and scrollCounter, progress keeps the 1 it starts at and nothing moves. If start and end are the same position, the window has no length and progress stays 0. The visualizer warns about both.
The rest of svg-scroll-draw 2.23.3: the docs, the text animation generator and the SVG path viewer.