More powerful alternative to Animated library for React Native with UI thread animations and advanced gesture handling.
—
Utilities for measuring components, scrolling, native platform integration, and accessing platform-specific functionality for optimal performance and native behavior.
Functions for measuring component dimensions and positions.
/**
* Measures the dimensions and position of a component
* @param ref - Animated ref to the component to measure
* @returns MeasuredDimensions object or null if measurement fails
*/
function measure(ref: AnimatedRef<any>): MeasuredDimensions | null;
/**
* Gets relative coordinates within a component
* @param ref - Animated ref to the reference component
* @param x - X coordinate to convert
* @param y - Y coordinate to convert
* @returns ComponentCoords object or null if conversion fails
*/
function getRelativeCoords(
ref: AnimatedRef<any>,
x: number,
y: number
): ComponentCoords | null;
interface MeasuredDimensions {
/** Component x position relative to its parent */
x: number;
/** Component y position relative to its parent */
y: number;
/** Component width */
width: number;
/** Component height */
height: number;
/** Component x position relative to the screen */
pageX: number;
/** Component y position relative to the screen */
pageY: number;
}
interface ComponentCoords {
/** X coordinate relative to the component */
x: number;
/** Y coordinate relative to the component */
y: number;
}Usage Examples:
import React, { useRef } from "react";
import Animated, {
useAnimatedRef,
measure,
getRelativeCoords,
useSharedValue,
useAnimatedStyle,
runOnUI,
runOnJS
} from "react-native-reanimated";
import { Button } from "react-native";
const MeasurementExample = () => {
const containerRef = useAnimatedRef();
const targetRef = useAnimatedRef();
const ballPosition = useSharedValue({ x: 50, y: 50 });
// Measure component dimensions
const measureComponent = () => {
runOnUI(() => {
'worklet';
const dimensions = measure(targetRef);
if (dimensions) {
runOnJS(console.log)('Component dimensions:', dimensions);
// Use measurements for animations
ballPosition.value = {
x: dimensions.width / 2,
y: dimensions.height / 2
};
}
})();
};
// Convert screen coordinates to component-relative coordinates
const handlePress = (event: any) => {
const { locationX, locationY } = event.nativeEvent;
runOnUI(() => {
'worklet';
const coords = getRelativeCoords(containerRef, locationX, locationY);
if (coords) {
ballPosition.value = coords;
runOnJS(console.log)('Relative coordinates:', coords);
}
})();
};
const ballStyle = useAnimatedStyle(() => ({
transform: [
{ translateX: ballPosition.value.x - 25 }, // Center the ball
{ translateY: ballPosition.value.y - 25 },
],
}));
return (
<>
<Button title="Measure Target" onPress={measureComponent} />
<Animated.View
ref={containerRef}
onTouchEnd={handlePress}
style={{
width: 300,
height: 200,
backgroundColor: "lightgray",
margin: 20,
borderRadius: 10,
position: "relative",
}}
>
<Animated.Text>Touch anywhere to move the ball</Animated.Text>
{/* Target component to measure */}
<Animated.View
ref={targetRef}
style={{
width: 100,
height: 100,
backgroundColor: "lightblue",
position: "absolute",
top: 20,
right: 20,
borderRadius: 5,
}}
>
<Animated.Text>Target</Animated.Text>
</Animated.View>
{/* Animated ball */}
<Animated.View
style={[
{
width: 50,
height: 50,
backgroundColor: "red",
borderRadius: 25,
position: "absolute",
},
ballStyle,
]}
/>
</Animated.View>
</>
);
};Programmatic scrolling control for ScrollView and other scrollable components.
/**
* Programmatically scrolls a scrollable component
* @param ref - Animated ref to the scrollable component
* @param x - X coordinate to scroll to
* @param y - Y coordinate to scroll to
* @param animated - Whether to animate the scroll
*/
function scrollTo(
ref: AnimatedRef<any>,
x: number,
y: number,
animated: boolean
): void;Usage Examples:
import React from "react";
import Animated, {
useAnimatedRef,
scrollTo,
useSharedValue,
useAnimatedScrollHandler,
runOnUI
} from "react-native-reanimated";
import { Button } from "react-native";
const ScrollControlExample = () => {
const scrollRef = useAnimatedRef();
const scrollY = useSharedValue(0);
// Track scroll position
const scrollHandler = useAnimatedScrollHandler({
onScroll: (event) => {
scrollY.value = event.contentOffset.y;
},
});
// Scroll to specific positions
const scrollToTop = () => {
runOnUI(() => {
'worklet';
scrollTo(scrollRef, 0, 0, true);
})();
};
const scrollToMiddle = () => {
runOnUI(() => {
'worklet';
scrollTo(scrollRef, 0, 500, true);
})();
};
const scrollToBottom = () => {
runOnUI(() => {
'worklet';
scrollTo(scrollRef, 0, 1500, true);
})();
};
// Smooth scrolling animation
const smoothScrollTo = (targetY: number) => {
runOnUI(() => {
'worklet';
const startY = scrollY.value;
const distance = targetY - startY;
const duration = Math.abs(distance) / 2; // Adjust speed based on distance
// Custom smooth scroll implementation
const startTime = Date.now();
const animate = () => {
'worklet';
const elapsed = Date.now() - startTime;
const progress = Math.min(elapsed / duration, 1);
const easeProgress = 1 - Math.pow(1 - progress, 3); // Ease out cubic
const currentY = startY + distance * easeProgress;
scrollTo(scrollRef, 0, currentY, false);
if (progress < 1) {
// Continue animation
setTimeout(() => animate(), 16); // ~60fps
}
};
animate();
})();
};
return (
<>
<Button title="Scroll to Top" onPress={scrollToTop} />
<Button title="Scroll to Middle" onPress={scrollToMiddle} />
<Button title="Scroll to Bottom" onPress={scrollToBottom} />
<Button title="Smooth Scroll to 800" onPress={() => smoothScrollTo(800)} />
<Animated.ScrollView
ref={scrollRef}
onScroll={scrollHandler}
scrollEventThrottle={16}
style={{ height: 400 }}
>
{/* Content to scroll through */}
{Array.from({ length: 100 }).map((_, index) => (
<Animated.View
key={index}
style={{
height: 60,
backgroundColor: index % 2 ? "white" : "lightgray",
justifyContent: "center",
alignItems: "center",
borderBottomWidth: 1,
borderBottomColor: "gray",
}}
>
<Animated.Text>Item {index} - Y: {index * 60}</Animated.Text>
</Animated.View>
))}
</Animated.ScrollView>
</>
);
};Direct manipulation of native component properties for performance-critical updates.
/**
* Sets native properties on a component directly
* @param ref - Animated ref to the component
* @param props - Object containing properties to set
*/
function setNativeProps(ref: AnimatedRef<any>, props: object): void;
/**
* Dispatches a command to a native component
* @param ref - Animated ref to the component
* @param commandName - Name of the command to dispatch
* @param args - Optional arguments for the command
*/
function dispatchCommand(
ref: AnimatedRef<any>,
commandName: string,
args?: any[]
): void;
/**
* Gets a property value from a native component
* @param ref - Animated ref to the component
* @param prop - Property name to get
* @returns Property value
*/
function getViewProp(ref: AnimatedRef<any>, prop: string): any;Usage Examples:
import React from "react";
import { TextInput } from "react-native";
import Animated, {
useAnimatedRef,
setNativeProps,
dispatchCommand,
getViewProp,
useSharedValue,
useAnimatedStyle,
runOnUI,
runOnJS
} from "react-native-reanimated";
import { Button } from "react-native";
const NativePropsExample = () => {
const textInputRef = useAnimatedRef();
const viewRef = useAnimatedRef();
const opacity = useSharedValue(1);
// Direct native prop manipulation
const changeInputProps = () => {
runOnUI(() => {
'worklet';
setNativeProps(textInputRef, {
text: "Updated via native props!",
placeholder: "Native update",
backgroundColor: "#ffeeee",
});
})();
};
// Focus input using dispatch command
const focusInput = () => {
runOnUI(() => {
'worklet';
dispatchCommand(textInputRef, "focus", []);
})();
};
// Blur input using dispatch command
const blurInput = () => {
runOnUI(() => {
'worklet';
dispatchCommand(textInputRef, "blur", []);
})();
};
// Get view properties
const getViewProperties = () => {
runOnUI(() => {
'worklet';
const opacity = getViewProp(viewRef, "opacity");
const backgroundColor = getViewProp(viewRef, "backgroundColor");
runOnJS(console.log)("View opacity:", opacity);
runOnJS(console.log)("View backgroundColor:", backgroundColor);
})();
};
// Batch native updates for performance
const batchNativeUpdates = () => {
runOnUI(() => {
'worklet';
// Multiple native updates in single UI frame
setNativeProps(viewRef, {
backgroundColor: `hsl(${Math.random() * 360}, 70%, 80%)`,
borderRadius: Math.random() * 20,
borderWidth: Math.random() * 5,
});
setNativeProps(textInputRef, {
fontSize: 14 + Math.random() * 10,
color: Math.random() > 0.5 ? "black" : "blue",
});
})();
};
const animatedStyle = useAnimatedStyle(() => ({
opacity: opacity.value,
}));
return (
<>
<Button title="Change Input Props" onPress={changeInputProps} />
<Button title="Focus Input" onPress={focusInput} />
<Button title="Blur Input" onPress={blurInput} />
<Button title="Get View Props" onPress={getViewProperties} />
<Button title="Batch Updates" onPress={batchNativeUpdates} />
<Animated.View
ref={viewRef}
style={[
{
width: 200,
height: 100,
backgroundColor: "lightblue",
margin: 20,
padding: 10,
borderRadius: 10,
},
animatedStyle,
]}
>
<TextInput
ref={textInputRef}
style={{
height: 40,
borderWidth: 1,
borderColor: "gray",
borderRadius: 5,
paddingHorizontal: 10,
backgroundColor: "white",
}}
placeholder="Type something..."
/>
</Animated.View>
</>
);
};Integration with gesture handling systems for advanced touch interactions.
/**
* Sets the state of a gesture handler
* @param handlerTag - Tag identifying the gesture handler
* @param newState - New state to set for the handler
*/
function setGestureState(handlerTag: number, newState: number): void;Usage Example:
import React from "react";
import Animated, {
useSharedValue,
useAnimatedStyle,
useAnimatedGestureHandler,
setGestureState,
runOnUI
} from "react-native-reanimated";
import { PanGestureHandler, State } from "react-native-gesture-handler";
const GestureIntegrationExample = () => {
const translateX = useSharedValue(0);
const translateY = useSharedValue(0);
const gestureHandlerTag = useSharedValue(0);
// Gesture handler with state control
const gestureHandler = useAnimatedGestureHandler({
onStart: (_, context) => {
context.startX = translateX.value;
context.startY = translateY.value;
},
onActive: (event, context) => {
translateX.value = context.startX + event.translationX;
translateY.value = context.startY + event.translationY;
// Control gesture state based on conditions
if (Math.abs(event.translationX) > 200) {
// Cancel gesture if moved too far
setGestureState(gestureHandlerTag.value, State.CANCELLED);
}
},
onEnd: (event) => {
// Snap back to center if released
if (Math.abs(translateX.value) > 100 || Math.abs(translateY.value) > 100) {
translateX.value = withSpring(0);
translateY.value = withSpring(0);
}
},
});
// Programmatic gesture control
const resetGesture = () => {
runOnUI(() => {
'worklet';
setGestureState(gestureHandlerTag.value, State.END);
translateX.value = withSpring(0);
translateY.value = withSpring(0);
})();
};
const animatedStyle = useAnimatedStyle(() => ({
transform: [
{ translateX: translateX.value },
{ translateY: translateY.value },
],
}));
return (
<>
<Button title="Reset Gesture" onPress={resetGesture} />
<PanGestureHandler
onGestureEvent={gestureHandler}
onHandlerStateChange={(event) => {
gestureHandlerTag.value = event.nativeEvent.handlerTag;
}}
>
<Animated.View
style={[
{
width: 100,
height: 100,
backgroundColor: "lightcoral",
borderRadius: 10,
margin: 50,
},
animatedStyle,
]}
>
<Animated.Text style={{ textAlign: "center", marginTop: 35 }}>
Drag me!
</Animated.Text>
</Animated.View>
</PanGestureHandler>
</>
);
};Utilities for handling platform differences and native integration.
Version and Configuration Checks:
import {
isConfigured,
isReanimated3,
reanimatedVersion
} from "react-native-reanimated";
// Check if Reanimated is properly configured
if (isConfigured()) {
console.log("Reanimated is ready");
}
// Check version
if (isReanimated3()) {
console.log("Using Reanimated 3+");
}
console.log("Reanimated version:", reanimatedVersion);Configuration and Debugging:
import {
configureReanimatedLogger,
ReanimatedLogLevel,
enableLayoutAnimations
} from "react-native-reanimated";
// Configure logging
configureReanimatedLogger({
level: ReanimatedLogLevel.warn,
strict: false,
});
// Enable layout animations (required for some platforms)
enableLayoutAnimations(true);Monitor and optimize animation performance using built-in tools.
/**
* Performance monitoring component for tracking animation performance
*/
const PerformanceMonitor: React.ComponentType<PerformanceMonitorProps>;
interface PerformanceMonitorProps {
/** Whether to show performance overlay */
enabled?: boolean;
/** Callback for performance data */
onPerformanceData?: (data: PerformanceData) => void;
}
interface PerformanceData {
/** Frames per second */
fps: number;
/** JavaScript thread usage */
jsThreadUsage: number;
/** UI thread usage */
uiThreadUsage: number;
/** Memory usage */
memoryUsage: number;
}Usage Example:
import React, { useState } from "react";
import Animated, {
PerformanceMonitor,
useSharedValue,
useAnimatedStyle,
withRepeat,
withTiming
} from "react-native-reanimated";
const PerformanceExample = () => {
const [performanceEnabled, setPerformanceEnabled] = useState(false);
const rotation = useSharedValue(0);
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ rotate: `${rotation.value}deg` }],
}));
const startHeavyAnimation = () => {
rotation.value = withRepeat(
withTiming(360, { duration: 100 }),
-1,
false
);
};
const handlePerformanceData = (data: PerformanceData) => {
console.log("Performance:", data);
};
return (
<>
<PerformanceMonitor
enabled={performanceEnabled}
onPerformanceData={handlePerformanceData}
/>
<Button
title={`Performance Monitor: ${performanceEnabled ? "ON" : "OFF"}`}
onPress={() => setPerformanceEnabled(!performanceEnabled)}
/>
<Button title="Start Heavy Animation" onPress={startHeavyAnimation} />
<Animated.View
style={[
{
width: 100,
height: 100,
backgroundColor: "lightgreen",
margin: 20,
},
animatedStyle,
]}
/>
</>
);
};interface AnimatedRef<T> {
current: T | null;
(component?: T | null): void;
}
interface SerializableRef {
__reanimatedSerializableRef: true;
__value: number;
}
type NativeMethods = {
measure(callback: (x: number, y: number, width: number, height: number, pageX: number, pageY: number) => void): void;
measureInWindow(callback: (x: number, y: number, width: number, height: number) => void): void;
measureLayout(
relativeToNativeNode: number,
onSuccess: (left: number, top: number, width: number, height: number) => void,
onFail: () => void
): void;
setNativeProps(nativeProps: object): void;
focus(): void;
blur(): void;
};
enum ReanimatedLogLevel {
debug = 0,
info = 1,
warn = 2,
error = 3,
}
interface LoggerConfig {
level?: ReanimatedLogLevel;
strict?: boolean;
}setNativeProps calls togetherscrollToInstall with Tessl CLI
npx tessl i tessl/npm-react-native-reanimated