Section
Show content over part of the video, by scroll position, time or frame.
A Section goes inside a ScrollVideo. It’s active while the scroll is
inside its range: by default it fades in, sits over the video, and fades out
again when you leave the range. Everything you put inside it is ordinary
React content.
import { ScrollVideo, Section } from "vidscroll";
export function Hero() {
return (
<ScrollVideo src="/videos/hero.mp4" length="400vh">
<Section start={0} end={0.3}>
<h2>Doors close</h2>
</Section>
<Section fromTime={12} toTime={18}>
<h2>Open water</h2>
</Section>
</ScrollVideo>
);
}Ranges
A range has a start and an end, and each can be given in one of three units:
| Props | Unit | Use it when |
|---|---|---|
start, end | Scroll progress, 0 to 1 | The text belongs to a stretch of scrolling, like the first third of the block. |
fromTime, toTime | Seconds of video | The text belongs to a scene, like a caption for 12 s to 18 s. |
fromFrame, toFrame | Frame numbers | You’re matching exact frames from an editing tool. |
The difference matters when you use easing. Progress ranges are positions
in the scroll, so easing doesn’t move them. Time and frame ranges are tied to
the video, so they stay on their scenes whatever the easing curve does to the
scroll.
Some details:
- A missing start means the beginning, a missing end means the end. A section with no range at all is active the whole time.
- The start and end are resolved separately, so you can mix them, for example
start={0.2}withtoTime={18}. If both kinds are given for one end, progress wins over time, and time over frames. - A section is active from its start up to, but not including, its end. A section that ends at the very end of the video stays active there.
- Time and frame ranges only become active once the video’s duration is known, so they never flash on while it loads.
- Frame numbers use the
fpsprop (30 by default) unless the browser measures the video’s real frame rate.
Following the progress with CSS
Every section sets a --progress CSS variable on itself: 0 before its range,
rising to 1 across it, and 1 after it. Children can use it for any effect,
with no JavaScript and no re-renders:
import { ScrollVideo, Section } from "vidscroll";
export default function Example() {
return (
<ScrollVideo src="/train.mp4" length="400vh">
<Section id="caption" start={0.1} end={0.9}>
<div style={{ textAlign: "center" }}>
<div style={{ width: 240, height: 4, margin: "0 auto 32px", background: "rgb(255 255 255 / 0.25)" }}>
<div style={{ width: "calc(var(--progress) * 100%)", height: "100%", background: "#f6b98e" }} />
</div>
<h2 style={{ transform: "translateY(calc((1 - var(--progress)) * 80px))" }}>
Rising with the scroll
</h2>
</div>
</Section>
</ScrollVideo>
);
}The bar fills with width: calc(var(--progress) * 100%) and the heading rises
into place as --progress goes from 0 to 1.
In your own CSS, the heading part looks like this:
.caption h2 {
transform: translateY(calc((1 - var(--progress)) * 80px));
}Styling active sections
By default a section covers the video, centres its content, and fades over
0.4 s. While active it has a data-active attribute, so you can restyle it with
CSS:
.caption {
align-items: flex-end;
padding-bottom: 12vh;
}
.caption[data-active] h2 {
letter-spacing: 0.02em;
}<Section className="caption" fromTime={12} toTime={18}>
<h2>Open water</h2>
</Section>activeClassName and inactiveClassName add a class in either state instead,
which helps with utility CSS such as Tailwind. All the default styles have zero
specificity, so any rule you write wins.
Section ids
Each section gets an id, used as its data-vidscroll-section attribute and in
events. An automatic one is generated, so you only need id when you want to
refer to the section from code, for example with api.getSectionProgress("intro")
or to tell sections apart in sectionEnter events. Ids must be unique within
one ScrollVideo.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
start | number | 0 | Start of the range in scroll progress, 0 to 1. |
end | number | 1 | End of the range in scroll progress, 0 to 1. |
fromTime | number | none | Start of the range in seconds. |
toTime | number | none | End of the range in seconds. |
fromFrame | number | none | Start of the range as a frame number. |
toFrame | number | none | End of the range as a frame number. |
id | string | automatic | Name for events and getSectionProgress. Unique within the ScrollVideo. |
as | ElementType | "div" | Element or component to render, such as "section". |
className | string | none | Class on the section element. |
activeClassName | string | none | Extra class while active. |
inactiveClassName | string | none | Extra class while inactive. |
style | CSSProperties | none | Inline styles on the section element. |
data | unknown | none | Stored with the section. Not passed to events or hooks yet, so it has no visible effect today. |
Related
- ScrollVideo:
length,easingand loading. - Hooks: react to active sections from your own components.