4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[React Native]アニメーションでちらつきが発生した時の対処法

Last updated at Posted at 2021-10-01

はじめに

今回、react-native-modalというライブラリを使ってモーダルを実装した際に、モーダルを閉じるアニメーションで背景にチラつきが発生する(下の動画参照)ことがあったため、その対応をまとめました。今回の解決策は、他のアニメーションの実装でも有用だと思うので、タイトルは広義な表現をしています。

bad.gif

追記

2021/12/13

react-native-modalでモーダルを閉じた際にチラつきが発生する問題について、後述している内容だけで治らなかった場合は、以下のプロパティを追加してください。

hideModalContentWhileAnimating={true}

解決方法

コンポーネントに対して、useNativeDriverというpropsを追加します。(useNativeDriver={true}も可)

<Modal
  isVisible={isOpen}
  onBackdropPress={onClose}
+ useNativeDriver>
 /** contents */
</Modal>

これによって、チラつきがなくなり綺麗なアニメーションになります。

good.gif

useNativeDriverって?

解決方法は至ってシンプルでしたが、そもそもuseNativeDriverがなにものか知らなかったので調査して見ました。
React NativeのUsing Native Driver for Animated という記事に以下の記述がありました。

The Animated API was designed with a very important constraint in mind, it is serializable. This means we can send everything about the animation to native before it has even started and allows native code to perform the animation on the UI thread without having to go through the bridge on every frame. It is very useful because once the animation has started, the JS thread can be blocked and the animation will still run smoothly.

なるほど、全てのフレームでJSブリッジを通過させずにネイティブコードを実行することで、アニメーションのパフォーマンスを向上させることができるみたいです。
となると、そもそもアニメーションがどう動いているかですが、それについても言及されていました。

従来のアニメーションのフロー

スクリーンショット 2021-10-01 11.10.03.png

  1. アニメーションドライバはrequestAnimationFrameを用いて全てのフレームで実行され、アニメーション曲線に基づいて計算された新しい値を使用して、アニメーションの値を更新します
  2. 中間値が計算され、Viewコンポーネントにアタッチされているpropsノードに渡します
  3. ViewsetNativePropsを用いて更新します
  4. JavaScriptからネイティブブリッジに処理が移ります
  5. UIViewもしくはandroid.Viewが更新されます

Using Native Driver for Animatedより引用

1.~ 3.の処理が JavaScriptのスレッド上で実行されていること、毎フレームでJSからネイティブブリッジに通過させる必要があることの2点が、ビューを更新する上で重い処理となっているようで、JavaScriptによる実行が他の処理などによってブロックされた場合アニメーションがそのフレームでスキップされてしまうようです。

上記の問題はこの辺りが原因なのでしょうか??

そこで、パフォーマンスを改善する方法として、JSスレッドで行われていた部分をネイティブで行う方法が考えられました。

JavaScriptを利用しないアニメーションのフロー

  1. ネイティブのアニメーションドライバはCADisplayLinkもしくはandroid.view.Choreographerを用いて全てのフレームで実行され、アニメーション曲線に基づいて計算された新しい値を使用して、アニメーションの値を更新します
  2. 中間値が計算され、ネイティブのビューにアタッチされているpropsノードに渡します
  3. UIViewもしくはandroid.Viewが更新されます

JavaScriptのスレッド上で実行する必要のある処理は一切なく、そのためJSからネイティブブリッジへの移動も無くなったため、アニメーションが高速になりました!詳しい内部処理は依然わからないところはありますが、概要はつかめたのではないかと思います。

上記のフローに変更するために必要なのが、useNativeDriverを追加することというわけです。

Animated.timing(this.state.animatedValue, {
  toValue: 1,
  duration: 500,
+ useNativeDriver: true, // <-- Add this
}).start();

対応しているアニメーションのプロパティ

記事の最後に、以下の記載がありました。

Not everything you can do with Animated is currently supported in Native Animated. The main limitation is that you can only animate non-layout properties, things like transform and opacity will work but Flexbox and position properties won't. Another one is with Animated.event, it will only work with direct events and not bubbling events. This means it does not work with PanResponder but does work with things like ScrollView#onScroll.

全てのアニメーションがネイティブアニメーション(つまりJavaScriptを用いないアニメーション)に対応しているわけではなく、transformopacityといったプロパティでは機能しますが、FlexBoxやpositionでは機能しないようです。(ブログで currently と書かれており、その時のReact Nativeの最新バージョンはv0.65(2021/10/01時))また、Animated.eventに関しては直接的なイベント(Scrollイベントなど)は機能しますが、バブリングするイベント(PanResponderイベントなど)では機能しないようです。まだ制約もあるのでuseNativeDriverのデフォルト値をfalseにしている、という感じでしょうか。

また、この機能はまだ実験段階であるようなので、React Nativeのバージョンが0.40以上で利用できるようです。React Nativeのv0.40.0のリリースが2017年1月4日とかなり前なので特に心配ないと思いますが、、:sweat_smile:

最後に

React Nativeではアニメーションのパフォーマンス改善に、JavaScriptを用いずにアニメーションを実行する方法があることがわかりました。今ではReact Nativeの標準のコンポーネントであるTouchableコンポーネント(TouchableHighlightTouchableOpacityなど)やReact Navigationというルーティングを管理するライブラリのアニメーションでも用いられているようです。

利用できる機能であればuseNativeDriverを有効にする、という考えで間違いないかと思います。そのうち全てのアニメーションで利用できるようになればいいですね:blush:

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?