Анимация в 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.
Заключение
Компоненты высшего порядка - это мощная концепция, позволяющая сделать наш код более лаконичным и поддерживаемым за счет разделения повторяющейся части и функции компонента. Надеюсь, эта статья поможет, спасибо!