Animierte Icons
glyphfox animiert Icons auf zwei Arten. Lebendige Zustände sind Presets wie draw, pop oder shake, die durch einen Auslöser starten. Morphs verwandeln ein Icon in ein anderes. Beides funktioniert in jedem Paket, und beides schaltet sich von selbst ab, wenn jemand reduzierte Bewegung eingestellt hat.
Einrichtung
Sie müssen nichts zusätzlich installieren: Animation ist Teil der gemeinsamen Props. Die folgenden Beispiele verwenden glyphfox/react; die Props heißen in Vue, Svelte und Angular genauso, ebenso in der Web Component, wo sie Attribute in Kebab-Case sind.
npm install glyphfox
Ein lebendiger Zustand besteht aus wenigen Zeilen CSS, die im SVG selbst mitgeliefert werden, und zwar in einem <style>-Element, das auf eine eigens für dieses Icon generierte Klasse beschränkt ist. Deshalb funktioniert dieselbe Animation in einer Komponente, im String aus toSvg, in einer Data-URI oder in einer exportierten Datei, ohne dass Sie ein Stylesheet importieren müssen.
Die sieben Presets
Jedes Preset ist auf Icons abgestimmt statt auf beliebige Elemente. draw zeichnet die Striche in der Reihenfolge nach, in der sie gezeichnet wurden, und blendet am Ende die Füllebene ein. shake pendelt vom oberen Ende aus, wie eine Glocke an einem Haken. Die übrigen bewegen das ganze Icon um seinen Mittelpunkt.
| Preset | Bewegung | Dauer | Wiederholt sich standardmäßig |
|---|---|---|---|
draw | zeichnet jeden Strich nach, einen nach dem anderen | 1100 ms | nein |
pop | wächst von 55 Prozent auf volle Größe, mit leichtem Überschwingen | 520 ms | nein |
wiggle | dreht sich nach links und rechts und kommt zur Ruhe | 720 ms | nein |
spin | volle Umdrehung mit konstanter Geschwindigkeit | 1000 ms | ja |
pulse | wird etwas kleiner und blasser und kehrt dann zurück | 1500 ms | ja |
bounce | springt hoch und kommt mit zwei kleineren Sprüngen zur Ruhe | 900 ms | nein |
shake | pendelt vom oberen Ende aus mit abnehmender Amplitude | 950 ms | nein |
import { LoaderCircleIcon, SparklesIcon, BellIcon, HeartIcon } from 'glyphfox/react'; export function Presets({ hasNews, liked }) { return ( <> <LoaderCircleIcon animate="spin" /> <SparklesIcon animate="draw" /> <BellIcon animate="shake" trigger="state" active={hasNews} /> <HeartIcon animate="pop" trigger="click" variant={liked ? 'solid' : 'outline'} /> </> ); }
duration und delay erwarten Millisekunden. loop akzeptiert true für endlose Wiederholung oder eine Anzahl von Durchläufen; spin und pulse wiederholen sich, sofern Sie nicht loop={false} übergeben. Läuft draw in einer Schleife, wechselt es jedes Mal die Richtung: Das Icon zeichnet sich erst und radiert sich dann wieder aus, statt zurückzuspringen.
Auslöser
Die Prop trigger legt fest, wann ein Preset abgespielt wird:
mount, der Standard, startet die Animation, sobald das Icon zum ersten Mal gerendert wird.hoverspielt die Animation ab, solange sich der Zeiger über dem Icon oder seinem direkten Elternelement befindet. Ein Icon in einem Button wird also animiert, wenn der Zeiger über dem Button liegt.clickspielt die Animation bei jedem Klick auf das Icon erneut ab.statespielt die Animation ab, solangeactiveden Wert true hat. Diesen Auslöser verwenden Sie für alles, worüber Ihre Anwendung entscheidet: eine neue Benachrichtigung, ein fehlgeschlagenes Speichern, ein abgeschlossener Upload.
import { BellIcon } from 'glyphfox/react'; export function NotificationsButton({ unread }) { return ( <button type="button" aria-label={`Notifications, ${unread} unread`}> <BellIcon animate="shake" trigger="state" active={unread > 0} loop={2} /> </button> ); }
In statischem Markup entsprechen die Auslöser einfachen CSS-Hooks. Eine state-Animation läuft, solange das Element svg die Klasse gf-on trägt. Serverseitig gerendertes HTML und Strings aus toSvg lassen sich daher mit einer einzigen Zeile Skript steuern: svg.classList.toggle('gf-on', unread > 0). hover kommt ganz ohne Skript aus.
Ein Icon in ein anderes morphen
Die Morph-Engine zerlegt beide Icons in Stränge, ordnet sie über eine optimale Zuweisung einander zu, tastet sie auf dieselbe Anzahl von Punkten neu ab und interpoliert. Stränge ohne Gegenstück schrumpfen und werden ausgeblendet. Zwei beliebige Icons lassen sich morphen; zehn Paare sind von Hand abgestimmt, damit sie gestaltet statt berechnet wirken:
| Von | Nach |
|---|---|
| menu | x |
| play | pause |
| plus | check |
| sun | moon |
| eye | eye-off |
| bell | bell-ring |
| heart | heart im Solid-Stil |
| copy | check |
| lock | unlock |
| wifi | wifi-off |
Es gibt drei Wege, einen Morph anzufordern. MorphIcon morpht, sobald sich sein name ändert. morphTo an einem beliebigen Icon morpht zu einem Ziel, sobald sein Auslöser eintritt, und wieder zurück, wenn er endet. Hooks und Stores (useMorph in React und Vue, morphStore in Svelte) liefern Ihnen die Frames für Ihr eigenes Rendering.
import { useState } from 'react'; import { MorphIcon, MenuIcon, registerIcons } from 'glyphfox/react'; import { iconCopy, iconCheck, iconX } from 'glyphfox'; // Morph targets are looked up by name: register their data once. registerIcons([iconCopy, iconCheck, iconX]); export function CopyButton({ text }) { const [copied, setCopied] = useState(false); const copy = async () => { await navigator.clipboard.writeText(text); setCopied(true); setTimeout(() => setCopied(false), 1500); }; return ( <button type="button" onClick={copy}> <MorphIcon name={copied ? 'check' : 'copy'} duration={300} /> {copied ? 'Copied' : 'Copy'} </button> ); } export function MenuButton({ open, onToggle }) { return ( <button type="button" aria-expanded={open} aria-label="Menu" onClick={onToggle}> <MenuIcon morphTo="x" trigger="state" active={open} /> </button> ); }
Morphs dauern 450 ms, sofern Sie nicht duration übergeben. Mit trigger="state" folgt das Icon active in beide Richtungen. Das passt zu Umschaltern wie einem Menü, einem Passwortfeld (eye zu eye-off) oder einem Play-Button.
Dieselben Morphs in Vue, Svelte und der Web Component
<script setup> import { ref } from 'vue'; import { MorphIcon, registerIcons } from 'glyphfox/vue'; import { iconPlay, iconPause } from 'glyphfox'; registerIcons([iconPlay, iconPause]); const playing = ref(false); </script> <template> <button type="button" :aria-label="playing ? 'Pause' : 'Play'" @click="playing = !playing"> <MorphIcon :name="playing ? 'pause' : 'play'" /> </button> </template>
<script> import { Icon, morphStore, registerIcons } from 'glyphfox/svelte'; import { iconSun, iconMoon } from 'glyphfox'; registerIcons([iconSun, iconMoon]); const icon = morphStore('sun', { duration: 400 }); let dark = $state(false); </script> <button type="button" aria-label="Dark theme" aria-pressed={dark} onclick={() => { dark = !dark; icon.set(dark ? 'moon' : 'sun'); }}> {#if $icon}<Icon icon={$icon.icon} variant={$icon.variant} />{/if} </button>
<script src="https://cdn.jsdelivr.net/npm/glyphfox@1"></script> <button type="button" id="menu" aria-expanded="false" aria-label="Menu"> <glyph-fox name="menu" morph duration="300"></glyph-fox> </button> <script> const button = document.getElementById('menu'); const icon = button.querySelector('glyph-fox'); button.addEventListener('click', () => { const open = button.getAttribute('aria-expanded') !== 'true'; button.setAttribute('aria-expanded', String(open)); icon.setAttribute('name', open ? 'x' : 'menu'); }); </script>
Mit dem Attribut morph animiert <glyph-fox> jede Änderung von name. Der CDN-Build enthält bereits alle Icons, dort ist also keine Registrierung nötig.
Morphs ohne JavaScript
toSvg mit morphTo liefert ein SVG, dessen Pfade die Ausgangsform enthalten und über eine CSS-Transition der Eigenschaft d in die Zielform übergehen. Browser, die d in CSS nicht unterstützen, blenden stattdessen exakt zum Ziel über. Das Ergebnis ist ein einfacher String, bereit für Server-Templates, statische Websites oder ein CMS.
import { toSvg } from 'glyphfox'; // Morphs while the pointer is over the icon or its direct parent. const menu = toSvg('menu', { morphTo: 'x', trigger: 'hover', duration: 300 }); // Morphs while the svg has the class gf-on: toggle it from any script. const play = toSvg('play', { morphTo: 'pause', trigger: 'state' });
Die CLI schreibt dieselbe Datei: npx glyphfox morph menu x --trigger hover --out menu.svg. Nur toSvg löst ein als Name angegebenes Ziel auf; renderToString aus glyphfox/core erwartet für morphTo Icon-Daten.
Animationsklassen für Tailwind CSS
Icons, die mit dem Tailwind-CSS-Plugin gerendert werden, sind Masken und kein SVG. Sie eignen sich daher für Animationen des ganzen Elements: gf-spin, gf-pulse, gf-pop, gf-wiggle, gf-bounce und gf-shake, mit demselben Timing wie die Presets. Sie funktionieren mit Varianten wie hover:gf-wiggle. Draw und Morphs erfordern Inline-SVG.
Reduzierte Bewegung
Jedes Preset bringt eine Regel für prefers-reduced-motion: reduce mit, die seine Animation abschaltet, der reine CSS-Morph wechselt ohne Übergang, und die JavaScript-Morphs der Komponenten springen direkt zur Endform. Sie müssen die Einstellung nicht selbst prüfen. Wer sie aktiviert hat, sieht trotzdem den Endzustand: das Herz im Solid-Stil, das x eines geöffneten Menüs, das Häkchen nach dem Kopieren.
Reduzierte Bewegung ist nicht der einzige Grund, Animationen kurz zu halten. Bewegung zieht den Blick auf sich. Reservieren Sie Endlosanimationen daher für laufende Vorgänge, etwa ein loader-circle, das sich dreht, während eine Anfrage läuft, und beenden Sie sie, sobald die Arbeit erledigt ist. Mehr zu Bewegung und Bedeutung finden Sie im Leitfaden zur Barrierefreiheit.
Animierte Dateien exportieren
Der Konfigurator im Icon-Browser exportiert jedes Icon mit einem Preset als animiertes SVG, als GIF oder als WebM-Video, mit exakt derselben Bewegung: Die Frames von GIF und WebM werden aus dem Timing des Presets abgetastet. Verwenden Sie das animierte SVG überall, wo SVG akzeptiert wird, und GIF oder WebM für Folien, Dokumentationstools und Chat-Apps, die SVG entfernen.
Performance
- Alle Presets außer
drawanimieren nurtransformundopacity, was Browser ohne Layout-Berechnung ausführen.drawanimiertstroke-dashoffset, wodurch das Icon in jedem Frame neu gezeichnet wird: unproblematisch bei wenigen Icons, in langen Listen besser zu vermeiden. - Morphs in Komponenten laufen über
requestAnimationFrameund rendern das Icon für die Dauer des Morphs in jedem Frame neu. Eine Handvoll gleichzeitig ist günstig, Hunderte, die zusammen starten, sind es nicht. - Die generierten Klassennamen sind Hashes der Einstellungen. Identische Icons erzeugen daher identisches Markup, und die Ausgaben von Server und Client stimmen überein.
Animations-Props
Das sind die Props, die Bewegung steuern. Jede andere gemeinsame Prop, etwa variant oder weight, lässt sich mit ihnen kombinieren.
| Prop | Typ | Standard | Beschreibung |
|---|---|---|---|
animate | draw, pop, wiggle, spin, pulse, bounce, shake | keiner | Voreinstellung für den lebendigen Zustand. |
trigger | mount, hover, click, state | mount | Wann die Animation oder der Morph abläuft. |
active | boolean | false | Der Zustand für den Auslöser state. |
duration | number (ms) | je nach Voreinstellung | Dauer der Animation oder des Morphs. |
delay | number (ms) | 0 | Verzögerung der Animation oder des Morphs. |
loop | boolean, number | false | Endlos oder eine bestimmte Anzahl von Malen wiederholen (spin und pulse laufen standardmäßig in Schleife). |
morphTo | name, data | keiner | Ziel des Morphs; der Auslöser bestimmt, wann es erscheint. |
Stolperfallen
- Morph-Ziele, die nichts rendern
- Morph-Ziele sowie die Namen für
MorphIconundmorphStorewerden über den Namen nachgeschlagen. Ein Name, dessen Daten nie importiert oder registriert wurden, wird bei Bedarf nachgeladen, und in einem Vite-Produktionsbuild findet dieses Nachladen nicht statt, weil Vite den variablen dynamischen Import innerhalb vonnode_modulesunverändert lässt. Das Ziel bleibt leer. Registrieren Sie jeden Namen, zu dem Sie morphen, mitregisterIcons. Eine Komponente wieCheckIconnur wegen ihres Seiteneffekts zu importieren, hilft nicht, denn Tree-Shaking entfernt einen Import, der nie verwendet wird. - Hover und Klick außerhalb des Icons
- Ein Hover-Preset auf Inline-SVG reagiert per CSS auch auf das direkte Elternelement. Für alles, was über Events gesteuert wird, gilt das nicht:
trigger="click"und der Hover-Auslöser eines Morphs hören auf Events am Icon selbst, undglyph-foxreagiert nur auf den Zeiger direkt über dem Element. Ein Klick auf das Padding eines Buttons erreicht sie nicht. Halten Sie den Zustand im Button und geben Sie ihn mittrigger="state"undactivenach unten weiter. - Mount-Animationen, die erneut ablaufen
- Eine
mount-Animation läuft jedes Mal, wenn das Icon gemountet wird: wenn eine Liste mit neuen Keys neu gerendert wird, bei einem Routenwechsel und beiglyph-foximmer dann, wenn sich eines seiner Attribute ändert. Soll ein Icon nur einmal animiert werden, verwenden Sietrigger="state"und entscheiden in Ihrem Code, wann die Animation läuft. - Zustandsanimationen, die nie aufhören
- Ein Preset mit
loopläuft weiter, solangeactivetrue bleibt. Setzen Sieactivewieder auf false, sobald das Ereignis behandelt ist, oder geben Sieloopeine Zahl. - Statisches Markup und Klicks
- Im HTML aus
toSvgwartet nichts auf Klicks. Verwenden Sietrigger: 'state'und schalten Sie die Klassegf-onin Ihrem eigenen Handler um.
Häufige Fragen
Brauchen animierte Icons JavaScript?
Lebendige Zustände nicht: Das CSS steckt im SVG, daher wird ein serverseitig gerendertes oder statisches Icon beim Mounten und beim Hovern ganz ohne Skript animiert. Auch der reine CSS-Morph aus toSvg läuft ohne Framework. Komponenten-Morphs wie MorphIcon berechnen die Frames mit JavaScript.
Kann ein Icon gleichzeitig morphen und ein Preset abspielen?
Ja. animate und morphTo sind voneinander unabhängige Props. So kann ein Herz beim Klick mit pop aufspringen, während es in den Solid-Stil morpht: <HeartIcon animate="pop" morphTo="heart/solid" trigger="click" />.