Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save wimleers/b7ba929643b57c844a2f28a2ee5c5b41 to your computer and use it in GitHub Desktop.

Select an option

Save wimleers/b7ba929643b57c844a2f28a2ee5c5b41 to your computer and use it in GitHub Desktop.
Images Go Go
import { h } from 'preact';
import { useState, useEffect, useRef } from 'preact/hooks';
const ErraticGallery = ({ images = [] }) => {
const containerRef = useRef(null);
const [items, setItems] = useState([]);
useEffect(() => {
if (!images || !Array.isArray(images)) return;
const initialItems = images.map((item) => {
// Extract the URL safely from the Drupal Object
let src = "";
if (typeof item === 'string') {
src = item;
} else if (item && typeof item === 'object') {
src = item.url || item.uri || item.src || "";
}
return {
src: src || "https://placehold.co/400x400?text=Missing+URL",
x: Math.random() * 50,
y: Math.random() * 50,
vx: (Math.random() - 0.5) * 12,
vy: (Math.random() - 0.5) * 12,
rotation: 0,
vr: (Math.random() - 0.5) * 15
};
});
setItems(initialItems);
}, [images]);
useEffect(() => {
if (items.length === 0) return;
let frameId;
const update = () => {
setItems((prev) =>
prev.map((item) => {
const container = containerRef.current;
if (!container) return item;
const w = container.offsetWidth;
const h = container.offsetHeight;
const size = 160; // 2x larger than before
let { x, y, vx, vy, rotation, vr } = item;
x += vx; y += vy; rotation += vr;
// Bounce logic with speed-up multiplier
if (x <= 0 || x >= w - size) {
vx *= -1.02; // Get faster on every bounce
vr = (Math.random() - 0.5) * 30; // Randomize spin on hit
}
if (y <= 0 || y >= h - size) {
vy *= -1.02;
vr = (Math.random() - 0.5) * 30;
}
// Speed limit to keep it from teleporting
vx = Math.min(Math.max(vx, -25), 25);
vy = Math.min(Math.max(vy, -25), 25);
return { ...item, x, y, vx, vy, rotation };
})
);
frameId = requestAnimationFrame(update);
};
frameId = requestAnimationFrame(update);
return () => cancelAnimationFrame(frameId);
}, [items.length]);
return (
<div
ref={containerRef}
style={{
width: '100%',
height: '600px',
position: 'relative',
overflow: 'hidden',
background: '#111',
borderRadius: '12px',
border: '5px solid #5419ad' // Drupal purple
}}
>
{items.map((item, i) => (
<img
key={i}
src={item.src}
style={{
position: 'absolute',
left: `${item.x}px`,
top: `${item.y}px`,
width: '400px', // 2x larger
height: '400px', // 2x larger
objectFit: 'cover',
borderRadius: '50%',
border: '4px solid #fff',
transform: `rotate(${item.rotation}deg)`,
boxShadow: '0 0 40px rgba(255,255,255,0.3)',
pointerEvents: 'none'
}}
onError={(e) => {
// If Drupal path is relative, try to fix it automatically
if (!item.src.startsWith('http') && !item.src.startsWith('data:')) {
e.target.src = window.location.origin + item.src;
}
}}
/>
))}
</div>
);
};
export default ErraticGallery;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment