Lottie Scroll Donut Chart — Segments Fill on Scroll

Lottie Scroll Donut Chart · Scroll · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Lottie JSON built in code with keyframe helpers — no exported file, nothing to fetch or 404
Each segment is a thick stroked ring trimmed to its own slice via a Lottie trim path (tm)
Every animated keyframe carries i/o easing tangents so lottie-web interpolates instead of holding
Segments draw in sequence, each over a timeline window sized to its value, starting where the last ended
Minus-ninety-degree layer rotation so zero percent begins at the top, as donut charts are read
autoplay: false + goToAndStop(frame, true): scroll position, not a timer, decides the frame
Fully reversible — scrolling up un-draws the segments in reverse for free
Data-driven: edit one VALUES array and one COLORS array to plot your own breakdown

About this UI Snippet

How to Build a Scroll-Filled Lottie Donut Chart With Sequential Trim-Path Segments

Screenshot of the Lottie Scroll Donut Chart snippet rendered live

The Lottie Scroll Donut Chart snippet fills a multi-colour donut one segment at a time as the visitor scrolls — each coloured slice sweeping in from where the last one ended — and empties it again on the way up. Nothing plays on a timer; the scrollbar drives the fill frame by frame, and the whole chart is generated in JavaScript, so the way each segment draws is fully visible in the code.

Lottie is JSON, assembled in code

A Lottie animation is a JSON document of vector layers and keyframes. Instead of fetching one, this snippet builds the animationData object directly and passes it to lottie.loadAnimation, so there is nothing to host and nothing that can 404. A segment helper stamps out each slice as a layer, and a keyframe helper assembles the draw with the easing tangents lottie-web requires.

Every segment is a trimmed ring

The donut is not four separate arcs drawn at different angles — it is four copies of the same thick stroked ring, each trimmed to a different slice of the circumference. A Lottie trim path (shape type tm) renders only a portion of a stroked path, from a start percentage to an end percentage. For segment two, for example, the trim start is fixed at forty percent and the trim end animates from forty to sixty-eight percent, so it draws only its own slice, beginning exactly where segment one ended. Every layer carries a minus-ninety-degree rotation so that zero percent starts at the top of the circle, the way a donut chart is conventionally read.

Drawing the segments in sequence

Each segment fills during its own window of the timeline. The windows are sized in proportion to the segment values and laid end to end, so the first slice draws over the opening frames, the second picks up right after, and so on until the ring is complete. Before a segment's window its trim end sits at its start percentage, so the slice is invisible until its turn; after the window it holds at full, so it stays drawn. The result is a donut that fills slice by slice in order rather than all at once.

The one keyframe detail that matters

When you author a Lottie by hand, every animated keyframe must carry its i (in) and o (out) bezier tangents. Omit them and lottie-web treats the keyframe as a hold keyframe: it never interpolates, so a trim end keyframed to grow stays stuck at its start and the slice never draws. The keyframe helper here attaches linear tangents to every keyframe, which is what makes the segments fill.

Autoplay off, scroll on

The animation is loaded with autoplay: false so its internal clock never runs. A GSAP ScrollTrigger pins the stage and scrubs a single plain value from 0 to 1 over a range several viewport-heights tall, and a requestAnimationFrame loop reads that value every frame and calls anim.goToAndStop(p * (totalFrames - 1), true). The true tells lottie-web the value is a frame number, and goToAndStop renders a single still frame without starting playback, so the fill is welded to scroll position. Because lottie-web interpolates between keyframes, fractional frames render smoothly.

Reversible for free

Deriving the frame from one scrubbed value means scrolling up produces smaller values, so the segments un-draw in reverse order with no extra code. A smoothed scrub: 0.5 lets the fill glide toward the scroll position rather than snapping to every wheel tick, and the caption fades out the moment the first slice appears. Pair it with the Lottie scroll bar chart or the Lottie scroll scrub ring for a fuller data reveal.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You do not have to reverse-engineer how a JSON file becomes a scroll-filled donut. Paste this snippet's HTML, CSS, and JS into an AI assistant like Claude and ask it to walk through how each segment is trimmed to its own slice, or why every keyframe needs i/o tangents. The same assistant can help you extend it — ask it to add a percentage label in the centre, a legend beside the donut, feed the VALUES from real data, or animate a slice popping out on the way in. Treat the code as a conversation starter, not a finished artifact.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a "scroll-filled Lottie donut chart" in plain HTML, CSS, and JavaScript using lottie-web and GSAP's ScrollTrigger plugin, both from a CDN (no bundler).

