Animating with React Native Skia

Lesson 5 of 6

Gestures: Dragging and Spring Animation

Pairing Skia with react-native-gesture-handler is how you build draggable, physics-feeling graphics, a slider, a drag-to-dismiss card, or a springy button.

Reading a pan gesture into a Skia value

import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import { Canvas, Circle, useValue } from '@shopify/react-native-skia';

export function DraggableDot() {
  const cx = useValue(100);
  const cy = useValue(100);

  const pan = Gesture.Pan().onChange((e) => {
    cx.current += e.changeX;
    cy.current += e.changeY;
  });

  return (
    <GestureDetector gesture={pan}>
      <Canvas style={{ flex: 1 }}>
        <Circle cx={cx} cy={cy} r={30} color="#22d3ee" />
      </Canvas>
    </GestureDetector>
  );
}

onChange fires on every frame of the gesture with the delta since the last event (changeX/changeY), adding that delta straight to the Skia values moves the circle in perfect sync with the finger, again with zero React re-renders.

Springing back on release

runSpring animates a value using spring physics (mass, damping, stiffness) instead of a fixed duration, ideal for "let go and it snaps back":

import { runSpring } from '@shopify/react-native-skia';

const pan = Gesture.Pan()
  .onChange((e) => {
    cx.current += e.changeX;
    cy.current += e.changeY;
  })
  .onEnd(() => {
    runSpring(cx, 100, { mass: 1, damping: 10, stiffness: 150 });
    runSpring(cy, 100, { mass: 1, damping: 10, stiffness: 150 });
  });

Higher damping settles faster with less bounce, higher stiffness snaps back faster, tune both to taste.

📝 Gestures Quiz

Passing score: 70%
  1. 1.Which gesture-handler event fires on every frame of a pan gesture with the movement delta?

  2. 2.____ animates a Skia value using spring physics instead of a fixed duration.

  3. 3.Reading gesture deltas straight into a Skia useValue still causes a React re-render on every frame.