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.
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
ScrollVideo | ScrollFrames | |
|---|---|---|
| Source | One video file | A numbered series of images |
| Download size | Small: video compresses frames against each other | Larger: every frame is a full image |
| Frames | Hundreds or thousands | Best up to a few hundred |
| Scrubbing | Smooth once keyframes are close | Exact from the start, no preparation |
| Loading screen | Built in | None: 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:
import { ScrollFrames } from "vidscroll";
export function Turntable() {
return <ScrollFrames urls="/frames/frame_{i3}.webp" count={120} length="300vh" />;
}urls | Example | Gives |
|---|---|---|
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
| Prop | Type | Default | Description |
|---|---|---|---|
urls | string | string[] | (i) => string | required | Where the images are: a pattern, an array or a function. |
count | number | required | Number of frames. |
length | string | number | "auto" | Scrolling that covers all frames. |
fps | number | 30 | Frame rate, used for "auto" length and the reported time. |
easing | EasingName | (t) => number | "none" | Curve from scroll progress to frame. |
fit | "cover" | "contain" | "cover" | How frames fill the screen. Read when the component starts. |
smoothScroll | boolean | SmoothScrollOptions | false | Eased mouse-wheel scrolling for the page. |
smoothingTauMs | number | 100 | How closely the frames follow the scroll, in ms. 35 when smoothScroll is on. |
debug | boolean | false | Log engine internals to the console. onDebug receives them. |
className, style | string, CSSProperties | none | Applied to the outer block. |
Related
- ScrollVideo: the same, with a video file.
- Section: ranges and
--progress.