Web Animations Medium
Clip-Path Image Reveal
Reveal photos with animated clip-path masks as each block enters the viewport using GSAP ScrollTrigger.
Open in Lab
MCP
html css gsap javascript
Targets: JS HTML
Code
[hidden] {
display: none !important;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: system-ui, sans-serif;
background: #f4f2ee;
color: #1a1a1a;
}
.hero {
min-height: 50vh;
display: grid;
place-content: center;
text-align: center;
padding: 2rem;
}
.grid {
max-width: 900px;
margin: 0 auto;
padding: 2rem 1.25rem 40vh;
display: flex;
flex-direction: column;
gap: 6rem;
}
.reveal {
margin: 0;
}
.frame {
overflow: hidden;
border-radius: 8px;
clip-path: inset(0 100% 0 0);
}
.reveal--right .frame {
clip-path: inset(0 0 0 100%);
}
.frame img {
display: block;
width: 100%;
aspect-ratio: 16 / 10;
object-fit: cover;
transform: scale(1.08);
}
figcaption {
margin-top: 0.75rem;
font-size: 0.8rem;
letter-spacing: 0.12em;
text-transform: uppercase;
color: #666;
}const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
gsap.registerPlugin(ScrollTrigger);
document.querySelectorAll("[data-reveal]").forEach((block) => {
const frame = block.querySelector(".frame");
const img = block.querySelector("img");
const fromClip = block.classList.contains("reveal--right")
? "inset(0 0 0 100%)"
: "inset(0 100% 0 0)";
if (reduced) {
gsap.set(frame, { clipPath: "inset(0 0 0 0)" });
gsap.set(img, { scale: 1 });
return;
}
const tl = gsap.timeline({
scrollTrigger: {
trigger: block,
start: "top 80%",
end: "top 35%",
scrub: 0.5,
},
});
tl.fromTo(frame, { clipPath: fromClip }, { clipPath: "inset(0 0 0 0)", ease: "power2.out" }).fromTo(
img,
{ scale: 1.12 },
{ scale: 1, ease: "power2.out" },
0
);
});<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Clip Reveal Images</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<header class="hero">
<h1>Clip reveals</h1>
<p>Scroll to unmask each frame.</p>
</header>
<main class="grid">
<figure class="reveal" data-reveal>
<div class="frame"><img src="https://images.unsplash.com/photo-1469474968028-56623f02e42e?w=800&q=80" alt="" /></div>
<figcaption>Horizon</figcaption>
</figure>
<figure class="reveal reveal--right" data-reveal>
<div class="frame"><img src="https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=800&q=80" alt="" /></div>
<figcaption>Canopy</figcaption>
</figure>
<figure class="reveal" data-reveal>
<div class="frame"><img src="https://images.unsplash.com/photo-1470071459604-3b5ec3a7fe05?w=800&q=80" alt="" /></div>
<figcaption>Fog line</figcaption>
</figure>
</main>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/ScrollTrigger.min.js"></script>
<script src="script.js"></script>
</body>
</html>Clip-Path Image Reveal
Images start fully clipped and expand into view with a directional mask — a lightweight alternative to heavy video transitions.