Radial Explosion Zoom Gallery Effect
This is a “bomb blast” or “explosion zoom” gallery effect — where clicking a thumbnail pushes all other images outward and centers/zooms the selected one, creating a dramatic, explosive layout transition.
Below is a complete working example using HTML, CSS, and JavaScript (vanilla) to recreate this effect.
Features:
- Grid of images
- Click any image → it expands to center
- All other images shrink and scatter around it
- Click again or outside → reset to grid
- Smooth animations with CSS transitions
- Responsive & works on any screen
<div class="overlay" id="overlay"></div>
<div class="gallery" id="gallery"></div>
CSS
* {margin:0;padding:0;box-sizing:border-box;}
body {background:#000;overflow:hidden;height:100vh;font-family:sans-serif;}
.gallery {position:relative;width:100vw;height:100vh;}
.item {
position:absolute;
cursor:pointer;
border-radius:8px;
overflow:hidden;
box-shadow:0 4px 12px rgba(0,0,0,.5);
transition: all .7s cubic-bezier(.2,.8,.2,1);
transform-origin:center;
}
.item img {width:100%;height:100%;object-fit:cover;display:block;}
.like {position:absolute:absolute;top:6px;right:6px;width:26px;height:26px;
background:rgba(255,255,255,.2);border-radius:50%;display:flex;
align-items:center;justify-content:center;font-size:15px;opacity:0;
transition:.3s;backdrop-filter:blur(4px);}
.item:hover .like,.item.expanded .like{opacity:1;}
.caption {
position:absolute;bottom:0px;left:0;right:0;
background:linear-gradient(transparent,rgba(0,0,0,.8));
color:#fff;padding-bottom:25px;text-align:center;font-size:13px;opacity:0; line-height:25px;
transition:opacity .4s;pointer-events:none;
}
.item.expanded .caption{opacity:1;font-size:16px;}
.overlay{
position:fixed;inset:0;background:rgba(0,0,0,.8);
opacity:0;pointer-events:none;transition:opacity .5s;z-index:999;
}
.overlay.active{opacity:0.25;pointer-events:all;}
Javascript
createImages():- Makes all image boxes once
arrangeImages():- Puts them in a circle
expandImage():- Makes one big + pushes others away
closeExpanded():- Returns everything to normal
/* ============================================================= */
/* BOMB BLAST GALLERY - WITH HOVER LIFT EFFECT */
/* ============================================================= */
// === STEP 1: SETTINGS ===
const TOTAL_IMAGES = 50;
const EXPANDED_WIDTH = 380;
const EXPANDED_HEIGHT = 500;
const MIN_SIZE = 70;
const MAX_SIZE = 120;
// === STEP 2: Sample Artworks ===
const artworks = [
{ url: "https://upload.wikimedia.org/wikipedia/commons/thumb/5/5e/Frans_Hals_-_Portrait_of_Tieleman_Roosterman.jpg/800px-Frans_Hals_-_Portrait_of_Tieleman_Roosterman.jpg",
title: "Portrait of Tieleman Roosterman (1654)", artist: "Frans Hals", views: "213" },
{ url: "https://upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Van_Gogh_-_Starry_Night_-_Google_Art_Project.jpg/800px-Van_Gogh_-_Starry_Night_-_Google_Art_Project.jpg",
title: "Starry Night", artist: "Vincent van Gogh", views: "1.2M" },
{ url: "https://upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Mona_Lisa%2C_by_Leonardo_da_Vinci%2C_from_C2RMF_retouched.jpg/687px-Mona_Lisa%2C_by_Leonardo_da_Vinci%2C_from_C2RMF_retouched.jpg",
title: "Mona Lisa", artist: "Leonardo da Vinci", views: "3.5M" },
{ url: "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0a/The_Great_Wave_off_Kanagawa.jpg/800px-The_Great_Wave_off_Kanagawa.jpg",
title: "The Great Wave", artist: "Hokusai", views: "890K" }
];
// === STEP 3: Get HTML elements ===
const gallery = document.getElementById('gallery');
const overlay = document.getElementById('overlay');
// === STEP 4: Store image boxes and data ===
let imageBoxes = [];
let originalData = [];
let expandedIndex = null;
// === STEP 5: Create all images ===
function createImages() {
for (let i = 0; i < TOTAL_IMAGES; i++) {
const box = document.createElement('div');
box.className = 'item';
const img = document.createElement('img');
img.loading = 'lazy';
if (i < artworks.length) {
img.src = artworks[i].url;
box.dataset.title = artworks[i].title;
box.dataset.artist = artworks[i].artist;
box.dataset.views = artworks[i].views;
} else {
img.src = `https://picsum.photos/300/400?random=${i}`;
box.dataset.title = `Artwork #${i + 1}`;
box.dataset.artist = `Artist ${i + 1}`;
box.dataset.views = `${Math.floor(Math.random() * 2000)}K`;
}
const like = document.createElement('div');
like.className = 'like';
like.textContent = 'Heart';
const caption = document.createElement('div');
caption.className = 'caption';
caption.innerHTML = `
<strong>${box.dataset.title}</strong><br>
<em>${box.dataset.artist}</em><br>
<small>On view in ${box.dataset.views} museums</small>
`;
box.appendChild(img);
box.appendChild(like);
box.appendChild(caption);
gallery.appendChild(box);
imageBoxes.push(box);
const size = MIN_SIZE + Math.random() * (MAX_SIZE - MIN_SIZE);
const width = size;
const height = size * 1.3;
originalData[i] = { width, height };
// === HOVER EFFECT: Lift on mouse over ===
box.addEventListener('mouseenter', () => {
if (expandedIndex !== null) return; // Don't lift if something is expanded
box.style.zIndex = 10;
box.style.transform = 'scale(1.05)'; // Optional: slight grow
});
box.addEventListener('mouseleave', () => {
if (expandedIndex !== null) return;
box.style.zIndex = 1;
box.style.transform = 'scale(1)';
});
box.onclick = () => toggleImage(i);
}
arrangeImages();
}
// === STEP 6: Arrange in circle ===
function arrangeImages() {
const centerX = window.innerWidth / 2;
const centerY = window.innerHeight / 2;
imageBoxes.forEach((box, i) => {
const data = originalData[i];
const angle = (i / TOTAL_IMAGES) * 2 * Math.PI;
const distance = 100 + (i % 4) * 80;
const x = centerX + Math.cos(angle) * distance - data.width / 2;
const y = centerY + Math.sin(angle) * distance - data.height / 2;
data.x = x;
data.y = y;
box.style.left = x + 'px';
box.style.top = y + 'px';
box.style.width = data.width + 'px';
box.style.height = data.height + 'px';
box.style.opacity = 1;
box.style.transform = 'rotate(0deg) scale(1)';
box.style.zIndex = 1; // Reset z-index
});
}
// === STEP 7: Toggle expand ===
function toggleImage(index) {
if (expandedIndex === index) {
closeExpanded();
} else {
expandImage(index);
}
}
// === STEP 8: Expand image ===
function expandImage(index) {
if (expandedIndex !== null) closeExpanded();
expandedIndex = index;
overlay.classList.add('active');
const centerX = window.innerWidth / 2;
const centerY = window.innerHeight / 2;
imageBoxes.forEach((box, i) => {
const data = originalData[i];
if (i === index) {
box.classList.add('expanded');
box.style.left = (centerX - EXPANDED_WIDTH / 2) + 'px';
box.style.top = (centerY - EXPANDED_HEIGHT / 2) + 'px';
box.style.width = EXPANDED_WIDTH + 'px';
box.style.height = EXPANDED_HEIGHT + 'px';
box.style.zIndex = 1000;
box.style.transform = 'scale(1)';
} else {
const rect = box.getBoundingClientRect();
const boxCenterX = rect.left + rect.width / 2;
const boxCenterY = rect.top + rect.height / 2;
const dx = boxCenterX - centerX;
const dy = boxCenterY - centerY;
const distance = Math.sqrt(dx * dx + dy * dy) || 1;
const angle = Math.atan2(dy, dx);
const pushDistance = distance * 1.5 + 300;
const newX = centerX + Math.cos(angle) * pushDistance - data.width / 2;
const newY = centerY + Math.sin(angle) * pushDistance - data.height / 2;
box.style.left = newX + 'px';
box.style.top = newY + 'px';
box.style.width = data.width * 0.6 + 'px';
box.style.height = data.height * 0.6 + 'px';
box.style.opacity = 0.7;
box.style.transform = `rotate(${angle * 180 / Math.PI + 720}deg) scale(0.6)`;
box.style.zIndex = 10;
}
});
}
// === STEP 9: Close expanded ===
function closeExpanded() {
overlay.classList.remove('active');
expandedIndex = null;
imageBoxes.forEach((box, i) => {
const data = originalData[i];
box.classList.remove('expanded');
box.style.left = data.x + 'px';
box.style.top = data.y + 'px';
box.style.width = data.width + 'px';
box.style.height = data.height + 'px';
box.style.opacity = 1;
box.style.transform = 'rotate(0deg) scale(1)';
box.style.zIndex = 1;
});
}
// === STEP 10: Events ===
overlay.onclick = closeExpanded;
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') closeExpanded();
});
window.addEventListener('resize', () => {
if (expandedIndex === null) {
arrangeImages();
} else {
expandImage(expandedIndex);
}
});
// === STEP 11: Start ===
createImages();