Animações performáticas no React Native com Reanimated 3
1. Introdução ao Reanimated 3 e o Paradigma das Animações no React Native
Animações performáticas são cruciais para a experiência do usuário em aplicativos mobile. Elas fornecem feedback visual imediato, orientam a navegação e criam uma sensação de fluidez que diferencia apps medianos de experiências premium. No React Native, o sistema de animações nativo enfrenta uma limitação fundamental: a comunicação entre a thread JavaScript (JS) e a thread da UI ocorre através da bridge, que é assíncrona e pode causar atrasos visíveis (jank). Cada quadro de animação precisa ser serializado, enviado pela bridge e processado, resultando em quedas de FPS em animações complexas.
O Reanimated 3 surge como uma solução revolucionária para esse problema. Ele permite que as animações sejam executadas diretamente na thread da UI, eliminando a necessidade de comunicação constante com a thread JS. O conceito central é o worklet — funções JavaScript que são compiladas e executadas nativamente no thread da UI. Isso reduz drasticamente o jank e permite animações a 60 ou até 120 FPS, mesmo em dispositivos de baixo custo. O Reanimated 3 abstrai toda essa complexidade, oferecendo uma API intuitiva para criar animações declarativas e reativas.
2. Instalação, Configuração e Primeiros Passos com Reanimated 3
Para começar, instale o pacote e configure o plugin do Babel:
npm install react-native-reanimated
# ou
yarn add react-native-reanimated
Em seguida, adicione o plugin no arquivo babel.config.js:
module.exports = {
presets: ['module:@react-native/babel-preset'],
plugins: ['react-native-reanimated/plugin'],
};
A estrutura básica do Reanimated 3 utiliza três elementos principais: useSharedValue, useAnimatedStyle e Animated.View. Um valor compartilhado (useSharedValue) mantém seu estado na thread da UI e pode ser lido/escrito de qualquer worklet. Ao contrário do useState, as alterações em useSharedValue não disparam re-renderizações no componente React — elas atualizam diretamente as propriedades animadas.
Exemplo básico de animação de opacidade:
import Animated, { useSharedValue, useAnimatedStyle, withTiming } from 'react-native-reanimated';
function FadeInView({ children }) {
const opacity = useSharedValue(0);
const animatedStyle = useAnimatedStyle(() => ({
opacity: opacity.value,
}));
React.useEffect(() => {
opacity.value = withTiming(1, { duration: 1000 });
}, []);
return <Animated.View style={animatedStyle}>{children}</Animated.View>;
}
3. Trabalhando com Worklets e a Thread da UI
Worklets são funções marcadas com 'worklet' que rodam no thread da UI. Eles têm acesso a valores compartilhados, mas não podem acessar diretamente variáveis do escopo JavaScript — exceto se forem passadas como argumentos ou capturadas via closures especiais. Para comunicação do worklet para a thread JS, usa-se runOnJS.
Exemplo de um worklet que atualiza um estado React:
import { runOnJS } from 'react-native-reanimated';
function GestureHandler() {
const [gestureState, setGestureState] = React.useState('idle');
const translateX = useSharedValue(0);
const updateGestureState = (state) => {
setGestureState(state);
};
const onGestureEvent = useAnimatedGestureHandler({
onStart: () => {
runOnJS(updateGestureState)('started');
},
onActive: (event) => {
translateX.value = event.translationX;
},
onEnd: () => {
runOnJS(updateGestureState)('ended');
translateX.value = withSpring(0);
},
});
// ...
}
4. Animações Baseadas em Gestos com Gesture Handler
A integração entre react-native-gesture-handler e Reanimated 3 é nativa e poderosa. O hook useAnimatedGestureHandler permite processar eventos de gesto diretamente na thread da UI.
Exemplo de um elemento arrastável com snap de retorno:
import { GestureDetector, Gesture } from 'react-native-gesture-handler';
function DraggableBox() {
const offset = useSharedValue({ x: 0, y: 0 });
const start = useSharedValue({ x: 0, y: 0 });
const gesture = Gesture.Pan()
.onStart(() => {
start.value = { x: offset.value.x, y: offset.value.y };
})
.onUpdate((event) => {
offset.value = {
x: start.value.x + event.translationX,
y: start.value.y + event.translationY,
};
})
.onEnd(() => {
offset.value = withSpring({ x: 0, y: 0 }); // snap de volta
});
const animatedStyle = useAnimatedStyle(() => ({
transform: [
{ translateX: offset.value.x },
{ translateY: offset.value.y },
],
}));
return (
<GestureDetector gesture={gesture}>
<Animated.View style={[styles.box, animatedStyle]} />
</GestureDetector>
);
}
5. Animações com Layout e Transições (Layout Animations)
O Reanimated 3 oferece funções de animação como withTiming, withSpring e withSequence. Para animações de entrada e saída, use os componentes FadeIn, SlideInLeft, BounceOut, entre outros.
Exemplo de animação de layout com withSpring:
function AnimatedCard({ expanded }) {
const height = useSharedValue(100);
React.useEffect(() => {
height.value = withSpring(expanded ? 300 : 100, { damping: 15 });
}, [expanded]);
const animatedStyle = useAnimatedStyle(() => ({
height: height.value,
}));
return <Animated.View style={[styles.card, animatedStyle]} />;
}
6. Animações Complexas e Performance: Scroll, Carrosséis e Parallax
Para animar listas longas, use Animated.FlatList e useAnimatedScrollHandler. O efeito parallax pode ser implementado sem travamentos, pois os cálculos ocorrem na thread da UI.
Exemplo de parallax em um FlatList:
function ParallaxList() {
const scrollOffset = useSharedValue(0);
const scrollHandler = useAnimatedScrollHandler({
onScroll: (event) => {
scrollOffset.value = event.contentOffset.y;
},
});
const renderItem = ({ item, index }) => {
const animatedStyle = useAnimatedStyle(() => ({
transform: [
{
translateY: scrollOffset.value * 0.3 * (index % 2 === 0 ? 1 : -1),
},
],
}));
return (
<Animated.View style={[styles.item, animatedStyle]}>
<Text>{item.title}</Text>
</Animated.View>
);
};
return (
<Animated.FlatList
data={data}
renderItem={renderItem}
onScroll={scrollHandler}
scrollEventThrottle={16}
/>
);
}
7. Otimizações Avançadas: Memoização, Reutilização e Debug
Para evitar re-renderizações, use useDerivedValue para derivar valores de outros valores compartilhados. Combine com useMemo para estilos animados em componentes filhos.
Exemplo de otimização:
function OptimizedChild({ sharedValue }) {
const derivedValue = useDerivedValue(() => {
return sharedValue.value * 2;
});
const animatedStyle = useAnimatedStyle(() => ({
opacity: derivedValue.value,
}));
return <Animated.View style={animatedStyle} />;
}
Para debug, utilize o Flipper com o plugin Reanimated DevTools para inspecionar valores compartilhados e métricas de FPS em tempo real.
8. Casos de Uso Reais e Boas Práticas para Produção
Exemplo completo: animação de like com feedback tátil:
function LikeButton({ onLike }) {
const scale = useSharedValue(1);
const isLiked = useSharedValue(false);
const handlePress = () => {
scale.value = withSequence(
withSpring(1.3, { damping: 2 }),
withSpring(1)
);
isLiked.value = !isLiked.value;
runOnJS(onLike)();
};
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ scale: scale.value }],
}));
return (
<GestureDetector gesture={Gesture.Tap().onEnd(handlePress)}>
<Animated.View style={animatedStyle}>
<Icon name={isLiked.value ? 'heart' : 'heart-outline'} size={32} />
</Animated.View>
</GestureDetector>
);
}
Boas práticas:
- Evite animações na thread JS; mova tudo para worklets.
- Prefira withSpring para gestos, pois oferece resposta mais natural.
- Teste em dispositivos de baixo custo para garantir 60 FPS.
- Use useDerivedValue para evitar cálculos redundantes.
Referências
- Documentação oficial do Reanimated 3 — Guia completo com API, worklets e exemplos práticos.
- Tutorial: Animações com Gesture Handler e Reanimated — Integração detalhada entre gestos e animações performáticas.
- Artigo: Otimizando animações no React Native — Estratégias nativas vs. Reanimated para performance máxima.
- Blog da Software Mansion: Reanimated 3 Novidades — Post técnico sobre as melhorias de performance e nova arquitetura.
- Tutorial: Layout Animations no Reanimated — Exemplos de animações de entrada, saída e layout com Reanimated 3.
- Guia de Debug com Flipper e Reanimated — Como usar o Flipper para monitorar FPS e animações em tempo real.
- Curso: Animações Avançadas com Reanimated 3 — Curso prático com projetos reais e otimizações para produção.