This is cinematography in its raw form with practical lamps, soft falloff, and the presence of grain that fills each corner of the frame.
Add these to your <head> or before closing </body> tag:
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.1/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.1/dist/ScrollTrigger.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.1/dist/SplitText.min.js"></script>
.block-line-wrapper {
position: relative;
width: max-content;
display: block;
}
.block-line {
position: relative;
display: block;
}
.block-revealer {
position: absolute;
top: 0;
left: 0;
width: 101%;
height: 101%;
pointer-events: none;
will-change: transform;
z-index: 1;
}
[data-block-reveal] {
visibility: hidden;
}
[data-block-reveal].is-ready {
visibility: visible;
}gsap.registerPlugin(ScrollTrigger, SplitText);
document.addEventListener("DOMContentLoaded", () => {
document.querySelectorAll("[data-block-reveal]").forEach((element) => {
const blockColor = element.dataset.blockColor || "#fe0100";
const stagger = parseFloat(element.dataset.stagger) || 0.15;
const duration = parseFloat(element.dataset.duration) || 0.75;
const lines = [];
const blocks = [];
const split = new SplitText(element, {
type: "lines",
linesClass: "block-line",
});
split.lines.forEach((line) => {
const wrapper = document.createElement("div");
wrapper.className = "block-line-wrapper";
line.parentNode.insertBefore(wrapper, line);
wrapper.appendChild(line);
const block = document.createElement("div");
block.className = "block-revealer";
block.style.backgroundColor = blockColor;
wrapper.appendChild(block);
lines.push(line);
blocks.push(block);
});
gsap.set(lines, { opacity: 0 });
gsap.set(blocks, { scaleX: 0, transformOrigin: "left center" });
element.classList.add("is-ready");
blocks.forEach((block, index) => {
const line = lines[index];
const tl = gsap.timeline({
paused: true,
delay: index * stagger,
});
tl.to(block, { scaleX: 1, duration: duration, ease: "power4.inOut" });
tl.set(line, { opacity: 1 });
tl.set(block, { transformOrigin: "right center" });
tl.to(block, { scaleX: 0, duration: duration, ease: "power4.inOut" });
ScrollTrigger.create({
trigger: element,
start: "top 90%",
once: true,
onEnter: () => tl.play(),
});
});
});
});<!-- Red block (default) -->
<h1 data-block-reveal>
Your heading text here.
</h1>
<!-- Custom black block -->
<p data-block-reveal data-block-color="#000000">
Your paragraph text here.
</p>
<!-- Custom color + slower animation -->
<h2 data-block-reveal data-block-color="#3b82f6" data-duration="1" data-stagger="0.2">
Custom styled text.
</h2>| Attribute | Default | Description |
|---|---|---|
data-block-reveal | required | Enables the animation |
data-block-color | #fe0100 | Color of the reveal block |
data-duration | 0.75 | Animation speed in seconds |
data-stagger | 0.15 | Delay between each line |
/* Add this if you want centered text */
section h1 .block-line-wrapper,
section p .block-line-wrapper {
margin: 0 auto;
}