LoginSignup
1
1

More than 3 years have passed since last update.

Expo/ReactNativeで特定のコンポーネントを表示する時に画面の明るさを変更する

Last updated at Posted at 2020-01-03

Expo/ReactNativeで画面の明るさを変更するためのexpo-brightnessを使う機会があったので、コンポーネント化してみました。QRコードを表示する時に画面を明るくするなどの場面に使えそうです。

expo-brightnessのドキュメントはこちら

Androidではシステム設定の明るさとアプリ単位での明るさと2種類あり、ここではパーミッションが必要ない後者を使います。
iOSではアプリ単位での明るさ設定が使えないので、アプリがforegroundかbackgroundかを監視して、backgroundになった時に明るさを元に戻すようにしました。

また、明るさを変更した時にAndroidではデフォルトでアニメーションされるのに対してiOSではアニメーションされずパッと切り替わるので、durationIOSを渡すとiOSでもアニメーションされるようにしてみました。

BrightnessControl.js
import React, { PureComponent } from "react";
import * as PropTypes from "prop-types";
import * as Brightness from "expo-brightness";
import { AppState, Animated, Easing, Platform } from "react-native";

/**
 * 明るさを制御する
 */
class BrightnessControl extends PureComponent {
  appState = null;
  originalBrightness = null;
  brightness = null;

  componentDidMount() {
    this._init();
  }

  /**
   * 明るさを元に戻し、リスナーなどを解除
   */
  componentWillUnmount() {
    if (this.brightness) {
      const { durationIOS } = this.props;
      if (durationIOS && Platform.OS === "ios") {
        Animated.timing(this.brightness, {
          toValue: this.originalBrightness,
          duration: durationIOS,
          easing: Easing.in(Easing.out(Easing.ease))
        }).start(() => {
          this.brightness.removeListener(this._updateBrightness);
        });
      } else {
        this.brightness.setValue(this.originalBrightness);
        this.brightness.removeListener(this._updateBrightness);
      }
      AppState.removeEventListener("change", this._handleAppStateChange);
    }
  }

  /**
   * propsのbrightnessの変更に対応
   * @param {object} prevProps
   */
  componentDidUpdate(prevProps) {
    if (
      this.props.brightness !== prevProps.brightness &&
      (this.appState === "active" || Platform.OS === "android")
    ) {
      this.brightness.setValue(this.props.brightness);
      this._updateBrightness();
    }
  }

  /**
   * 初期処理
   * @returns {Promise<void>}
   * @private
   */
  _init = async () => {
    this.originalBrightness = await Brightness.getBrightnessAsync(); // 元の明るさを保持しておく

    // 明るさはAnimatedで変更
    this.brightness = new Animated.Value(this.originalBrightness);
    this.brightness.addListener(this._updateBrightness);

    if (Platform.OS === "ios") {
      // iOSの場合はAppStateでbackground/foregroundを監視
      this._handleAppStateChange(AppState.currentState);
      AppState.addEventListener("change", this._handleAppStateChange);
    } else {
      const { brightness } = this.props;
      this.brightness.setValue(brightness);
    }
  };

  /**
   * 明るさを更新
   * @private
   */
  _updateBrightness = () => {
    Brightness.setBrightnessAsync(this.brightness._value);
  };

  /**
   * AppStateの変更に合わせて制御
   * @param {string} nextAppState
   * @returns {Promise<void>}
   * @private
   */
  _handleAppStateChange = async nextAppState => {
    if (nextAppState !== this.appState) {
      this.appState = nextAppState;
      if (this.appState === "active") {
        this.originalBrightness = await Brightness.getBrightnessAsync();
      }
      const { brightness, durationIOS } = this.props;
      if (durationIOS) {
        // アニメーションさせる場合
        Animated.timing(this.brightness, {
          toValue:
            this.appState === "active" ? brightness : this.originalBrightness,
          duration: durationIOS,
          easing: Easing.in(Easing.out(Easing.ease))
        }).start();
      } else {
        // アニメーションさせない場合
        this.brightness.setValue(
          this.appState === "active" ? brightness : this.originalBrightness
        );
      }
    }
  };

  render() {
    const { children } = this.props;
    return <>{children}</>;
  }
}

BrightnessControl.defaultProps = {
  brightness: 1,
  durationIOS: 0
};

BrightnessControl.propTypes = {
  brightness: PropTypes.number, // 明るさ(0~1)
  durationIOS: PropTypes.number // アニメーションの長さ(ms)
};

export default BrightnessControl;

下記のような感じで使うと、BrightnessControlコンポーネントの表示・非表示によって明るさが変化するようになります。

使用例
<BrightnessControl brightness={1} durationIOS={500}>
  {...}
</BrightnessControl>
1
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
1
1