Requirements:
- A pinned full-height section with a centered container div for the Lottie, an intro caption overlay, and a live "percent filled" read-out.
- Build the Lottie as an inline animationData JSON object (leave a comment showing how to swap it for path: 'your-animation.json'). Include a keyframe helper that ALWAYS attaches i/o easing tangents (linear) — without them lottie-web holds the value and nothing draws.
- The animation is a donut made of several coloured segments. Each segment is a copy of the same thick stroked ring, trimmed to its slice: the trim start is fixed at the segment's beginning percentage and the trim end animates from that start to its end. Rotate every segment layer by -90 degrees so 0% is at the top. Draw the segments in sequence, each over a timeline window sized in proportion to its value and laid end to end. Add a faint full-circle track behind them.
- Load with lottie.loadAnimation using renderer 'svg', loop false, autoplay FALSE.
- Register a GSAP tween on a ScrollTrigger targeting the pinned section, with pin true, start at top top, scrub around 0.5, and an end several hundred percent tall, animating one value p from 0 to 1.
- In a requestAnimationFrame loop, read p and call anim.goToAndStop(p * (anim.totalFrames - 1), true), update the read-out, and fade the caption out once p passes a small threshold.
- Confirm scrolling up un-draws the segments in reverse.

Step by step

How to Use

  1. 1
    Load the three CDN scriptsAdd lottie.min.js, gsap.min.js, and ScrollTrigger.min.js from the CDN panel, in that order.
  2. 2
    Paste the HTML, CSS, and JSA faint track ring appears centred in a pinned stage with a live "% filled" read-out.
  3. 3
    Scroll downThe coloured segments draw in one after another around the donut, tied directly to scroll position.
  4. 4
    Scroll back upThe segments un-draw exactly, because goToAndStop seeks a single frame rather than playing on a timer.
  5. 5
    Set your own dataEdit the VALUES array (fractions that sum to 1) and the COLORS array to plot your own breakdown.
  6. 6
    Tune the fillChange the ScrollTrigger end value (+=400%) for a longer, slower fill or a shorter, quicker one.

Real-world uses

Common Use Cases

Scroll-revealed breakdowns
Fill a donut of budget, market share, or usage split exactly as the reader scrolls into your data section, so each slice lands with motion.
Lightweight dashboard rings
A vector donut that reacts to scroll makes a crisp dashboard accent without pulling in a full charting library.
Pricing and plan comparisons
Show how a plan or budget divides up, drawing each portion as visitors scroll through the explanation.
Report and survey results
Animate a survey or composition breakdown into place as its section scrolls in, pairing each slice with its label.
Learn Lottie trim segments
A clear example of building multiple trim-path slices from one ring and sequencing them. Pair it with the Lottie scroll scrub ring.
Data-driven hero graphics
Feed real proportions into the VALUES array to turn the donut into a live, scroll-reactive summary of your own data.

Got questions?

Frequently Asked Questions

Every segment is a copy of the same thick stroked ring, but its trim path start is fixed at the segment beginning and its trim end animates from that start to the segment end. So segment two, starting at forty percent, has its trim end grow from forty to sixty-eight percent — it renders only that slice, beginning exactly where segment one finished. A minus-ninety-degree rotation on each layer puts zero percent at the top.

Because lottie-web treats an animated keyframe with no i/o easing tangents as a hold keyframe — it never interpolates, so a trim end keyframed to grow stays stuck at its start and the slice never draws. The keyframe helper in this snippet attaches linear tangents to every keyframe. If you hand-edit a keyframe and a slice stops drawing, missing tangents are the first thing to check.

Edit the VALUES array so its entries are fractions that sum to 1, and the COLORS array to match. The segment start percentages, timeline windows, and draw order all derive from those values, so they adapt automatically — add or remove entries to change the number of slices. Keep the values summing to 1 so the ring closes cleanly at full scroll.

play runs the animation on its internal clock, which is exactly what you do not want when scroll should control it. goToAndStop(frame, true) renders one specific still frame and never starts the timer — the second argument tells lottie-web the value is a frame number, not milliseconds. Multiplying scroll progress by totalFrames gives the frame to show, and fractional frames interpolate smoothly so the fill is continuous.

Yes. Click JSX for a React component, Vue for a Vue 3 SFC, Angular for a standalone component, or Tailwind for a React + Tailwind version. Call lottie.loadAnimation against a ref inside a mount effect, register the ScrollTrigger there, and on cleanup call anim.destroy() and kill the ScrollTrigger instance (or revert a gsap.context) so the SVG and scroll listener are released on unmount.