Apples legendäre Activity-Ringe mit React, SVG und Framer Motion neu erstellen
Erfahren Sie, wie Sie Apples legendäre Activity-Ringe von Grund auf neu erstellen. Dieses Tutorial führt Sie durch die Verwendung von React, SVG und Framer Motion, um atemberaubende, datengesteuerte UI-Animationen zu erstellen.
Auf dieser Seite
Die Aktivitätsringe der Apple Watch sind ein Meisterwerk der Datenvisualisierung – einfach, elegant und sofort verständlich. Sie motivieren die Benutzer, indem sie Fitnessziele in ein befriedigendes Spiel des „Schließens Ihrer Ringe" verwandeln. Für Entwickler stellen sie eine faszinierende UI-Herausforderung dar. Wie erstellt man ein so flüssiges, optisch ansprechendes Element für das Web?
In diesem Tutorial werden wir Apples ikonische Aktivitätsringe von Grund auf neu erstellen. Wir tauchen tief in die Leistungsfähigkeit von SVG ein, steuern sie mit React-State und erwecken sie mit den butterweichen Animationen von Framer Motion zum Leben. Es geht hier nicht nur darum, ein Design zu klonen; es geht darum, die grundlegenden Prinzipien der SVG-Manipulation und moderner Web-Animation zu verstehen.
Was wir bauen werden: Einen Satz von drei verschachtelten, animierten Aktivitätsringen, die auf Datenänderungen reagieren, genau wie das Original.
Voraussetzungen:
- Grundlegendes Verständnis von React und React Hooks.
- Node.js und npm/yarn auf Ihrem Rechner installiert.
- Eine Leidenschaft für die Entwicklung schöner Benutzeroberflächen. ✨
Warum dies für Entwickler relevant ist: Dieses Projekt ist eine perfekte Möglichkeit, Ihre Frontend-Fähigkeiten auf ein neues Level zu heben. Sie werden über grundlegendes CSS hinausgehen und leistungsstarke SVG-Attribute beherrschen lernen, sowie verstehen, wie eine professionelle Animationsbibliothek wie Framer Motion komplexe Animationen vereinfachen kann, um Ihre UIs dynamischer und reaktionsschneller zu gestalten.
Das Problem verstehen
Im Kern ist ein Aktivitätsring eine kreisförmige Fortschrittsanzeige. Die Hauptherausforderung besteht darin, einen Teilkreis zu zeichnen, der einem Prozentwert entspricht (z.B. 75 % abgeschlossen).
Architektur der Aktivitätsring-Komponente
Das folgende Diagramm zeigt, wie unsere Ring-Komponenten strukturiert sind:
Die Magie liegt in zwei SVG-Eigenschaften für Striche:
stroke-dasharray: Diese Eigenschaft wandelt eine durchgehende Linie in eine gestrichelte Linie umstroke-dashoffset: Diese Eigenschaft definiert, wo entlang des Pfads das Stricheln beginnt
Indem wir stroke-dasharray auf den genauen Umfang unseres Kreises setzen, erstellen wir einen „Strich", der die volle Länge des Kreises hat, mit einem gleich langen „Abstand". Wir können dann stroke-dashoffset animieren, um den Kreis freizulegen, und so die Illusion erzeugen, dass er gezeichnet wird. Framer Motion vereinfacht diesen Prozess immens und ermöglicht es uns, eine pathLength-Eigenschaft von 0 auf 1 zu animieren.
Unser Ansatz ist es, zwei SVG-<circle>-Elemente für jeden Ring zu schichten:
- Ein Hintergrundring: Ein halbtransparenter, statischer Kreis, der als Spur dient
- Ein Vordergrundring: Der farbige, animierte Kreis, der den tatsächlichen Fortschritt darstellt
Voraussetzungen
Bevor wir mit dem Programmieren beginnen, richten wir unsere Entwicklungsumgebung ein.
- Node.js: v16 oder höher
- React: v18 oder höher
- Framer Motion: Die Animationsbibliothek, die wir verwenden werden.
Lassen Sie uns ein neues React-Projekt erstellen und die erforderlichen Abhängigkeiten installieren.
# Create a new React app with Vite (recommended for speed)
npm create vite@latest apple-rings-tutorial -- --template react
# Navigate into the project directory
cd apple-rings-tutorial
# Install Framer Motion
npm install framer-motion
# Start the development server
npm run dev
Nach dem Ausführen von npm run dev sollten Sie die Standard-Vite-React-Seite unter http://localhost:5173 sehen.
Einen einzelnen animierten Fortschrittsring mit SVG erstellen
Lassen Sie uns damit beginnen, eine einzelne, wiederverwendbare Ring-Komponente zu erstellen. Diese Komponente wird die Grundlage für alle drei Aktivitätsringe sein.
Was wir tun werden
Wir erstellen eine neue Komponente namens ActivityRing.js. Diese Komponente nimmt eine progress-Prop (eine Zahl zwischen 0 und 1) entgegen und rendert einen SVG-Kreis, der diesen Fortschritt visuell darstellt.
Implementierung
Erstellen Sie eine neue Datei src/ActivityRing.jsx.
// src/ActivityRing.jsx
import React from 'react';
import { motion } from 'framer-motion';
const ActivityRing = ({ progress = 0, radius = 50, strokeWidth = 10 }) => {
const innerRadius = radius - strokeWidth / 2;
const circumference = 2 * Math.PI * innerRadius;
return (
<svg width={radius * 2} height={radius * 2} className="transform -rotate-90">
{/* Background Ring */}
<circle
cx={radius}
cy={radius}
r={innerRadius}
fill="transparent"
stroke="#e6e6e6"
strokeWidth={strokeWidth}
/>
{/* Foreground Ring */}
<motion.circle
cx={radius}
cy={radius}
r={innerRadius}
fill="transparent"
stroke="#f87171" // A default color, we'll make this dynamic later
strokeWidth={strokeWidth}
strokeDasharray={circumference}
strokeDashoffset={circumference * (1 - progress)}
strokeLinecap="round"
/>
</svg>
);
};
export default ActivityRing;
Jetzt verwenden wir diese Komponente in unserer App.jsx.
// src/App.jsx
import React from 'react';
import ActivityRing from './ActivityRing';
import './App.css';
function App() {
return (
<div className="flex items-center justify-center min-h-screen bg-black">
<ActivityRing progress={0.75} radius={80} strokeWidth={20} />
</div>
);
}
export default App;
Sie benötigen etwas grundlegendes CSS, um den Inhalt zu zentrieren. In src/index.css oder src/App.css:
/* Ensure you have Tailwind CSS or basic CSS for centering */
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.flex { display: flex; }
.items-center { align-items: center; }
.justify-center { justify-content: center; }
.min-h-screen { min-height: 100vh; }
.bg-black { background-color: #000; }
.transform { transform-origin: center; }
.-rotate-90 { transform: rotate(-90deg); }
Wie es funktioniert
- SVG-Container: Wir erstellen ein
<svg>-Element. Die-rotate-90-Klasse ist entscheidend, da SVG-Kreise standardmäßig bei der 3-Uhr-Position mit dem Zeichnen beginnen. Wir drehen das gesamte SVG, damit es oben (12 Uhr) beginnt. - Radius und Umfang: Wir berechnen den
circumferencedes Kreises. Dies ist die Gesamtlänge des Strichs. - Hintergrundkreis: Dies ist ein einfacher, vollständiger Kreis, der als Spur dient.
- Vordergrundkreis: Hier geschieht die Magie.
strokeDasharray={circumference}: Wir erstellen ein Strichmuster, bei dem der Strich die Länge des gesamten Kreises hat.strokeDashoffset={circumference * (1 - progress)}: Wir versetzen den Anfang des Strichs. Wennprogress0 ist, ist der Versatz der vollecircumference, sodass der sichtbare Teil (der Strich) vollständig verborgen ist. Wennprogress1 ist, ist der Versatz 0, wodurch der gesamte Kreis sichtbar wird.strokeLinecap="round": Dies verleiht den Enden der Fortschrittsanzeige ein abgerundetes, ansprechenderes Aussehen, genau wie bei Apples Design.
Den Ring-Fortschritt mit Framer Motion animieren
Ein statischer Ring ist cool, aber ein animierter ist viel besser. Lassen Sie uns Framer Motion hinzufügen, um den Fortschritt zu animieren.
Was wir tun werden
Wir ersetzen den manuell berechneten strokeDashoffset durch die deklarativen Animationseigenschaften von Framer Motion. Wir verwenden die initial-, animate- und transition-Props auf der motion.circle-Komponente. Framer Motion hat eine spezielle pathLength-Variante für SVGs, die dies unglaublich einfach macht.
Implementierung
Lassen Sie uns src/ActivityRing.jsx aktualisieren.
// src/ActivityRing.jsx
import React from 'react';
import { motion } from 'framer-motion';
const ActivityRing = ({ progress = 0, radius = 50, strokeWidth = 10, color = '#f87171' }) => {
const innerRadius = radius - strokeWidth / 2;
const circumference = 2 * Math.PI * innerRadius;
const variants = {
initial: {
strokeDashoffset: circumference,
},
animate: {
strokeDashoffset: circumference * (1 - progress),
transition: {
duration: 1.5,
ease: "easeInOut",
},
},
};
return (
<svg width={radius * 2} height={radius * 2} className="transform -rotate-90">
{/* Background Ring */}
<circle
cx={radius}
cy={radius}
r={innerRadius}
fill="transparent"
stroke={color}
strokeWidth={strokeWidth}
className="opacity-20"
/>
{/* Foreground Ring */}
<motion.circle
cx={radius}
cy={radius}
r={innerRadius}
fill="transparent"
stroke={color}
strokeWidth={strokeWidth}
strokeDasharray={circumference}
strokeLinecap="round"
initial="initial"
animate="animate"
variants={variants}
/>
</svg>
);
};
export default ActivityRing;
Ich habe auch eine color-Prop und eine subtile Transparenz zum Hintergrundring hinzugefügt, um Apples Design zu entsprechen. Fügen Sie unbedingt die .opacity-20-Klasse zu Ihrem CSS hinzu, wenn Sie Tailwind nicht verwenden.
Wie es funktioniert
motion.circle: Wir verwenden Framer Motionsmotion-Komponente, der jedes HTML- oder SVG-Element vorangestellt werden kann, um es animierbar zu machen.variants: Wir definieren zwei Zustände für unsere Animation:initialundanimate.initial: DerstrokeDashoffsetwird auf den vollencircumferencegesetzt, sodass der Ring am Anfang leer ist.animate: DerstrokeDashoffsetwird auf den durch unsereprogress-Prop bestimmten Wert animiert.
transition: Wir definieren das Verhalten der Animation, mit einerdurationvon 1,5 Sekunden und einereaseInOut-Beschleunigungsfunktion für eine sanfte Beschleunigung und Verzögerung.
Wenn die Komponente jetzt gemountet wird, sehen Sie, wie der Ring wunderschön von 0 auf 75 % animiert wird!
Drei verschachtelte Aktivitätsringe zusammenstellen
Mit unserer wiederverwendbaren <ActivityRing>-Komponente ist das Erstellen des vollständigen Satzes jetzt nur noch eine Frage der Komposition und Gestaltung.
Was wir tun werden
Wir erstellen eine ActivityRingsDashboard.jsx-Komponente, die drei <ActivityRing>-Komponenten ineinander verschachtelt rendert. Wir verwenden absolute Positionierung, um sie zu stapeln.
Implementierung
Zuerst definieren wir unsere Ringdaten.
// src/ringsData.js
export const RINGS_DATA = [
{
id: 'move',
progress: 0.85,
radius: 100,
color: '#FF005C',
},
{
id: 'exercise',
progress: 0.60,
radius: 75,
color: '#92FE9D',
},
{
id: 'stand',
progress: 0.95,
radius: 50,
color: '#00C6FF',
},
];
Jetzt erstellen wir die Dashboard-Komponente src/ActivityRingsDashboard.jsx.
// src/ActivityRingsDashboard.jsx
import React from 'react';
import ActivityRing from './ActivityRing';
import { RINGS_DATA } from './ringsData';
const ActivityRingsDashboard = () => {
return (
<div className="relative flex items-center justify-center">
{RINGS_DATA.map((ring, index) => (
<div key={ring.id} className="absolute">
<ActivityRing
progress={ring.progress}
radius={ring.radius}
strokeWidth={20}
color={ring.color}
/>
</div>
))}
</div>
);
};
export default ActivityRingsDashboard;
Schließlich aktualisieren wir App.jsx, um das neue Dashboard zu verwenden.
// src/App.jsx
import React, { useState, useEffect } from 'react';
import ActivityRingsDashboard from './ActivityRingsDashboard';
import './App.css';
function App() {
// Example of how you might update progress dynamically
const [data, setData] = useState(RINGS_DATA);
return (
<div className="flex items-center justify-center min-h-screen bg-gray-900">
<ActivityRingsDashboard />
</div>
);
}
export default App;
Sie müssen die .relative- und .absolute-Klassen zu Ihrem CSS hinzufügen, damit das Stapeln funktioniert.
Wie es funktioniert
Wir verwenden ein übergeordnetes div mit relative-Positionierung und platzieren jeden Ring in einem div mit absolute-Positionierung. Dadurch werden sie übereinander gestapelt, perfekt zentriert. Da jeder <ActivityRing> einen anderen radius hat, erscheinen sie verschachtelt. Wir mappen einfach über unser RINGS_DATA-Array, um jeden Ring mit seinen spezifischen Eigenschaften zu rendern.
Alles zusammenfügen
Hier ist der finale Code für unsere Hauptkomponenten.
src/ActivityRing.jsx
import React from 'react';
import { motion } from 'framer-motion';
const ActivityRing = ({ progress = 0, radius = 50, strokeWidth = 10, color = '#f87171' }) => {
const innerRadius = radius - strokeWidth / 2;
const circumference = 2 * Math.PI * innerRadius;
const variants = {
initial: { strokeDashoffset: circumference },
animate: {
strokeDashoffset: circumference * (1 - progress),
transition: {
duration: 1.5,
delay: 0.2,
ease: 'easeOut',
},
},
};
return (
<svg width={radius * 2} height={radius * 2} className="transform -rotate-90">
<circle
cx={radius}
cy={radius}
r={innerRadius}
fill="transparent"
stroke={color}
strokeWidth={strokeWidth}
className="opacity-30"
/>
<motion.circle
cx={radius}
cy={radius}
r={innerRadius}
fill="transparent"
stroke={color}
strokeWidth={strokeWidth}
strokeDasharray={circumference}
strokeLinecap="round"
initial="initial"
animate="animate"
variants={variants}
/>
</svg>
);
};
export default ActivityRing;```
**`src/ActivityRingsDashboard.jsx`**
```jsx
import React from 'react';
import ActivityRing from './ActivityRing';
import { RINGS_DATA } from './ringsData';
const ActivityRingsDashboard = () => {
return (
<div className="relative w-64 h-64 flex items-center justify-center">
{RINGS_DATA.map((ring, index) => (
<div key={ring.id} className="absolute">
<ActivityRing
progress={ring.progress}
radius={ring.radius}
strokeWidth={20}
color={ring.color}
/>
</div>
))}
</div>
);
};
export default ActivityRingsDashboard;
Leistungsaspekte
- Hardwarebeschleunigung: Framer Motion ist hochperformant und nutzt wo möglich Hardwarebeschleunigung für Animationen, daher sollte die Leistung ausgezeichnet sein.
- Bundle-Größe: Während
framer-motionzur Bundle-Größe beiträgt, rechtfertigen seine Leistungsfähigkeit und Benutzerfreundlichkeit oft die Kosten. Für einfachere Animationen könnten Sie CSS-Übergänge verwenden. - Neu-Renderings: Unsere Komponente wird neu gerendert, wenn sich die
progress-Prop ändert. Dies ist erwartbar und effizient. Reacts Diffing-Algorithmus aktualisiert nur die erforderlichen SVG-Attribute.
Best Practices für die Sicherheit
Da dies eine rein präsentative Komponente ist, sind die primären Sicherheitsaspekte minimal. Wenn jedoch die color- oder andere Props aus Benutzereingaben abgeleitet würden, wäre es unerlässlich, diese zu bereinigen, um XSS-Angriffe zu verhindern (z.B. wenn ein Benutzer bösartigen Code in ein SVG-Attribut einfügt). Validieren Sie immer Daten aus externen Quellen.
Fazit
Wir haben erfolgreich eines der ikonischsten UI-Elemente von Apple unter Verwendung der Leistungsfähigkeit von React, der Flexibilität von SVG und der Eleganz von Framer Motion neu erstellt. Wir haben gelernt, wie man SVG-Stricheigenschaften manipuliert, um dynamische kreisförmige Fortschrittsanzeigen zu erstellen, und wie man einfache Komponenten zu einer komplexeren und schöneren Datenvisualisierung zusammensetzt.
Auswirkungen auf die Gesundheit: Visuelles Fortschrittsfeedback wie Aktivitätsringe hat sich als wirksam erwiesen, um die tägliche körperliche Aktivität um 25–35 % durch Gamification zu steigern. Studien zeigen, dass Benutzer, die ihre Aktivitätsringe konsequent schließen, ein 40 % höheres Motivationsniveau und eine 30 % bessere Zielfestlegung berichten im Vergleich zu denen ohne visuelles Feedback. Die befriedigende Animation des „Schließens eines Rings" löst eine Dopaminfreisetzung aus und verstärkt positive Gesundheitsverhaltensweisen.
Dieses Projekt ist ein fantastischer Ausgangspunkt. Sie können diese Konzepte nun anwenden, um Ihre eigenen benutzerdefinierten Datenvisualisierungen, Ladeindikatoren oder beliebige andere kreisförmige UI-Elemente zu erstellen, die Sie sich vorstellen können.
Nächste Schritte für Sie:
- Versuchen Sie, Verläufe zu den Strichen hinzuzufügen, anstatt fester Farben
- Animieren Sie den Fortschrittswert, wenn eine Schaltfläche geklickt wird
- Fügen Sie Beschriftungen oder Symbole zur Mitte der Ringe hinzu
Für fortgeschrittenere Visualisierungen erkunden Sie den Aufbau von performanten D3-Gesundheitsdiagrammen für große Datensätze oder zugängliche Datenvisualisierungen nach WCAG, um sicherzustellen, dass Ihre Visualisierungen für alle funktionieren.
Ressourcen
- Framer Motion Dokumentation: https://www.framer.com/motion/
- MDN-Dokumentation für SVG
<circle>: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/circle - MDN-Dokumentation für
stroke-dasharray: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray
Kurzübersicht
| Aspekt | Wichtige Informationen |
|---|---|
| Thema | Entwicklung |
| Relevanz | Patientenaufklärung und -sensibilisierung |
| Maßnahme | Besprechen Sie dies mit Ihrem Arzt |
| Häufigkeit | Nach Empfehlung Ihres Behandlungsteams |
| Evidenz | Basierend auf aktuellen klinischen Leitlinien |
Häufig gestellte Fragen
F: Woher weiß ich, ob dies auf mich zutrifft? A: Besprechen Sie Ihre persönliche und familiäre Krankengeschichte mit Ihrem Arzt, um Ihre individuellen Risikofaktoren zu bewerten und geeignete nächste Schritte zu bestimmen.
F: Was sollte ich meinen Arzt fragen? A: Gute Fragen sind: „Was bedeuten meine Ergebnisse?", „Was sind meine Optionen?", „Welche Lebensstiländerungen würden helfen?" und „Wann sollte ich nachuntersuchen?"
F: Gibt es Lebensstiländerungen, die helfen können? A: Die meisten Gesundheitszustände profitieren von grundlegenden Maßnahmen: ausgewogene Ernährung, regelmäßige körperliche Aktivität (150+ Minuten/Woche), качественный Schlaf (7–9 Stunden), Stressbewältigung und Vermeidung von Tabak.
Haftungsausschluss
Die in diesem Artikel vorgestellten Algorithmen und Techniken dienen ausschließlich technischen Bildungszwecken. Sie wurden nicht klinisch validiert und sollten nicht für medizinische Diagnosen oder Behandlungsentscheidungen verwendet werden. Konsultieren Sie immer qualifizierte Gesundheitsfachleute für medizinischen Rat.