Анимация в React Native
Animated API - отличный подход в React Native для создания увлекательной анимации.
Он работает, создавая анимированное значение анимации, а затем сопоставляя анимированное значение со стилем, который вы хотите анимировать. Если вы не знакомы с Animated, ознакомьтесь с документацией.
Управлять анимацией с помощью логического значения?
Иногда мы хотим анимировать наш компонент с помощью логического значения, но Animated API не предоставляет его по умолчанию. Это немного неудобно. Например, вы хотите, чтобы на кнопке был эффект плавного появления и исчезновения. Реализация может выглядеть примерно так:
Container
(раньше)
// ... class MyContainer extends React.Component { // ... toggleBtn() { if (this.state.btnActive) { this.setState({ btnActive: false }); Animated.spring( this.state.animatedValue, { duration: 300, toValue: 0, friction: 10 } ).start(); } else { this.setState({ btnActive: true }); Animated.spring( this.state.animatedValue, { duration: 300, toValue: 1, friction: 10 } ).start(); } } render() { return ( // ... <Animated.View style={ { opacity: this.state.animateValue.interpolate({ inputRange: [0, 1], outputRange: [0, 1] }), } } > <Button title="I am a button" /> </Animated.View> // ... ); } }
Он не идеален по двум причинам.
- Есть две задачи для переключения кнопки
- Детали реализации анимации находятся в файле
Container
.
Анимированные компоненты высшего порядка
Создание animatedHOC
может решить проблему. Дополнительный слой может абстрагироваться от деталей для переключения кнопки с анимацией, так что Container
больше не заботится о том, как должна быть анимирована кнопка.
animatedHOC
обрабатывает всю логику анимации значений анимации следующим образом:
import React, { Component } from ‘react’; import { Animated, InteractionManager } from ‘react-native’; export default function animatedHOC(WrappedComponent) { return class AnimatedCtrl extends Component { constructor(props) { super(props); this.state = { active: props.active, animateValue: new Animated.Value(0) }; } componentWillReceiveProps(nextProps) { if (this.state.active === nextProps.active) return; Animated.spring( this.state.animateValue, { duration: this.props.duration, // Components which are wrapped by the HOC can decide what // animation to implement based on the animateValue // between 0 and 1. toValue: nextProps.active ? 1 : 0, friction: 10 } ).start(); this.setState({ active: nextProps.active }); } render() { return (<WrappedComponent { ...this.props } animateValue={ this.state.animateValue } />); } };
FadeAnimationBtn
получает animateValue
реквизит для реализации соответствующей анимации.
Container
(после)
import React from ‘react’; import { View, Animated, Button, Text } from ‘react-native’; import Button from 'path/to/myButton'; import animatedHOC from ‘../animatedHOC’; export const FloatBtn = ({ onPress, title, animateValue }) => ( <Animated.View style={ { opacity: animateValue.interpolate({ inputRange: [0, 1], outputRange: [0, 1] }), } } > <Button onPress={ onPress } title={ title } /> </Animated.View> ); export default animatedHOC(FloatBtn);
Обратите внимание, что FadeAnimationBtn
должен быть обернут animatedHOC
, поскольку FadeAnimationBtn
требуетanimatedValue
в качестве его свойств.
Container
// ... import FadeAnimationBtn from 'path/to/FadeAnimationBtn'; class MyContainer extends React.Component { // ... toggleBtn() { this.setState({ btnActive: !this.state.btnActive }); } render() { return ( // ... <FadeAnimationBtn active={ this.state.btnActive } title="I am a Button" > // ... ); } }
Благодаря такому подходу мы можем реализовать логическую анимацию намного чище. Container
заботится только о состоянии кнопки.
Выше только минимальная реализация. Вы можете улучшить AnimatedHOC
, добавив InteractionManager
или что угодно. Вы также можете добавить другой эффект перехода на основе animatedValue.
Заключение
Компоненты высшего порядка - это мощная концепция, позволяющая сделать наш код более лаконичным и поддерживаемым за счет разделения повторяющейся части и функции компонента. Надеюсь, эта статья поможет, спасибо!