Advertisement

Fluid Shared Element Image Gallery

| by Vladimir | 2 min read | code by pretender
Intermediate

Tech & Dependencies

HTML SCSS JavaScript

Features

  • FLIP Animation
  • Shared Element
  • Hardware Accelerated

Browser Support

Chrome 36+ Edge 79+ Firefox 48+ Safari 13.1+

Core

This is a Fluid Shared Element Image Gallery. It connects a thumbnail grid to a full-screen detail view through continuous spatial morphing. Its function is to eliminate context loss, carrying the user’s visual focus seamlessly from a macro overview to a micro inspection.

Specs

  • Weight: ~3 KB. Zero dependencies.
  • Performance: High. The transition strictly animates transform (translate and scale), ensuring GPU acceleration without triggering expensive DOM reflows during the animation cycle.
  • Theming / Customization: Layout is governed by a few SCSS variables ($app-width, $app-height).
  • Responsiveness: Handled via CSS Flexbox and vmin units, ensuring the application container retains its proportions across devices.
  • Web APIs: Web Animations API (element.animate), DOM Rect API.
  • Graceful Degradation: [!] Fundamentally relies on JavaScript for layout switching and mathematical calculation. If JavaScript fails, the detail view remains hidden permanently. Keyboard navigation and screen reader attributes are absent, severely breaking accessibility.

Anatomy

  • HTML: A dual-scene architecture. .scene.-gallery holds the base grid. .scene.-detail serves as the destination container.
  • CSS (SCSS): Establishes the spatial boundaries. It uses absolute positioning for the scenes to allow them to overlap perfectly in the Z-axis. transform-origin: top left is applied to elements to simplify the scaling math.
  • JS: The logic engine. It binds click events, retrieves image attributes, calculates the geometric differences between the thumbnail and the target container, and executes the native animate method.

Logic

The architectural core of this snippet is the manual implementation of the FLIP technique (First, Last, Invert, Play) using the native Web Animations API.

let firstRect = itemImage.getBoundingClientRect();
let lastRect = detailItem.getBoundingClientRect();

detailItem.animate([
  {
    transform: `
      translateX(${firstRect.left - lastRect.left}px)
      translateY(${firstRect.top - lastRect.top}px)
      scale(${firstRect.width / lastRect.width})
    `
  },
  { transform: `translateX(0) translateY(0) scale(1)` }
], { duration: 600, easing: 'cubic-bezier(0.2, 0, 0.2, 1)' });

Instead of moving the original DOM node, the script hides the thumbnail and shows the destination image in its final state (Last). It immediately calculates the difference between the starting coordinates (firstRect) and the ending coordinates (lastRect). It applies a negative transform (Invert) to force the large image to visually match the thumbnail’s exact size and position. Finally, it animates those transforms back to zero (Play). This ensures perfect, jank-free rendering.

Feel

Direct and mechanical. The image does not vanish into a loading screen; it physically travels to its new destination. The specific cubic-bezier(0.2, 0, 0.2, 1) easing gives the movement a fast, decisive acceleration followed by a smooth, deliberate settling. It feels less like navigating through discrete web pages and more like manipulating physical objects on a continuous plane.

Advertisement