Iris ist eine SVG-Lamellenblende mit echter Mechanik: starre Lamellen, die sich um
feste Drehpunkte unter dem Ring schwenken. Die Öffnung ist durchsichtig - dahinter
liegt die Seite oder ein Bild. Konfiguration deklarativ über data-iris-*
oder per JavaScript, Auto-Init beim DOM-ready inklusive.
Es gibt kein separates Stylesheet. Beim ersten Einsatz setzt iris.js einmal
ein kleines <style id="iris-tokens"> an den Anfang des
<head>. Darin stehen ausschließlich Theme-Tokens
(--iris-*) am Host .iris. Weil das Element ganz vorn steht, überschreibt
jedes Seiten-CSS diese Tokens schon mit gleicher Spezifität.
Das gesamte Painting liegt gekapselt im Shadow Root jeder Instanz. Alle
Instanzen teilen sich ein CSSStyleSheet-Objekt
(adoptedStyleSheets); wo das nicht geht, greift automatisch ein
<style>-Fallback. Seiten-CSS auf nackte Tags erreicht die Blende nicht.
<script src="iris.js" defer></script>
<div class="iris"
data-iris-shape="klassisch"
data-iris-blades="8"
data-iris-open="60"></div>
Die Größe steuerst du über --iris-size (Standard 100% der
Breite) oder einfach über die Breite des Elements; die Blende ist immer quadratisch.
<div class="iris" style="--iris-size: 240px" data-iris-animate="breathe"></div>
Jede Lamelle ist ein starres Blech - ein Kreis, aus dem der sogenannte Arbeitskreis ausgeschnitten ist - und schwenkt um einen festen Drehpunkt unter dem Ring. Aus der gewünschten Öffnung folgt der Schwenkwinkel über den Kosinussatz. Die Öffnung dreht sich dabei mit, wie bei einer echten Blende.
Jede Lamelle wird von ihren (bis zu) drei Nachfolgerinnen verdeckt. Diese Stapelung ist ringsum gleich - es gibt keine erste oder letzte Lamelle. Alle Formen sind für 3 bis 24 Lamellen durchgerechnet: in jeder Stellung ohne Lücke und mit genau einer obersten Lamelle an jedem Punkt.
Die Formen unterscheiden sich durch die Krümmung der Arbeitskante. Sie bestimmt die Öffnungsform und zugleich die Fugenlinien, denn die sichtbare Grenze zwischen zwei Lamellen ist die Fortsetzung der Arbeitskante der oberen.
| Wert | Charakter | Kleinste Öffnung |
|---|---|---|
wirbel | stark geschwungene Fugen, fast runde Öffnung | 12 % |
rund | weiche, runde Öffnung | 5 % |
klassisch (default) | leicht gerundetes Vieleck | 0 % (schließt ganz) |
flach | sanfte Kanten | 0 % |
vieleck | nahezu gerade Fugen, scharfes Vieleck | 0 % |
open läuft trotzdem immer von 0 bis 100; 0 bedeutet die kleinste Öffnung der Form.
| Attribut | Wert | Beschreibung |
|---|---|---|
data-iris-shape | wirbel | rund | klassisch | flach | vieleck | Lamellenform. Default klassisch. |
data-iris-blades | 3 – 24 | Anzahl der Lamellen. Default 8. |
data-iris-open | 0 – 100 | Stellung. 0 = kleinste Öffnung der Form, 100 = ganz offen. Default 60. |
data-iris-mirror | true | false | Drehsinn umkehren (exaktes Spiegelbild). |
data-iris-theme | messing | hell | icon | Vorgefertigtes Theme. Ohne Attribut: Stahl. |
data-iris-edge | Zahl, z.B. 3 | Stärke der Kantenlinien (viewBox-Einheiten, Blende = 1000). Ohne Angabe gilt --iris-edge-width aus dem CSS bzw. dem Theme. |
data-iris-image | URL | Bild hinter der Öffnung, siehe unten. |
data-iris-ring | false | Frontring ausblenden; der Ausschnitt wird dann enger. |
data-iris-mechanism | true | Drehpunkte und Umriss einer Lamelle einblenden. |
data-iris-interactive | true | Bedienbar per Ziehen, Mausrad und Tastatur. |
data-iris-animate | breathe | Öffnet und schließt selbstständig. |
data-iris-duration | Zahl (ms) | Dauer eines Atemzugs. Default 4000. |
data-iris-min / data-iris-max | 0 – 100 | Unter- und Obergrenze der Stellung beim Atmen. |
data-iris-label | Text | aria-label. Default Blende. |
Ein Bild wird immer passend eingesetzt: die kürzere Bildseite füllt die
Öffnung, die längere wird mittig beschnitten - wie object-fit: cover. Ein
Querformat verliert also links und rechts, ein Hochformat oben und unten.
<div class="iris" data-iris-image="landschaft.jpg"></div>
// oder zur Laufzeit
el._iris.setImage('landschaft.jpg');
el._iris.setImage(null); // entfernen
Ohne Bild ist die Öffnung durchsichtig. Willst du dort eine Farbe, setze
--iris-backdrop.
const iris = new Iris(element, {
shape: 'rund',
blades: 11,
open: 40,
interactive: true,
onChange: (d) => console.log(d.open, d.diameter)
});
iris.setOpen(75); // sofort
await iris.animateTo(0, 800); // weich, liefert ein Promise
iris.play(); // atmen
iris.stop();
iris.update({ shape: 'vieleck', blades: 6, theme: 'messing' });
const svg = iris.toSVG(); // eigenständiges SVG als String
iris.destroy();
// Alle .iris-Elemente nachträglich initialisieren (idempotent):
Iris.init();
// Optional: jQuery-Bridge
$('.iris').iris({ blades: 12 });
Die Optionen heißen wie die Data-Attribute ohne Präfix (shape,
blades, open, mirror, theme,
edge, image, ring, mechanism,
interactive, animate, duration, min,
max, label) plus onChange. Übergebene Optionen haben
Vorrang vor den Attributen. Die Instanz hängt als element._iris am Element.
Ein zweites new Iris(element, options) liefert dieselbe Instanz und wendet
die Optionen per update() an.
| Methode | Beschreibung |
|---|---|
setOpen(v) | Stellung 0 – 100 sofort setzen. Hält ein laufendes Atmen an. |
animateTo(v, ms?) | Weich zur Stellung, Default 600 ms. Liefert ein Promise (true = angekommen, false = unterbrochen). |
play() / stop() / toggle() | Atmen starten, anhalten, umschalten. Der Atemzug schließt nahtlos an die aktuelle Stellung an. |
update(options) | Beliebige Optionen ändern und neu zeichnen. Enthält options ein open, werden Atmen und ein laufendes animateTo() beendet. |
setShape(), setBlades(), setMirror(), setTheme(), setImage(), setMechanism() | Kurzformen für update(). |
toSVG() | Eigenständiges SVG mit aufgelösten Farben. Bilder werden per URL referenziert, nur data:-URLs sind eingebettet. |
destroy() | Stoppt Animationen, löst Listener, leert den Shadow Root, entfernt _iris. Attribute, Klasse und --iris-edge-width am Element erhalten wieder ihren Zustand von vor der Initialisierung. |
Iris.init(selector?) | Statisch. Initialisiert alle passenden Elemente, Default .iris. Idempotent. |
| Eigenschaft | Beschreibung |
|---|---|
open | Aktuelle Stellung 0 – 100. |
diameter | Tatsächlicher Durchmesser der Öffnung in % der größten Öffnung (berücksichtigt die kleinste Öffnung der Form). |
minDiameter | Kleinste Öffnung der aktuellen Form in %. |
isPlaying | Truthy, solange geatmet wird. |
options | Kopie der aktuellen Optionen. |
Iris.shapes, Iris.themes, Iris.version | Statisch. Listen für eigene Bedienoberflächen. |
Bei jeder Änderung der Stellung feuert am Element iris:change (bubbelt).
event.detail enthält open, diameter, blades,
shape und source - user, api,
animation oder breathe.
el.addEventListener('iris:change', e => {
if (e.detail.source === 'user') label.textContent = e.detail.diameter + ' %';
});
Mit data-iris-interactive="true" wird die Blende zu einem Regler (role="slider"):
Drei vorgefertigte Themes via data-iris-theme, dazu der Standard ohne Attribut
(Stahl mit Messing-Akzent):
.iris.mein-theme {
--iris-blade: #2d3b4a; /* Grundfarbe der Lamellen */
--iris-edge: #9fc2e0; /* Kantenlinien */
--iris-edge-width: 3; /* Linienstärke, Blende = 1000 Einheiten */
--iris-shade: 0.7; /* Stärke der Lichtschattierung, 0 = flach */
--iris-ring-1: #8aa3bb; /* Ringverlauf, hell ... */
--iris-ring-2: #55697d;
--iris-ring-3: #2a3642;
--iris-ring-4: #4b5c6d; /* ... dunkel */
--iris-ring-line: #10161c; /* Ringkanten */
--iris-ring-line-width: 3; /* Linie zwischen Ring und Lamellen */
--iris-ring-outer: #10161c; /* äußere Ringkante, Default = --iris-ring-line */
--iris-tick: #a9c0d6; /* Rändelung */
--iris-backdrop: transparent; /* Füllung der Öffnung ohne Bild */
--iris-mech: #e8b964; /* Mechanik-Ansicht */
--iris-focus: #e8b964; /* Fokusring */
}
Da das Painting gekapselt ist, ist das der einzige Weg zum Theming. CSS auf interne
Klassen im Shadow Root wirkt nicht. toSVG() übernimmt die gerade gültigen
Token-Werte in das exportierte SVG.
prefers-reduced-motion: reduce startet data-iris-animate="breathe" nicht von selbst, und animateTo() springt ohne Übergang. Ein expliziter Aufruf von play() funktioniert weiter.IntersectionObserver). Events und Stellung laufen weiter.toSVG() vergibt bei jedem Export eine eigene Zufalls-ID, sodass sich auch mehrere exportierte SVGs inline in einer Seite nicht in die Quere kommen.el.shadowRoot. Von außen erreichbar sind die Ebenen über ::part(): stack (Container aller Ebenen), image (Bild hinter der Öffnung), blades (Lamellen, weiterhin auch als svg), ring (Frontring und Fokusring) und mechanism (Mechanik-Ansicht)..iris (Breite, Rand, Schatten) wirkt weiterhin - das ist gewollt.