React Native Academy · Lektion

Pan- och svepgester med Gesture Handler

Installera React Native Gesture Handler, bygg ett dragbart kort med PanGestureHandler och useAnimatedGestureHandler och låt det återgå till ursprungspositionen när ni släpper det.

Lektion 3 av 413 steg

Pan- och svepgester med Gesture Handler är en gratis lektion i React Native Academy på CoddyKit. Detta är lektion 3 av 4. Du kan läsa vilka 3 lektioner som helst i den här lärvägen kostnadsfritt i sin helhet – därefter låser CoddyKit PRO upp alla lektioner, plus praktisk övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Den ingår i lärvägen för React Native Academy, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i React Native Academy innehåller totalt 4 lektioner.

Vad är React Native Gesture Handler?

React Native Gesture Handler ersätter det inbyggda beröringssystemet med ett som körs helt på den inbyggda UI-tråden. React Natives standardbaserade responder-system behandlar beröringshändelser på JavaScript-tråden, vilket skapar fördröjning när JS är upptagen. Gesture Handler flyttar identifiering och respons till inbyggd kod, vilket ger samma snabba känsla som inbyggda appar för iOS och Android.

Gesture Handler är Reanimateds standardkomplement — de är utformade för att fungera tillsammans. Det tillhandahåller identifierare för panorering, nypning, rotation, tryck, långtryckning och fling-gester som matar data direkt till Reanimated-worklets.

// Install:
// npx expo install react-native-gesture-handler

// Wrap your app root with GestureHandlerRootView:
import { GestureHandlerRootView } from 'react-native-gesture-handler';

export default function App() {
  return (
    <GestureHandlerRootView style={{ flex: 1 }}>
      <YourApp />
    </GestureHandlerRootView>
  );
}

Gesture API (v2)

Gesture Handler v2 (det moderna API:et) använder fabriksobjektet Gesture för att skapa gester och komponenten GestureDetector för att koppla dem till UI:t. Detta ersätter det äldre komponentbaserade API:et (PanGestureHandler, TapGestureHandler) med en renare kompositionsmodell.

Du skapar ett gestobjekt (till exempel Gesture.Pan()), konfigurerar det med metodkedjor och omsluter komponenten med GestureDetector. Gestcallback-funktionerna är worklets som körs på UI-tråden.

import { Gesture, GestureDetector } from 'react-native-gesture-handler';

const panGesture = Gesture.Pan()
  .onUpdate((event) => {
    'worklet';
    // event.translationX, event.translationY available
  });

return (
  <GestureDetector gesture={panGesture}>
    <Animated.View style={animatedStyle} />
  </GestureDetector>
);

Bygga ett dragbart kort

Det enklaste exemplet på en pan-gest är ett dragbart kort. Skapa delade värden för x- och y-positionen. I onUpdate lägger du gestens translationX/translationY till en sparad startposition. I onEnd sparar du den aktuella positionen som den nya startpositionen, så att nästa drag fortsätter där kortet stannade.

Kortet följer visuellt fingret utan någon medverkan från JavaScript-tråden — helt mjukt även när JS är belastat.

import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import Animated, { useSharedValue, useAnimatedStyle } from 'react-native-reanimated';

const offsetX = useSharedValue(0);
const offsetY = useSharedValue(0);
const startX = useSharedValue(0);
const startY = useSharedValue(0);

const pan = Gesture.Pan()
  .onBegin(() => {
    startX.value = offsetX.value;
    startY.value = offsetY.value;
  })
  .onUpdate((e) => {
    offsetX.value = startX.value + e.translationX;
    offsetY.value = startY.value + e.translationY;
  });

const style = useAnimatedStyle(() => ({
  transform: [
    { translateX: offsetX.value },
    { translateY: offsetY.value },
  ],
}));

Snäppa tillbaka till ursprunget vid släpp

Ett vanligt mönster är att låta användaren dra ett kort fritt men snäppa tillbaka det till dess ursprungliga position när det släpps. I callback-funktionen onFinalize eller onEnd tilldelar du en withSpring-animering till offset-värdena, så att de återgår till noll med en tillfredsställande studs.

Fjäderns initiala hastighet kan anges utifrån gestens sluthastighet (event.velocityX, event.velocityY) för att skapa en naturlig kast-och-fånga-effekt som fortsätter rörelsen innan kortet studsar tillbaka.

import { withSpring } from 'react-native-reanimated';

const pan = Gesture.Pan()
  .onUpdate((e) => {
    offsetX.value = startX.value + e.translationX;
    offsetY.value = startY.value + e.translationY;
  })
  .onEnd((e) => {
    offsetX.value = withSpring(0, { velocity: e.velocityX });
    offsetY.value = withSpring(0, { velocity: e.velocityY });
  });

Mönstret svep för att stänga

Ett kort med funktionen svep för att stänga spårar den horisontella positionen under panoreringen. När kortet släpps med tillräcklig horisontell hastighet eller förflyttning animeras det utanför skärmen med withTiming eller withDecay i stället för att snäppa tillbaka.

Använd event.velocityX för att avgöra om kastet var avsiktligt (tillräckligt snabbt), och event.translationX för att kontrollera om kortet drogs tillräckligt långt. Om något av tröskelvärdena uppnås flyger du kortet utanför skärmen och anropar runOnJS för att meddela React att ta bort det.

const DISMISS_VELOCITY = 800;
const DISMISS_DISTANCE = 150;

const pan = Gesture.Pan()
  .onUpdate((e) => { offsetX.value = e.translationX; })
  .onEnd((e) => {
    const shouldDismiss =
      Math.abs(e.velocityX) > DISMISS_VELOCITY ||
      Math.abs(e.translationX) > DISMISS_DISTANCE;

    if (shouldDismiss) {
      const direction = e.translationX > 0 ? 1 : -1;
      offsetX.value = withTiming(direction * 400, { duration: 200 },
        () => runOnJS(onDismiss)()
      );
    } else {
      offsetX.value = withSpring(0);
    }
  });

Tryckgest för tryckinteraktioner

Gesture.Tap() känner igen tryck. Till skillnad från Pressable eller TouchableOpacity kör en tryckgest från Gesture Handler sin callback på UI-tråden, vilket möjliggör animerad återkoppling utan inblandning av JavaScript. Ni kan konfigurera maxDuration och numberOfTaps för att identifiera dubbeltryck.

Tryckgester är särskilt användbara tillsammans med Reanimated: callbacken för tryckgesten kan direkt utlösa en fjäderanimation för skala eller opacitet, vilket ger visuell återkoppling utan fördröjning och känns omedelbar.

const scale = useSharedValue(1);

const tap = Gesture.Tap()
  .onBegin(() => {
    scale.value = withSpring(0.95);
  })
  .onFinalize(() => {
    scale.value = withSpring(1);
  });

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

return (
  <GestureDetector gesture={tap}>
    <Animated.View style={[styles.button, style]} />
  </GestureDetector>
);

Kombinera gester: samtidiga och exklusiva

Gesture Handler gör det möjligt att kombinera flera gester på samma komponent med hjälp av Gesture.Simultaneous (båda körs samtidigt) och Gesture.Exclusive (endast en kan vara aktiv åt gången). Det möjliggör komplexa interaktioner, till exempel att svepa samtidigt som ni zoomar i en bildvisare.

Använd Gesture.Simultaneous(panGesture, pinchGesture) för att tillåta både panorering och nypgest på samma element samtidigt. Använd Gesture.Exclusive(swipeGesture, tapGesture) för att säkerställa att ett svep avbryter identifieringen av ett tryck.

const pan = Gesture.Pan().onUpdate(...);
const pinch = Gesture.Pinch().onUpdate(...);

// Both work at the same time (e.g. photo viewer):
const composedGesture = Gesture.Simultaneous(pan, pinch);

// Only one wins (e.g. swipe OR tap):
const exclusiveGesture = Gesture.Exclusive(
  Gesture.Fling().direction(Directions.LEFT).onEnd(onSwipe),
  Gesture.Tap().onEnd(onTap)
);

Gestens livscykel: början, uppdatering, slut och slutförande

Varje gestidentifierare har callback-funktioner för livscykeln: onBegin körs när gesten identifieras, onUpdate körs vid varje ny beröringshändelse, onEnd körs när gesten slutförs utan fel och onFinalize körs efter gestens slut eller när den avbryts.

Använd onBegin för att spara startpositionen, onUpdate för att följa rörelsen, onEnd för att hantera ett lyckat slutförande och onFinalize för att städa upp oavsett om gesten lyckades eller avbröts (till exempel av ett telefonsamtal).

const pan = Gesture.Pan()
  .onBegin(() => { startPos.value = offsetX.value; })
  .onUpdate((e) => { offsetX.value = startPos.value + e.translationX; })
  .onEnd((e) => {
    // Gesture completed successfully
    if (Math.abs(e.translationX) > 100) runOnJS(onSwipeAway)();
    else offsetX.value = withSpring(0);
  })
  .onFinalize(() => {
    // Always runs — reset any temporary state
  });

Långtrycksgest

Gesture.LongPress() känner igen tryck som varar längre än en minimitid. Konfigurera .minDuration(ms) för att ange hur länge trycket ska vara. Ett vanligt mönster är att utlösa haptisk återkoppling och aktivera ett redigeringsläge efter ett långtryck, till exempel när objekt i en lista ska ordnas om.

Kombinera långtryck med panorering för att dra och ordna om: långtrycket aktiverar dragläget och panoreringen flyttar objektet medan dragläget är aktivt. Gesture.Exclusive säkerställer att ett tryck ignoreras medan dragning pågår.

import * as Haptics from 'expo-haptics';

const [isDragging, setIsDragging] = React.useState(false);

const longPress = Gesture.LongPress()
  .minDuration(400)
  .onStart(() => {
    scale.value = withSpring(1.05);
    runOnJS(Haptics.selectionAsync)();
    runOnJS(setIsDragging)(true);
  });

const pan = Gesture.Pan()
  .enabled(isDragging)
  .onUpdate((e) => {
    offsetY.value = e.translationY;
  });

Gestens tillstånd och aktivering/inaktivering

Gester kan aktiveras villkorligt med .enabled(boolean). När en gest är inaktiverad aktiveras den inte, vilket gör det möjligt att växla interaktivitet utifrån appens tillstånd. Ni kan också anropa .shouldCancelWhenOutside(true) för att avbryta en gest när fingret flyttas utanför komponentens gränser.

Alternativet .hitSlop utökar området som reagerar på tryck utanför komponentens visuella gränser — användbart för små ikonknappar som är svåra att trycka på exakt på mobilskärmar.

const [isEnabled, setIsEnabled] = React.useState(true);

const tap = Gesture.Tap()
  .enabled(isEnabled)
  .hitSlop({ top: 10, bottom: 10, left: 10, right: 10 })
  .shouldCancelWhenOutside(true)
  .onEnd(() => {
    runOnJS(handleTap)();
  });

// Disable temporarily:
setIsEnabled(false); // tap won't fire

Hantera gestkonflikter med ScrollView

En vanlig utmaning är ett dragbart kort inuti en ScrollView — båda konkurrerar om samma beröringshändelse. Gesture Handler tillhandahåller simultaneousWithExternalGesture och blocksExternalGesture för att styra prioriteten. Alternativet waitFor fördröjer en gest tills en annan misslyckas, vilket möjliggör mönster med nästlade flöden där rullning sker inuti ett svep.

För inbyggda rullningsvyer bör ni omsluta dem med ScrollView från Gesture Handler (import { ScrollView } from 'react-native-gesture-handler') i stället för React Natives standardkomponent, så att gestsamordningen fungerar korrekt.

import { ScrollView } from 'react-native-gesture-handler';

// Use GH's ScrollView to allow gesture coordination:
<ScrollView>
  <GestureDetector gesture={pan}>
    <Animated.View style={style}>
      {/* Draggable card inside scroll */}
    </Animated.View>
  </GestureDetector>
</ScrollView>

Snabbtest

Testa er förståelse av begreppen inom React Native Mobile Development från den här lektionen.

Sammanfattning av lektionen

I den här lektionen har ni lärt er att Gesture.Pan() skapar en gestidentifierare för panorering som följer fingrets förflyttning i onUpdate och avslutas i onEnd, att withSpring på offset-värden skapar effekter där objekt fjädrar tillbaka eller bort efter att fingret släppts och att Gesture.Simultaneous kombinerar flera gester så att de fungerar samtidigt. Nästa steg är att kombinera PinchGestureHandler och rotation för att skapa en bildkomponent som kan zoomas.

Gratis att börja

Lär dig JavaScript med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
30
Lektioner
120

Vanliga frågor

Är lektionen ”Pan- och svepgester med Gesture Handler” gratis?

Ja – du kan läsa vilka 3 lektioner som helst i lärvägen React Native Academy, inklusive ”Pan- och svepgester med Gesture Handler”, kostnadsfritt i sin helhet här på webben. Därefter låser CoddyKit PRO upp alla lektioner, plus interaktiv övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Kursen i React Native Academy innehåller totalt 4 lektioner.

Vad lär jag mig i ”Pan- och svepgester med Gesture Handler”?

Installera React Native Gesture Handler, bygg ett dragbart kort med PanGestureHandler och useAnimatedGestureHandler och låt det återgå till ursprungspositionen när ni släpper det. Ni övar på React Native Academy med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig React Native Academy?

Du behöver inga förkunskaper. Utbildningen i React Native Academy på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 3 av 4.

Hur lång tid tar lektionen ”Pan- och svepgester med Gesture Handler”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här React Native Academy-lektionen?

Ja. Varje React Native Academy-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Installera Reanimated och delade värden
  2. Worklets och kodkörning på UI-tråden
  3. Pan- och svepgester med Gesture Handler
  4. Nyp för att zooma och rotera
← Tillbaka till React Native Academy