An infinite, draggable image gallery with dynamic loading of content as you navigate. Originally created for airimagination.nike.com/gallery.
- ♾️ Infinite scrolling in all directions
- 🖱️ Smooth dragging with inertia
- 📱 Mobile and desktop support
- 🌈 Dynamic loading of content as you navigate
- 🖼️ Responsive grid layout
- 🔄 Efficient rendering with virtualization
- 🎨 Beautiful animations powered by GSAP
Check out the live demo at gallery-prototype-three.vercel.app
- Next.js - React framework
- TypeScript - Type safety
- GSAP - Animation library
- Valtio - State management
- Tailwind CSS - Styling
- Unsplash API - Image source
- Node.js 18.x or later
- npm or yarn
-
Clone the repository:
git clone https://github.com/artokun/gallery-prototype.git cd gallery-prototype -
Install dependencies:
npm install # or yarn install -
Set up environment variables:
cp .env.example .env
-
Add your Unsplash API key to the
.envfile:NEXT_PUBLIC_UNSPLASH_ACCESS_KEY=your-unsplash-access-key
-
If you're using GSAP premium features, add your GSAP token:
GSAP_TOKEN=your-gsap-token
Run the development server:
npm run dev
# or
yarn devOpen http://localhost:3000 with your browser to see the result.
The gallery uses a chunking system to efficiently render and manage an infinite grid of images:
- Images are fetched from the Unsplash API and organized into chunks
- As you navigate, new chunks are dynamically loaded in the direction you're moving
- GSAP powers the smooth dragging and inertia effects
- Valtio manages the state of chunks and their positions
- The gallery automatically detects when you're approaching the edge of the current content and loads more
The gallery is built with three main components that work together:
The top-level component that manages the overall gallery and its draggable behavior.
Key Responsibilities:
- Implements GSAP's Draggable for smooth dragging with inertia
- Captures mouse wheel events for scrolling in all directions
- Uses Valtio for state management via
chunkState - Sets up the first chunk (0,0) when the gallery loads
Key Features:
// GSAP plugin registration
gsap.registerPlugin(Observer, Draggable, InertiaPlugin);
// Draggable setup for mouse/touch interaction
Draggable.create(anchorRef.current, {
inertia: true,
zIndexBoost: false,
minDuration: 0,
});This component manages a section (chunk) of the gallery and handles the loading of adjacent chunks.
Key Responsibilities:
- Positions itself based on x,y coordinates in the grid
- Uses IntersectionObserver to detect when edges are visible
- Creates new chunks when edges come into view
- Removes chunks when they're no longer visible
- Defines the grid layout for a set of images
Key Features:
// Creating new chunks when edges are visible
const createChunk = contextSafe(
(entry: IntersectionObserverEntry, x: number, y: number) => {
if (entry.isIntersecting) {
chunkState.chunkElements.set(`${x},${y}`, {
x, y, startIndex: chunkState.currentIndex,
});
chunkState.currentIndex += CHUNK_ITEMS;
}
}
);This component handles the display and animation of individual images within the grid.
Key Responsibilities:
- Fetches and displays images from the shared state
- Animates the appearance/disappearance of images
- Uses IntersectionObserver to only show visible images
- Handles different size variants (small/large)
Key Features:
// Animation when item becomes visible
if (entry.isIntersecting) {
gsap.to(itemRef.current, {
opacity: 1,
scale: 1,
duration: 0.3,
ease: "power2.inOut",
});
}-
Initialization:
GridChunksinitializes the draggable container and sets up the first chunk- The first chunk (0,0) is created with a starting index of 0
-
User Interaction:
- User drags or scrolls the gallery
GridChunksupdates the position of the anchor element
-
Dynamic Loading:
- As the user navigates, edge detectors in
GridChunkcome into view - IntersectionObservers detect when edges are visible
- New chunks are created in the appropriate directions
- Images in those chunks are loaded and animated in
- As the user navigates, edge detectors in
-
Optimization:
- Chunks that are far from view are removed to save memory
- Images only animate and load when they're visible
- The modulo operation (
index % images.length) allows infinite cycling through available images
This architecture creates an efficient infinite scrolling experience by only rendering what's visible or about to be visible, dynamically loading and unloading content as needed, and using GSAP for smooth animations and interactions.
You can customize various aspects of the gallery:
- Adjust chunk size in
state/chunks.ts - Modify the grid layout in
components/GridChunk.tsx - Change the image source in
app/page.tsx - Adjust animation parameters in
components/GridItem.tsx
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.
