Skip to the page
TACTILE UI

05Object 05 · Reveals

Iris

A lens’s iris diaphragm, as a reveal. Curved blades pivot on their pins, close to a pinhole and open onto what lies beneath, the engraved f-stop ring turning with them.

Version
0.1.0
Size
26.8 KB gzipped
Access
With a Pass
Needs
iris.mjs + iris.css
Tests
25 tests · 11 states
On this page
  1. Use it
  2. Options
  3. Methods
  4. States
  5. How it works
  6. Blocks built with it
01

Use it

Reveal a hero image, a portfolio thumbnail or a page with a mechanism people recognise from a camera, not a fade: the content stays real, accessible DOM under the blades, and the opening is driven by the ring, hover, a press, the scroll, or a slow breathing loop for a loader.

Copy the iris folder from its package next to your page, link its stylesheet and construct it on an element. Every option has a default; the lab’s Code tab writes the same example at any settings, and for React, Vue, Svelte or a custom element (Using a framework).

<link rel="stylesheet" href="iris/iris.css">
<div id="iris" style="width: min(92vw, 560px); aspect-ratio: 1; border-radius: 0"></div>

<script type="module">
  import Iris from './iris/iris.mjs';

  const iris = new Iris(document.getElementById('iris'), {
    blades: 9,
    curvature: 0.6,
    finish: 'graphite',
  });

  // iris.update({ … });   change any option in place
  // iris.destroy();        and take it away again
</script>
02

Options

OptionTypeDefaultWhat it does
bladesnumber, 5 to 169Blades
curvaturenumber, 0 to 10.60 is straight blades and a sharp polygon; 1 is as round as a blade that still hides under the lip.
finishgraphite · ink · steel · brass'graphite'Finish
fitlens · cover'lens'Lens: the whole object, content seen only through it. Cover: the blades fill the frame and wide open clears it (a reveal).
ringbooleantrueOff leaves a thin lip. Lens fit only.
knurlbooleantrueKnurled grip
scalebooleantrueEngraved f-stop scale
driverdial · hover · press · scroll · breathe · manual'dial'dial: turn the ring (or arrow keys, a stop at a time). hover and press open it. scroll follows the page. breathe is a loader. manual is open() and close() only.
introbooleantrueFor dial, hover, press and manual.
restnumber, 0 to 10.35Where it settles: 1 is wide open (f/1.4), 0.35 about f/4, 0 shut. Hover and press open from here.
stiffnessnumber, 60 to 400180Spring stiffness
lightnumber, 0 to 3453150 is from above, clockwise.
readoutbooleantrueShow f-number
soundbooleanfalseSynthesised, no audio file. Starts only after you touch it.
stillopen · rest'open'open: fully open, because the content is the information. rest: the rest opening, for a loader.
labeltext'Aperture'The accessible name of the ring slider or the toggle button.
03

Methods

new Iris(element, options)
Builds it inside the element with these options.
opening
The current opening (0 shut … 1 wide open), as drawn.
aim
Where the opening is heading.
state
open(v = 1)
Open to v (0…1, default fully open) through the spring.
close()
Shut the iris.
toggle()
Between the rest opening and wide open.
replay()
Shut, then open again to whatever the driver asks for.
update(opts = {})
Change options in place.
destroy()
Stops it and cleans up after it.
04

States

  1. At restSettled at the rest opening (f/4 by default); the loop is asleep and costs nothing.
  2. Opening when first seenShut until the lens is a third in view, then a short beat and the blades open to rest through the spring.
  3. Turning the ringDrag round the ring or use the arrow keys: the blades follow the hand with no lag and click into the nearest f-stop on release; the slider's value text is the f-number. In the cover fit a sideways drag scrubs.
  4. Hover and pressHover (or focus inside) opens it wide and leaving returns it to rest; as a toggle it has its own small button (aria-expanded) and a click on anything not interactive in the object toggles too, so links in the content still work.
  5. Scroll-linkedShut as it enters from the bottom of the viewport, wide open by the time it is a little above the middle; the spring gives the blades weight.
  6. BreathingThe loader: stops down to a pinhole and opens back to rest every 3.6 s, and only while it can be seen.
  7. ShutThe blades meet at the centre in a pinwheel; the content under them stays in the accessibility tree.
  8. Off screen or hiddenThe loop pauses; a tab returning with a long frame advances the spring one clamped step.
  9. TouchA vertical swipe anywhere over the object scrolls the page (pan-y); the knurled ring is the grab zone and takes a drag in any direction; hover becomes a tap.
  10. Reduced motionNo animation: the first paint is fully open (or the rest opening, by choice), and every later change snaps.
  11. No WebGLPainted with Canvas 2D from the same linkage: blades stacked sector by sector in the exact shingle order, each lit from the lamp with a brushed grain, cast shadow and lit or shaded edge; the housing's turned sheen from the same formula. Same motion; also used if the WebGL context is lost.
05

How it works

Made of: Pivot linkage, Shingle stack, Sdf shading, Spring opening, Shared webgl context, Polar engraving texture, Sleeping loop, Canvas 2d fallback, Sector ordered painting, Touch grab zone.

Original. Modelled from how a real diaphragm is built (rigid blades on fixed pivots turned together), not from a catalogue reference, so references is honestly empty. One shared WebGL context renders every instance, with a Canvas 2D painter of the same geometry when WebGL is missing or lost; no image or font ships (the preview's photograph is painted procedurally, the exported example's is inline SVG). PRESETS in controls.mjs: Graphite prime (default), Brass field camera, Six-blade hexagon, Steel cine lens, Cover reveal.

06

Blocks built with it

Docs
Finish
Volume