Homepage Change Log 07: The Final Photography Aperture Geometry and Animation
The final section uses outer photo blades, a central SVG aperture, and a mouse preview. Photos are split by clip-path, gaps come from background, and one progress value drives the entry motion.
The recent photography aperture became stable when the section stopped relying on visual patches. The final structure has three layers:
- Outer photo blades.
- Central aperture glyph.
- Mouse-following preview.
Each layer has its own job.
Splitting the Outer Photos
The outside uses up to six recent photos. Each photo becomes one aperture-like sector, not a normal pie slice.
The geometry comes from apertureWheelGeometry(progress):
const geometry = apertureWheelGeometry(progress);
const sector = geometry.sectors[index];
const imageStyle = {
clipPath: sector.clipPath,
WebkitClipPath: sector.clipPath,
opacity: geometry.progress,
filter: `blur(${8 * (1 - geometry.easedProgress)}px)`,
};clipPath is a generated polygon(...). It depends on the inner radius, outer radius, boundary angles, and side inset.
There is no SVG separator layer now. Each sector shifts its two side edges inward by PHOTO_SECTOR_GAP_WIDTH / 2. Neighboring sectors both give up space, so the panel background becomes the separator.
That means the hovered blade can lift as a single clipped object. No fixed rim or line layer remains to expose the old frame.
Building the Center Aperture
The center aperture is SVG geometry rather than a bitmap.
apertureGlyphGeometry(progress) generates six blade paths. Each blade runs from the outer ring toward the center hexagon. The center opening is the space those blades leave behind.
<circle r={glyph.bodyR} fill={apertureBasePaint} />
{glyph.blades.map((blade) => (
<path key={blade.index} d={blade.path} fill={bladePaint} />
))}
<path d={glyph.holePath} fill={cutoutPaint} />
<circle r={glyph.outerRingR} fill="none" stroke={ringPaint} />The hole path uses the same theme-aware surface color. It is not a hard white or black patch, so light and dark themes stay coherent.
Only the center aperture keeps its own ring. The photo disc does not need a fixed outer rim.
One Progress Value for Motion
The loading animation is driven by a single motionProgress value:
const motionProgress = useMotionValue(0);
const apertureProgress = progress;
const photoProgress = clampProgress((progress - 0.22) / 0.78);The center aperture starts moving first. The photo sectors begin slightly later. The result feels like the lens opening before the photos expand into place.
The animation is long enough to be visible, but it is still decorative:
animate(motionProgress, 1, {
duration: 2.45,
delay: 0.35,
ease: [0.22, 1, 0.36, 1],
});With prefers-reduced-motion, progress is set to 1 immediately.
Hover Moves the Whole Blade
The hover target is the clipped sector, not the image inside it:
<li className="motion-safe:hover:-translate-y-1 motion-safe:hover:scale-[1.018]">
<Link>
<Image className="object-cover" />
</Link>
</li>This matters because the shape itself is the interactive object. If only the image moves inside the sector, it looks like a picture sliding behind a frame. Moving the <li> makes the whole blade lift.
Because the separator is now background, not a separate line, nothing crosses the lifted blade.
Preview Handles the Full Photo
The circular aperture needs object-cover, so some photo detail may be cropped. Hover therefore shows a larger preview that follows the pointer.
previewPositionFromPoint(x, y, width, height)The preview chooses the left or right side based on pointer position and uses object-contain, so it can show more of the actual photo.
The wheel carries the visual metaphor. The preview carries inspection.
Why the Final Version Holds Together
The stable version mostly came from deleting things:
- No fixed photo rim.
- No SVG separator lines.
- No image-only hover scale.
- No hard black or white center fill.
The remaining system is geometric:
- Photo sectors are clipped polygons.
- Gaps are exposed background.
- Center blades are SVG paths.
- One progress value drives loading animation.
The lesson is simple enough to keep: do not use overlay layers to fake structure when the structure itself can create the visual.

