Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save rsp2k/b729bacaeaf4969b76f68354593b34a7 to your computer and use it in GitHub Desktop.
Save rsp2k/b729bacaeaf4969b76f68354593b34a7 to your computer and use it in GitHub Desktop.
[gsap/component] ❍ Full Screen Image Zoom on Hover
<div id="background-image-container">
<img id="background-image" crossorigin="anonymous" alt="">
</div>
<div class="container">
<div class="projects-container">
<!-- Projects will be rendered here by JavaScript -->
</div>
</div>
// Project data with minimalist titles
const projects = [
{
id: 1,
title: "Silence",
year: "2021",
image:
"https://cdn.cosmos.so/7d47d4e2-0eff-4e2f-9734-9d24a8ba067e?format=jpeg"
},
{
id: 2,
title: "Resonance",
year: "2022",
image:
"https://cdn.cosmos.so/5eee2d2d-3d4d-4ae5-96d4-cdbae70a2387?format=jpeg"
},
{
id: 3,
title: "Essence",
year: "2022",
image:
"https://cdn.cosmos.so/def30e8a-34b2-48b1-86e1-07ec5c28f225?format=jpeg"
},
{
id: 4,
title: "Void",
year: "2023",
image:
"https://cdn.cosmos.so/44d7cb23-6759-49e4-9dc1-acf771b3a0d1?format=jpeg"
},
{
id: 5,
title: "Presence",
year: "2023",
image:
"https://cdn.cosmos.so/7712fe42-42ca-4fc5-9590-c89f2db99978?format=jpeg"
},
{
id: 6,
title: "Flow",
year: "2024",
image:
"https://cdn.cosmos.so/cbee1ec5-01b6-4ffe-9f34-7da7980454cf?format=jpeg"
},
{
id: 7,
title: "Clarity",
year: "2024",
image:
"https://cdn.cosmos.so/2e91a9d1-db85-4499-ad37-6222a6fea23b?format=jpeg"
},
{
id: 8,
title: "Breath",
year: "2024",
image:
"https://cdn.cosmos.so/ff2ac3d3-fa94-4811-89f6-0d008b27e439?format=jpeg"
},
{
id: 9,
title: "Stillness",
year: "2025",
image:
"https://cdn.cosmos.so/c39a4043-f489-4406-8018-a103a3f89802?format=jpeg"
},
{
id: 10,
title: "Surrender",
year: "2025",
image:
"https://cdn.cosmos.so/e5e399f2-4050-463b-a781-4f5a1615f28e?format=jpeg"
}
];
document.addEventListener("DOMContentLoaded", function () {
const projectsContainer = document.querySelector(".projects-container");
const backgroundImage = document.getElementById("background-image");
// Render projects
renderProjects(projectsContainer);
// Initialize animations
initialAnimation();
// Preload images
preloadImages();
// Add hover events to project items
setupHoverEvents(backgroundImage, projectsContainer);
});
// Render project items
function renderProjects(container) {
projects.forEach((project) => {
const projectItem = document.createElement("div");
projectItem.classList.add("project-item");
projectItem.dataset.id = project.id;
projectItem.dataset.image = project.image;
projectItem.innerHTML = `
<div class="project-title">${project.title}</div>
<div class="project-year">${project.year}</div>
`;
container.appendChild(projectItem);
});
}
// Initial animation for project items
function initialAnimation() {
const projectItems = document.querySelectorAll(".project-item");
// Set initial state
projectItems.forEach((item, index) => {
item.style.opacity = "0";
item.style.transform = "translateY(20px)";
// Animate in with staggered delay
setTimeout(() => {
item.style.transition = "opacity 0.8s ease, transform 0.8s ease";
item.style.opacity = "1";
item.style.transform = "translateY(0)";
}, index * 60);
});
}
// Setup hover events for project items
function setupHoverEvents(backgroundImage, projectsContainer) {
const projectItems = document.querySelectorAll(".project-item");
let currentImage = null;
let zoomTimeout = null;
// Preload all images to ensure immediate display
const preloadedImages = {};
projects.forEach((project) => {
const img = new Image();
img.crossOrigin = "anonymous";
img.src = project.image;
preloadedImages[project.id] = img;
});
projectItems.forEach((item) => {
item.addEventListener("mouseenter", function () {
const imageUrl = this.dataset.image;
// Clear any pending zoom timeout
if (zoomTimeout) {
clearTimeout(zoomTimeout);
}
// Reset transform and transition
backgroundImage.style.transition = "none";
backgroundImage.style.transform = "scale(1.2)";
// Immediately show the new image
backgroundImage.src = imageUrl;
backgroundImage.style.opacity = "1";
// Force browser to acknowledge the scale reset before animating
// This ensures the zoom effect happens every time
requestAnimationFrame(() => {
requestAnimationFrame(() => {
// Re-enable transition and animate to scale 1.0
backgroundImage.style.transition =
"transform 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94)";
backgroundImage.style.transform = "scale(1.0)";
});
});
// Update current image
currentImage = imageUrl;
});
});
// Handle mouse leaving the projects container
projectsContainer.addEventListener("mouseleave", function () {
// Hide the image
backgroundImage.style.opacity = "0";
currentImage = null;
});
}
// Preload images
function preloadImages() {
projects.forEach((project) => {
const img = new Image();
img.crossOrigin = "anonymous";
img.src = project.image;
});
}
@font-face {
src: url("https://fonts.cdnfonts.com/css/pp-neue-montreal") format("woff2");
font-family: "PP Neue Montreal", sans-serif;
font-weight: 400;
}
*,
*:before,
*:after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--warm-off-black: #1a1917;
--warm-off-white: #f8f5f2;
}
html,
body {
height: 100%;
overflow: hidden;
}
body {
font-family: "PP Neue Montreal", sans-serif;
font-weight: 700;
font-size: 18px;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background-color: var(--warm-off-black);
text-transform: uppercase;
letter-spacing: -0.03em;
color: var(--warm-off-white);
position: relative;
margin: 0;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
}
/* Animated noise effect */
body::before {
content: "";
position: fixed;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: transparent
url("http://assets.iceable.com/img/noise-transparent.png") repeat 0 0;
background-size: 300px 300px;
animation: noise-animation 0.3s steps(5) infinite;
opacity: 0.9;
will-change: transform;
z-index: 100;
pointer-events: none;
}
@keyframes noise-animation {
0% {
transform: translate(0, 0);
}
10% {
transform: translate(-2%, -3%);
}
20% {
transform: translate(-4%, 2%);
}
30% {
transform: translate(2%, -4%);
}
40% {
transform: translate(-2%, 5%);
}
50% {
transform: translate(-4%, 2%);
}
60% {
transform: translate(3%, 0);
}
70% {
transform: translate(0, 3%);
}
80% {
transform: translate(-3%, 0);
}
90% {
transform: translate(2%, 2%);
}
100% {
transform: translate(1%, 0);
}
}
/* Container for centering */
.container {
width: 100%;
max-width: 1000px; /* Increased width */
height: 100%;
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
position: relative;
}
/* Full-screen background image */
#background-image-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 0;
overflow: hidden;
}
#background-image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
transform: scale(1.2); /* Start zoomed in */
transition: transform 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94);
opacity: 0;
}
/* Projects Container */
.projects-container {
width: 100%;
position: relative;
z-index: 10;
max-height: 80vh;
overflow-y: auto;
padding: 20px;
/* Hide scrollbar for Chrome, Safari and Opera */
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE and Edge */
}
/* Hide scrollbar for Chrome, Safari and Opera */
.projects-container::-webkit-scrollbar {
display: none;
}
/* Project Item Styles */
.project-item {
position: relative;
display: flex;
justify-content: space-between;
padding: 0.7rem 0; /* Further reduced padding for smaller line height */
border-bottom: 1px solid rgba(248, 245, 242, 0.1);
cursor: pointer;
}
/* Project title and year */
.project-title,
.project-year {
font-size: 1.8rem;
position: relative;
z-index: 2;
mix-blend-mode: exclusion;
}
/* List view hover animation with exclusion blend mode */
.project-item::before {
content: "";
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 0;
background-color: var(--warm-off-white);
z-index: 1;
pointer-events: none;
transition: height 0.2s cubic-bezier(0.445, 0.05, 0.55, 0.95) 0s;
}
.project-item:hover::before {
height: 100%;
}
.project-item:not(:hover)::before {
transition-duration: 0.8s;
}
/* Responsive Styles */
@media (max-width: 768px) {
.project-title,
.project-year {
font-size: 1.4rem;
}
}
@media (max-width: 480px) {
.project-item {
flex-direction: column;
gap: 0.5rem;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment