React Native ブール値を使ったアニメーション
2018年5月6日2分で読める

React Native ブール値を使ったアニメーション

この記事はAIによって翻訳されたため、不正確な場合があります。

免責聲明: このブログ記事の翻訳はAIによって生成されました。翻訳の正確性について保証はありません。重要な情報については、元の記事をご参照ください。

React Nativeのアニメーション

Animated APIは、React Nativeで魅力的なアニメーションを作成するための素晴らしいアプローチです。

これはアニメーション値のトゥイーンを作成し、アニメーション値をアニメーションしたいスタイルにマップすることで動作します。Animatedに馴染みがない場合は、ドキュメントをご確認ください。

ブール値でアニメーションを制御する?

時々、ブール値でコンポーネントをアニメーションしたいことがありますが、Animated APIはデフォルトでそれを提供していません。少し不便です。例えば、ボタンにフェードイン、フェードアウト効果を持たせたい場合、実装は以下のようになるかもしれません:

Container (before)

// ...

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>
      // ...
    )
  }
}

これは二つの理由で理想的ではありません。

  1. ボタンを切り替えるために二つのタスクがある
  2. アニメーションの実装詳細が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,
          // HOCにラップされたコンポーネントは、animateValue
          // に基づいて実装するアニメーションを決定できます
          // 0と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 (after)

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)

FadeAnimationBtnanimatedValueをそのプロップとして必要とするため、animatedHOCによってラップされる必要があることに注意してください。

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はボタンの状態のみを処理します。

上記は最小限の実装です。InteractionManagerや必要なものを追加してAnimatedHOCを拡張できます。animatedValueに基づいて他のトランジション効果も追加できます。

結論

高階コンポーネントは、重複する部分を分離し、コンポーネントへの関心を分離することで、コードをより簡潔で保守可能にする強力な概念です。この記事が役立つことを願っています。ありがとうございます!