React Native 純 javascript toast實現

qiushijie發表於2019-04-03

toast只有在android平臺才可以使用,在ios平臺沒有,而一般用到的時候都希望兩個平臺能一起使用,使用動畫來封裝一個存js的,兩個平臺一起使用。

元件原始碼Toast.tsx

import * as React from 'react';
import {Animated, Dimensions, StyleSheet, Text, TextStyle, View, ViewStyle} from 'react-native';

const {width, height} = Dimensions.get('window');

interface IProps {
  textStyle?: TextStyle;
  containerStyle?: ViewStyle;
  position?: 'top' | 'center' | 'bottom';
  opacity?: number;
  fadeInDuration?: number;
  fadeOutDuration?: number;
  defaultCloseDelay?: number;
  positionValue?: number;
}

interface IState {
  visible: boolean;
  text: string;
}

class Toast extends React.Component<IProps, IState> {
  visible: boolean;
  timer: any;
  constructor(props: IProps) {
    super(props);
    this.visible = false;
  }

  static DURATION = {
    LENGTH_SHORT: 500,
    FOREVER: 0
  }
  public static defaultProps: Partial<IProps> = {
    position: 'center',
    opacity: 0.8,
    fadeInDuration: 500,
    fadeOutDuration: 500,
    defaultCloseDelay: 250,
    positionValue: 20,
    textStyle: {
      color: 'white'
    }
  }
  state = {
    visible: false,
    opacityValue: new Animated.Value(this.props.opacity!),
    text: ''
  }
  show(text: string, duration?: number) {
    this.setState({
      text,
      visible: true
    });
    Animated.timing(this.state.opacityValue, {
      toValue: this.props.opacity!,
      duration: duration || this.props.fadeInDuration
    }).start(() => {
      this.visible = true;
      if(duration != Toast.DURATION.FOREVER) this.close();
    });
  }
  close(duration: number = this.props.defaultCloseDelay!) {
    if (! this.visible && ! this.state.visible) return;
    if (this.timer != undefined) {
      clearTimeout(this.timer);
    }
    this.timer = setTimeout(() => {
      Animated.timing(this.state.opacityValue, {
        toValue: 0.0,
        duration: this.props.fadeOutDuration
      }).start(() => {
        this.setState({
          visible: false
        });
        this.visible = false;
      });
    }, duration);
  }
  componentWillUnmount() {
    if (this.timer != undefined) {
      clearTimeout(this.timer);
    }
  }
  render() {
    let pos = 0;
    switch (this.props.position) {
      case 'top':
        pos = this.props.positionValue!;
        break;
      case 'center':
        pos = height / 2;
        break;
      case 'bottom':
        pos = height - this.props.positionValue!;
        break;
    }
    if (this.state.visible) {
      return (
        <View style={[styles.container, {top: pos}]} pointerEvents="none">
          <Animated.View style={[styles.content, { opacity: this.state.opacityValue }, this.props.containerStyle]}>
            {React.isValidElement(this.state.text)
              ? this.state.text
              : <Text style={this.props.textStyle}>{this.state.text}</Text>
            }
          </Animated.View>
        </View>
      );
    }
    return null;
  }
}

const styles = StyleSheet.create({
  container: {
    position: 'absolute',
    left: 0,
    right: 0,
    elevation: 999,
    alignItems: 'center',
    zIndex: 10000,
  },
  content: {
    backgroundColor: 'black',
    borderRadius: 5,
    padding: 10,
  }
});

export default Toast;
複製程式碼

使用

private toast: Toast | null = null;

<Toast ref={ref => this.toast = ref} />

this.toast!.show('HelloWorld!');
// 長時間顯示
this.toast!.show('HelloWorld', 2000);
複製程式碼

相關文章