@ivanalbizu/astro-particle-image-viewer
v0.0.4
Published
WebGL particle image viewer for Astro with stunning animation effects using Three.js
Maintainers
Readme
Astro Particle Image Viewer
An Astro component that displays images with a stunning WebGL particle animation effect using Three.js. When users click on a thumbnail, the image explodes into particles that animate and reassemble into a fullscreen view.
Features
- WebGL-powered particle animations using Three.js
- Smooth transitions between images
- Keyboard navigation (Arrow keys, Escape)
- Touch-friendly navigation buttons
- Swipe gestures for mobile navigation
- Responsive design with mobile optimizations
- Customizable animation parameters
- Sliding window pagination with WCAG-compliant 44px touch targets
- Accessible with ARIA labels and focus management
- Focus-visible styles for keyboard users
- Respects accessibility preferences - uses SimpleLightbox fallback for reduced motion, low-performance devices, and no WebGL support
- Dynamic imports - only loads Three.js when needed (code-splitting)
- CSS custom properties for easy theming
- View Transitions compatible - works seamlessly with Astro View Transitions
Installation
npm install @ivanalbizu/astro-particle-image-viewerUsage
---
import { ParticleImageViewer } from '@ivanalbizu/astro-particle-image-viewer';
---
<ParticleImageViewer
config={{
openDuration: 2000,
closeDuration: 1200,
maxWidth: 1440,
}}
>
<img src="/image-1.jpg" width="300" alt="Description 1" />
<img src="/image-2.jpg" width="300" alt="Description 2" />
<img src="/image-3.jpg" width="300" alt="Description 3" />
</ParticleImageViewer>The component uses a slot, so you can add any <img> elements as children. Each image will automatically be wrapped in an accessible button.
Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| title | string | - | Optional title above the gallery |
| class | string | - | Additional CSS class for the wrapper |
| config | ParticleViewerConfig | {} | Animation configuration options |
ParticleViewerConfig
interface ParticleViewerConfig {
segments?: number; // Particle grid resolution (default: 180, mobile: 80)
padding?: number; // Image padding multiplier (default: 1.1)
openDuration?: number; // Open animation duration in ms (default: 2000)
closeDuration?: number; // Close animation duration in ms (default: 1200)
crossfadeStart?: number; // When to start crossfade 0-1 (default: 0.2)
srcAttribute?: string; // Attribute for image source (default: 'src')
maxWidth?: number; // Maximum image width in pixels (default: 0 = no limit)
}Lazy Loading Support
Use srcAttribute to work with lazy loading libraries that use custom attributes like data-src:
<ParticleImageViewer
config={{
srcAttribute: 'data-src'
}}
>
<img data-src="/image-1.jpg" src="/placeholder.jpg" alt="Description" />
</ParticleImageViewer>Styling
The component uses CSS custom properties that you can override:
.particle-image-viewer-wrapper {
/* Colors */
--piv-title-color: #333;
--piv-text-color: white;
--piv-text-color-inverse: #111;
--piv-shadow: rgba(0, 0, 0, 0.1);
--piv-overlay-bg: rgba(0, 0, 0, 0.25);
--piv-caption-bg: rgba(0, 0, 0, 0.5);
--piv-button-bg: rgba(0, 0, 0, 0.6);
--piv-button-bg-hover: rgba(0, 0, 0, 0.8);
--piv-button-border: rgba(255, 255, 255, 0.5);
--piv-button-border-hover: rgba(255, 255, 255, 0.8);
--piv-button-shadow: rgba(0, 0, 0, 0.3);
--piv-active-bg: rgba(255, 255, 255, 0.9);
--piv-active-glow: rgba(255, 255, 255, 0.4);
--piv-focus-ring: rgba(255, 255, 255, 0.9);
/* Spacing */
--piv-spacing-sm: 8px;
--piv-spacing-md: 20px;
--piv-spacing-lg: 30px;
--piv-caption-bottom: 90px;
--piv-pagination-bottom: 30px;
/* Sizes */
--piv-button-size: 50px;
--piv-dot-size: 44px;
--piv-border-radius: 8px;
--piv-border-radius-round: 50%;
--piv-border-radius-pill: 20px;
--piv-border-width: 2px;
/* Typography */
--piv-font-size-close: 32px;
--piv-font-size-nav: 24px;
--piv-font-size-caption: 18px;
--piv-font-size-dot: 14px;
/* Blur */
--piv-blur-overlay: 5px;
--piv-blur-button: 4px;
--piv-blur-caption: 10px;
/* Transitions */
--piv-transition-fast: 0.2s ease;
--piv-transition-normal: 0.3s ease;
--piv-transition-slow: 0.5s ease;
/* Z-index */
--piv-z-container: 1000;
--piv-z-controls: 10;
--piv-z-pagination: 20;
}Keyboard Navigation
When the viewer is open:
- Escape: Close the viewer
- Arrow Left: Previous image
- Arrow Right: Next image
- Tab: Navigate between controls (with focus trap)
Touch Navigation
- Swipe left: Next image
- Swipe right: Previous image
- Tap outside image: Close viewer
Accessibility & Performance
The component automatically provides a simplified lightbox experience when:
- User has
prefers-reduced-motion: reduceenabled - Browser has Data Saver mode enabled
- Connection is 2G or slow-2g
- Device has both low memory (<4GB) AND few CPU cores (≤2)
- WebGL is not supported
When any of these conditions are detected, SimpleLightbox is used instead of ParticleViewer. This provides the same navigation functionality without WebGL animations, respecting user preferences and device capabilities.
Privacy browser compatible: Works with Brave and other privacy-focused browsers. Hardware detection requires both low memory AND low cores to avoid false positives from browsers that falsify individual values.
Manual fallback detection
import { shouldUseFallback, getFallbackReason } from '@ivanalbizu/astro-particle-image-viewer';
if (shouldUseFallback()) {
console.log('Using SimpleLightbox:', getFallbackReason());
// 'prefers-reduced-motion' | 'low-performance-device' | 'webgl-not-supported'
}View Transitions Support
The component is fully compatible with Astro View Transitions. It automatically:
- Cleans up resources before page navigation (
astro:before-swap) - Reinitializes after navigation completes (
astro:page-load) - Prevents memory leaks by properly disposing WebGL contexts and event listeners
- Works without View Transitions - the component functions normally on pages without View Transitions enabled
No additional configuration is needed. The component handles both scenarios transparently.
Advanced Usage
For more control, you can use the classes directly:
import { ParticleViewer, SimpleLightbox } from '@ivanalbizu/astro-particle-image-viewer';
// WebGL version with particle animations
const viewer = new ParticleViewer(
'.my-container',
'.my-image-buttons',
{ openDuration: 3000, srcAttribute: 'data-src' }
);
// Simple version without animations (for reduced motion)
const lightbox = new SimpleLightbox(
'.my-container',
'.my-image-buttons',
'data-src' // optional: custom source attribute
);
// Clean up when done
viewer.destroy();Development
# Install dependencies
npm install
# Start development server
npm run dev
# Build site
npm run build
# Publish to npm
npm publish --access publicLicense
MIT
