Skip to content

Instantly share code, notes, and snippets.

@stanwmusic
Created December 16, 2025 08:38
Show Gist options
  • Select an option

  • Save stanwmusic/e72a2806e4b13a73f9aa0f5b041d8aaf to your computer and use it in GitHub Desktop.

Select an option

Save stanwmusic/e72a2806e4b13a73f9aa0f5b041d8aaf to your computer and use it in GitHub Desktop.
Image Zoom in with ScrollTrigger
<main>
<div class="hero-container">
<section class="hero">
<div class="hero__content">
<div class="hero__bg"></div>
<h1 class="hero__title font-bold">Hobbiton</h1>
</div>
<div class="hero__cover">
<img class="hero__cover-img" src="https://assets.codepen.io/204808/hobbit-hole.png" alt="" />
</div>
</section>
</div>
<section class="section-stick min-h-screen bg-black flex justify-center items-center text-white">
<p class="opacity-reveal text-7xl text-center w-3/5">If ever you are passing my way, don't wait to knock! Tea is at four; but any of you are welcome at any time.</p>
</section>
<section class="hobbiton">
<img class="hobbiton-img" src="https://assets.codepen.io/204808/hobitton.jpg" alt="" />
</section>
</main>
gsap.registerPlugin(ScrollTrigger, ScrollSmoother);
ScrollSmoother.create({
smooth: 1,
effects: true,
normalizeScroll: true
});
gsap
.timeline({
scrollTrigger: {
trigger: ".hero-container",
start: "top top",
end: "+=150%",
pin: true,
scrub: 1
// markers: true
}
})
.to(".hero__cover-img", {
scale: 2,
z: 350,
transformOrigin: "center center",
ease: "power1.inOut"
})
.to(
".hero__cover",
{
"--overlay-opacity": 0,
ease: "power1.inOut"
},
"<" // sync with image zoom
)
.to(
".hero__bg",
{
scale: 1.1,
filter: "blur(0px) brightness(1)",
ease: "power1.inOut"
},
"<"
)
.to(
".hero__title",
{
scale: 1,
xPercent: -50,
yPercent: -50,
opacity: 1,
filter: "blur(0px)",
ease: "power1.inOut"
},
"<"
);
const splitLetters = SplitText.create(
document.querySelector(".opacity-reveal")
);
gsap.set(splitLetters.chars, { opacity: "0.2", y: 0 });
gsap
.timeline({
scrollTrigger: {
trigger: ".section-stick",
pin: true,
start: "center center",
end: "+=1500",
//markers: true,
scrub: 1
}
})
.to(splitLetters.chars, {
opacity: "1",
duration: 1,
ease: "none",
stagger: 1
})
.to({}, { duration: 10 })
.to(".opacity-reveal", {
opacity: "0",
scale: 1.2,
duration: 50
});
<script src="https://unpkg.com/gsap@3/dist/gsap.min.js"></script>
<script src="https://unpkg.com/gsap@3/dist/ScrollSmoother.min.js"></script>
<script src="https://unpkg.com/gsap@3/dist/ScrollTrigger.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<script src="https://unpkg.com/gsap@3/dist/SplitText.min.js"></script>
@function pxToRem($pixel) {
@return $pixel / 16 + rem;
}
:root {
--black: #1f1f1f;
--white: #fff;
}
body {
background-color: var(--black);
color: var(--white);
font-family: "Roboto", sans-serif;
font-optical-sizing: auto;
font-size: 20px;
line-height: 1.5;
}
.hero__content {
height: 100vh;
overflow-x: hidden;
width: 100%;
}
.hero__bg {
background-image: url("https://assets.codepen.io/204808/hobbiton.jpg");
background-position: center center;
background-repeat: no-repeat;
background-size: cover;
filter: blur(3px) brightness(1.5); /* initial fog */
height: 100vh;
width: 100%;
will-change: filter;
}
.hero__title {
color: var(--black);
filter: blur(10px);
font-size: clamp(3.125rem, 17.321vw + -1.357rem, 12.5rem);
line-height: clamp(4.688rem, 21.363vw + -0.84rem, 16.25rem);
left: 50%;
margin: 0;
opacity: 0;
padding: 0;
position: absolute;
top: 50vh;
transform: translate(-50%, -50%) scale(0.5);
z-index: 100;
}
.hero__cover {
--overlay-opacity: 1;
position: absolute;
left: 0;
top: 0;
height: 100vh;
width: 100vw;
perspective: 500px;
overflow: hidden;
z-index: 2;
}
.hero__cover {
&::after {
background: radial-gradient(
circle,
rgba(0, 0, 0, 0) 20%,
rgba(0, 0, 0, 1) 90%
);
content: "";
inset: 0;
opacity: var(--overlay-opacity);
position: absolute;
pointer-events: none;
}
}
.hero__cover-img {
height: 100%;
object-fit: cover;
width: 100%;
pointer-events: none;
}
.section {
max-width: 600px;
margin: 0 auto;
padding: 100px 0;
}
.hobbiton {
position: relative;
&::after {
background: radial-gradient(
circle,
rgba(0, 0, 0, 0) 40%,
rgba(0, 0, 0, 0.7) 90%
);
content: "";
inset: 0;
opacity: var(--overlay-opacity);
position: absolute;
pointer-events: none;
}
}
.hobbiton-img {
height: 100vh;
object-fit: cover;
width: 100%;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/modern-normalize/3.0.1/modern-normalize.min.css" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment