vidscroll0.3.3

ScrollFrames

Scrub through a sequence of images instead of a video.

ScrollFrames works like ScrollVideo, but its source is a numbered series of images, drawn to a canvas. There’s no video decoding, so every frame is exact and shows instantly once it has loaded.

Scroll inside the preview
Example.tsx
import { ScrollFrames, Section } from "vidscroll";

export default function Example() {
  return (
    <ScrollFrames urls="/frames/train/{i3}.webp" count={87} length="300vh">
      <Section id="stills" start={0} end={0.4}>
        <h2>87 still images</h2>
      </Section>
      <Section id="water" fromFrame={50} toFrame={75}>
        <h2>Frames 50 to 75</h2>
      </Section>
    </ScrollFrames>
  );
}

When to use it

ScrollVideoScrollFrames
SourceOne video fileA numbered series of images
Download sizeSmall: video compresses frames against each otherLarger: every frame is a full image
FramesHundreds or thousandsBest up to a few hundred
ScrubbingSmooth once keyframes are closeExact from the start, no preparation
Loading screenBuilt inNone: it starts as soon as enough frames are in

Use ScrollFrames when you already have frames, such as 3D renders or a product turntable, or need pixel-exact stills. For filmed footage or anything long, ScrollVideo downloads far less.

Pointing it at your images

count is the number of images. urls describes where they are, in one of three ways:

Turntable.tsx
import { ScrollFrames } from "vidscroll";

export function Turntable() {
  return <ScrollFrames urls="/frames/frame_{i3}.webp" count={120} length="300vh" />;
}
urlsExampleGives
A pattern with {i}"/frames/{i}.jpg"/frames/0.jpg, /frames/1.jpg …
A pattern with {iN}, padded to N digits"/frames/frame_{i3}.webp"/frames/frame_000.webp, /frames/frame_001.webp …
An array[a, b, c]Those URLs, in order
A function(i) => `/f/${i + 1}.png`Whatever it returns for each index

Numbering starts at 0. If your files start at 1, use a function. With an array, set count to the array’s length.

Loading

Frames load in order, 12 at a time. The component starts following the scroll once the first frame and a tenth of the rest have loaded. Until a frame is there, the nearest loaded one is shown, and between two frames the next one fades in over the current one, so slow scrolling looks smooth even with few frames.

There’s no loading overlay, so the first frame should work as a still on its own. On high-density screens the canvas is drawn at up to twice the CSS size.

Length and sections

length works as in ScrollVideo. With the default "auto", the length comes from the frame count: count divided by fps (30 by default) gives a duration, at 40vh of scrolling per second. Setting length yourself is usually clearer.

Sections can use start and end, or frame numbers with fromFrame and toFrame. Hooks and --video-progress work the same as with a video.

Props

PropTypeDefaultDescription
urlsstring | string[] | (i) => stringrequiredWhere the images are: a pattern, an array or a function.
countnumberrequiredNumber of frames.
lengthstring | number"auto"Scrolling that covers all frames.
fpsnumber30Frame rate, used for "auto" length and the reported time.
easingEasingName | (t) => number"none"Curve from scroll progress to frame.
fit"cover" | "contain""cover"How frames fill the screen. Read when the component starts.
smoothScrollboolean | SmoothScrollOptionsfalseEased mouse-wheel scrolling for the page.
smoothingTauMsnumber100How closely the frames follow the scroll, in ms. 35 when smoothScroll is on.
debugbooleanfalseLog engine internals to the console. onDebug receives them.
className, stylestring, CSSPropertiesnoneApplied to the outer block.