Skip to content
React Native Reanimated ガイドブック
Esc
navigateopen⌘Jpreview
On this page

アクセシビリティ

「動きを減らす」設定にアニメーションを合わせるための細かなふるまいと、スクリーンリーダーなどアニメーション以外の配慮をまとめます。実際に動くデモは第3部3章にあります。

アニメーションは体験を豊かにする一方で、人によっては使いづらさの原因にもなります。動きに敏感なユーザーは、大きく揺れる画面で目まいを感じることがありますし、スクリーンリーダーを使うユーザーには、視覚的な動きそのものが情報として届きません。この章では、Reanimatedのアニメーションをアクセシビリティに配慮させるための細かなふるまいと、動き以外の配慮点を整理します。

「動きを減らす」設定への対応の基本と、実際に切り替わるデモは第3部3章で扱いました。useReducedMotionフックとReducedMotionConfigコンポーネントの使い方はそちらに任せ、この章ではもう一段細かいふるまいと、スクリーンリーダーへの配慮を補います。

端末設定とSystem、Always、Neverの指定から通常の動きと動きを減らす表示へ分岐する図

既定のSystemは端末設定に従い、AlwaysNeverはアニメーションごとに動きの扱いを固定します。

ReduceMotionの3つの値

個々のアニメーションには、reduceMotionオプションで「動きを減らす」設定への従い方を指定できます。値はReduceMotion列挙型の3つです。

  • ReduceMotion.System — デバイスの設定に従う。設定が有効ならアニメーションを無効化し、無効なら通常どおり動かす
  • ReduceMotion.Always — デバイスの設定に関わらず、常に無効化する
  • ReduceMotion.Never — 常に有効のまま動かす

既定ではすべてのアニメーションがReduceMotion.Systemで構成されています。つまり、何も指定しなければ自動的にデバイスの設定を尊重します。

import { withDelay, withTiming, ReduceMotion } from 'react-native-reanimated';

function App() {
  sv1.value = withTiming(0, { reduceMotion: ReduceMotion.System });
  sv2.value = withDelay(1000, withTiming(toValue, { duration }), ReduceMotion.System);
  // ...
}

無効化されたときのふるまい

「動きを減らす」が有効なとき、各アニメーション関数は動きを再生する代わりに、目的の状態へ即座に到達します。ただし関数ごとにふるまいが少しずつ違います。

  • withSpringwithTimingは、toValueを即座に返す
  • withDecayは、クランプの設定を考慮したうえで現在の値を即座に返す
  • withDelayは、遅延を挟まず次のアニメーションをすぐ開始する
  • withRepeatは、繰り返し回数が無限または偶数で、かつ動きが反転する構成のときは繰り返し自体を開始しない。それ以外のときは1回だけ実行する
  • withSequenceは、動きを減らす設定が無効になっているアニメーションだけを再生する

高階のアニメーションと設定の継承

withDelaywithSequenceのような高階のアニメーションは、自分のreduceMotion設定を子のアニメーションへ引き継ぎます。ただし引き継ぐのは、子がユーザーによって設定されていない場合だけです。子が自分でreduceMotionを持っていれば、そちらが優先されます。

たとえば次のアニメーションは、外側のwithDelayAlwaysを指定しているので、withTimingtoValueへ瞬時に到達します。

sv.value = withDelay(1000, withTiming(toValue, { duration }), ReduceMotion.Always);

逆にこちらはNeverなので、デバイスで「動きを減らす」が有効でも通常どおり動きます。

sv.value = withDelay(1000, withTiming(toValue, { duration }), ReduceMotion.Never);

そして次の例では、子のwithTimingが自分でNeverを持っているため、外側のAlwaysを上書きします。結果としてwithTimingは通常どおり、しかも遅延なしで実行されます。

sv.value = withDelay(
  1000,
  withTiming(toValue, { duration, reduceMotion: ReduceMotion.Never }),
  ReduceMotion.Always
);

レイアウトアニメーションでの指定

レイアウトアニメーションにも、.reduceMotion()メソッドで同じ設定を渡せます。

import { BounceIn, ReduceMotion } from 'react-native-reanimated';

function App() {
  const entering = BounceIn.reduceMotion(ReduceMotion.System);
  // ...
}

「動きを減らす」が有効なときのふるまいは次のとおりです。

  • 出現(entering)・キーフレーム・レイアウトアニメーションは、終点へ瞬時に到達する
  • 消滅(exiting)アニメーションと共有要素トランジションは省略される

useReducedMotionと組み合わせれば、設定に応じてアニメーション自体を差し替えることもできます。派手なBounceInの代わりに控えめなFadeInを使う、といった切り替えです。

import { BounceIn, FadeIn, ReduceMotion } from 'react-native-reanimated';

function App() {
  const reduceMotion = useReducedMotion();
  const entering = reduceMotion ? FadeIn.reduceMotion(ReduceMotion.Never) : BounceIn;
  // ...
}

スクリーンリーダーへの配慮

「動きを減らす」への対応は、アクセシビリティの一部でしかありません。スクリーンリーダーを使うユーザーには、そもそも視覚的な動きが情報として届かない、という前提を忘れないようにしたいところです。

アニメーションで状態の変化を伝えているなら、その情報が動き以外の手段でも届くようにします。たとえば要素がスライドインして「新着あり」を示すなら、その要素にaccessibilityLabelで内容を持たせ、必要ならAccessibilityInfo.announceForAccessibilityで変化を読み上げます。動きだけに意味を負わせないのが基本です。

アニメーション中の中間状態が読み上げられて邪魔になる場合は、アニメーションの入れ物にaccessibilityElementsHidden(iOS)やimportantForAccessibility="no-hide-descendants"(Android)を使い、動いている最中の要素をアクセシビリティツリーから外す、という選択肢もあります。フォーカスの制御が必要なときは、React NativeのAccessibilityInfoが持つAPIを組み合わせてください。

これらはReanimated固有の機能ではなく、React Nativeのアクセシビリティ機構の話です。アニメーションを足すときには、見えている動きの裏で、見えない経路の情報が失われていないかをあわせて確認する、と覚えておくとよいと思います。

この章のもとになった公式ドキュメント