Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Animationen mit Reanimated in React Native

Animationen mit Reanimated

~17 Min. Lesezeit Zuletzt aktualisiert am 8. August 2026

React Natives eingebaute Animated-API (siehe "React Native Referenz", Thema "Ein-/Ausblenden-Animation") läuft bereits performant auf dem NATIVE-Thread dank useNativeDriver: true – ABER nur für eine begrenzte Menge an Eigenschaften (opacity, transform). Reanimated geht einen entscheidenden Schritt weiter: JavaScript-Code läuft DIREKT auf dem UI-Thread, dank JSI aus dem letzten Kapitel.

Worklets: der Kern von Reanimated

Ein Worklet ist eine kleine JavaScript-Funktion, die Reanimated (dank JSI) auf den UI-Thread "kopiert" und dort AUSFÜHRT – nicht auf dem normalen JS-Thread. Das bedeutet: selbst wenn der JS-Thread durch teure Arbeit blockiert ist (erinnern Sie sich an die künstlich blockierte Suche aus "React für Profis" Kapitel 33), laufen Reanimated-Animationen UNBEEINTRÄCHTIGT weiter, ohne einen Frame zu verlieren.

Installation

npx expo install react-native-reanimated
babel.config.js
module.exports = function (api) {
  api.cache(true);
  return {
    presets: ['babel-preset-expo'],
    plugins: ['react-native-reanimated/plugin'], // MUSS das letzte Plugin in der Liste sein
  };
};

Achtung: Das Babel-Plugin ist PFLICHT, nicht optional – es transformiert Funktionen, die mit 'worklet' markiert sind (oder implizit als Worklet erkannt werden, z. B. innerhalb von useAnimatedStyle), sodass sie auf dem UI-Thread ausführbar werden. Ohne dieses Plugin schlägt Reanimated zur Laufzeit fehl.

Praxis: das Favoriten-Herz in ProductCard animieren

Wir erweitern ProductCards Herz-Button aus Kapitel 3 um eine kleine "Pop"-Animation beim Favorisieren – ein typischer, kleiner Polish-Effekt, der die App hochwertiger wirken lässt:

components/ProductCard.js
import { memo } from 'react';
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
import { Image } from 'expo-image';
import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withSpring,
} from 'react-native-reanimated';

const BLURHASH = '|rF?hV%2WCj[ayj[a|j[az_NaeWBj@ayfRayfQfQM{M|azj[azf6fQfQfQIpWXofj[ayj[j[fQayWCoeoeaya}j[ayfQa{oLj?j[WVj[ayayj[fQoff7azayj[ayj[j[ayofayayayj[fQj[ayayj[ayfjj[j[ayjuayj[';

function ProductCard({ name, price, imageUrl, onPress, isFavorite, onToggleFavorite }) {
  const scale = useSharedValue(1);

  const heartStyle = useAnimatedStyle(() => ({
    transform: [{ scale: scale.value }],
  }));

  function handleToggleFavorite() {
    scale.value = withSpring(1.4, { damping: 4 }, () => {
      scale.value = withSpring(1);
    });
    onToggleFavorite();
  }

  return (
    <TouchableOpacity style={styles.card} onPress={onPress}>
      <Image
        source={imageUrl}
        style={styles.image}
        placeholder={{ blurhash: BLURHASH }}
        contentFit="cover"
        transition={200}
        cachePolicy="memory-disk"
      />
      <View style={styles.info}>
        <Text style={styles.name}>{name}</Text>
        <Text style={styles.price}>${price.toFixed(2)}</Text>
      </View>
      <TouchableOpacity style={styles.favoriteButton} onPress={handleToggleFavorite}>
        <Animated.Text style={[styles.favoriteIcon, heartStyle]}>
          {isFavorite ? '♥' : '♡'}
        </Animated.Text>
      </TouchableOpacity>
    </TouchableOpacity>
  );
}

const styles = StyleSheet.create({
  card: {
    flexDirection: 'row',
    padding: 12,
    marginBottom: 8,
    backgroundColor: '#f9fafb',
    borderRadius: 8,
    alignItems: 'center',
  },
  image: { width: 60, height: 60, borderRadius: 4 },
  info: { marginLeft: 12, flex: 1 },
  name: { fontSize: 16, fontWeight: '600' },
  price: { fontSize: 14, color: '#6b7280' },
  favoriteButton: { padding: 8 },
  favoriteIcon: { fontSize: 20, color: '#ef4444' },
});

export default memo(ProductCard);

Die drei Kernbausteine im Detail

  • useSharedValue(1) – Reanimateds Äquivalent zu useState, ABER der Wert lebt AUSSERHALB des normalen React-Renderzyklus, direkt zugänglich vom UI-Thread. Lesen/Schreiben über .value, NICHT wie ein normaler State-Setter.
  • useAnimatedStyle(() => ({{ ... }})) – ein Worklet, das aus einem oder mehreren Shared Values ein Style-Objekt berechnet. Läuft bei JEDER Änderung von scale.value AUTOMATISCH auf dem UI-Thread neu, OHNE dass React selbst einen Re-Render der Komponente auslöst.
  • withSpring(1.4, {{ damping: 4 }}, callback) – eine federphysik-basierte Animationsfunktion (Alternative: withTiming für lineares/kurvenbasiertes Timing wie React Natives Animated.timing) – der optionale dritte Parameter ist ein Callback, der nach ABSCHLUSS der Animation läuft, hier genutzt, um sofort zurück auf Größe 1 zu federn.

Achtung: <Animated.Text> statt normalem <Text> ist ZWINGEND – GENAU wie bei React Natives eingebauter Animated-API (siehe "React Native Referenz") können nur Animated.*-Komponenten animierte Style-Werte entgegennehmen.

Warum das schneller ist als React Natives eingebautes Animated

React Natives Animated-API mit useNativeDriver: true läuft zwar AUCH auf dem nativen Thread, MUSS aber die Animation VORHER als deklarative Konfiguration ("animiere von X nach Y über Z Millisekunden") an die native Seite übergeben – komplexe, dynamische Logik (z. B. "reagiere in Echtzeit auf eine Gesten-Position", siehe nächstes Kapitel) lässt sich damit kaum ausdrücken. Reanimated-Worklets sind ECHTER JavaScript-Code, der auf dem UI-Thread läuft – beliebig komplexe Logik, bedingte Verzweigungen, Berechnungen, alles mit derselben nativen Performance.

Tipp: Faustregel: für einfache, rein deklarative Animationen (Ein-/Ausblenden, feste Übergänge) reicht React Natives eingebautes Animated oft aus – weniger Abhängigkeiten, weniger zu lernen. Reanimated lohnt sich, sobald Animationen auf Nutzer-GESTEN in Echtzeit reagieren müssen (Swipe-Gesten, Drag-and-Drop, interaktive Übergänge) – genau das Thema des nächsten Kapitels.