Advertisement

Draggable 3D Parallax Image Ring

| by Vladimir | 3 min read | code by Tom Miller
Advanced

Tech & Dependencies

HTML CSS JavaScript
GSAP GSAP Draggable

Features

  • 3D CSS Transforms
  • GSAP Draggable
  • Parallax Backgrounds
  • Infinite Looping

Browser Support

Chrome 90+ Edge 90+ Safari 14+ Firefox 88+

Core

This Draggable 3D Parallax Image Ring creates a high-end, immersive gallery experience often associated with WebGL, but built entirely with DOM elements and GSAP. It arranges images in a 3D circle that users can spin with a swipe or mouse drag. The standout feature is the calculated parallax effect: as the ring rotates, the background position of each image shifts in the opposite direction, creating a convincing illusion of depth and window-like transparency.

Core Technique

The component leverages the power of CSS 3D transforms for structure and GSAP for interaction logic.

1. Constructing the Ring via transformOrigin

Instead of calculating complex (x, z) coordinates for every image using trigonometry (Math.sin, Math.cos), the code uses a clever CSS trick. By setting the transformOrigin to 50% 50% 500px (where 500px is the radius), the origin point is pushed far behind the element. When the element is rotated (rotateY), it swings around that distant center point, naturally forming a circle.

.set('.img', { 
  // Distribute 10 images around 360 degrees (36 deg each)
  rotateY: (i) => i * -36,
  
  // Push the pivot point 500px back along the Z-axis
  transformOrigin: '50% 50% 500px',
  
  // Move the element itself back to correct visual perspective
  z: -500,
  // ...
})

2. The Invisible Dragger

To avoid issues with 3D hit-testing (where clicking a rotated element might behave unexpectedly), the interaction is decoupled from the visuals. An invisible #dragger layer sits on top of everything. Draggable.create monitors this layer. When the user drags, the script calculates the delta (clientX) and applies rotation to the visual #ring element.

3. Calculated Parallax

The parallax effect creates the illusion that the images are “windows” into a stationary background. This is achieved by updating the backgroundPosition of every image on every frame of the drag. The getBgPos function uses gsap.utils.wrap to ensure the calculations loop seamlessly between 0 and 360 degrees, translating rotation degrees into pixel offsets.

function getBgPos(i) {
  return ( 
    // Wrap ensures smooth looping when rotation goes beyond 360
    -gsap.utils.wrap(
      0, 
      360, 
      gsap.getProperty(ring, 'rotationY') - 180 - i * 36
    ) / 360 * 400 
  ) + 'px 0px';
}

Accessibility (A11y)

  • Content: Images are loaded as background-image in CSS. Screen readers will perceive the gallery as 10 empty divs.
  • Interaction: The interaction relies entirely on mouse drag or touch swipes. It is completely inaccessible to keyboard users.
  • Fix:
    1. Use real <img> tags or add role="img" with aria-label to the divs.
    2. Implement keyboard listeners (Left/Right arrows) to trigger the GSAP rotation tween.
    3. Respect prefers-reduced-motion by reducing the entrance animation duration or disabling parallax.

Browser Support

This snippet relies on GSAP (which handles vendor prefixing perfectly) and standard CSS 3D transforms.

Advertisement