react native通过Animate封装兼容android安卓和ios苹果的Toast提示框

通过添加根节点的形式实现的toast,几乎满足整个项目的需求场景。

需要用到插件

import RootSiblings from 'react-native-root-siblings';

react-native-root-siblings地址

在控制台下载

npm install react-native-root-siblings --save

下载完成之后就可以使用了。

接下来添加copy代码作为一个组件放在项目的工具文件夹里面

import React from 'react';
import {
  Text,
  View,
  StyleSheet,
  Dimensions
} from 'react-native';
const { width } = Dimensions.get('window')
import RootSiblings from 'react-native-root-siblings';
const siblings_ = new RootSiblings()

let setTime = null;
// Toast公用版本 (添加根节点)
class Toast {
  static show(massege, duration = 3000, callback) {
    siblings_.update(
      <View style={styles.container}>
        <Text style={styles.text}>
          {massege}
        </Text>
      </View>
    );

    let tempDuration = 3000  // 执行时间
    if (typeof duration === 'number') {
      tempDuration = duration
    }

    setTime !== null ? clearTimeout(setTime) : null
    setTime = setTimeout(() => {
      callback && callback()  // 回调
      if (duration && typeof duration === 'function') {
        duration && duration()
      }
      siblings_.destroy();
      clearTimeout(setTime)
    }, tempDuration);
  }
}

const styles = StyleSheet.create({
  container: {
    position: 'absolute',
    width: width / 2,
    left: (width / 2) / 2,
    bottom: 80,
    padding: 15,
    backgroundColor: '#666',
    borderRadius: 5,
    flexDirection: 'column',
    justifyContent: 'center',
    alignItems: 'center'
  },
  text: {
    color: '#fff'
  }
})

export default Toast

在APP.js里面注册成为全局使用

APP.js

import Toast from '@/utils/Toast.js';


global.Toast =Toast    // 全局Toast弹框

以下的是modal版本

业务需要专门看了react native的Animate动画做出一个Toast提示框
当前的toast是使用modal弹出框,作为最高层级。在日常业务中不能完整全部场景。

//  Toast组件
import React from 'react';
import { Animated, Text, View,Easing,StyleSheet,Dimensions } from 'react-native';
const {width} = Dimensions.get('window')


export default class ToastView extends React.Component {
    state = {
        fadeAnim: new Animated.Value(0),  // 透明度初始值设为0
    }


    //  执行动画
    shwoToast = (duration = 3000) => {
        Animated.sequence([
            Animated.timing(                  // 随时间变化而执行动画
                this.state.fadeAnim,            // 动画中的变量值
                {
                    easing:Easing.linear,
                    toValue: 1,                   // 透明度最终变为1,即完全不透明
                    duration: duration,              // 让动画持续一段时间
                }
            ),
        ]).start();                      // 开始执行动画
    }

    render() {  
        let { fadeAnim } = this.state;
        let {showToast,text,duration} = this.props
        if (showToast) {  //  布尔值判断是否显示Toast
            this.state.fadeAnim.setValue(0) // 必须要每次执行都变更一次为0,才能使得动画从0隐藏到1显示
            this.shwoToast(duration)  // 执行动画 
        }
        
        const opacity = fadeAnim.interpolate({
            inputRange: [0, 0.5,1],    //  显示
            outputRange: [0, 5,0]      //  隐藏
        });
        return (
            <Animated.View                 // 使用专门的可动画化的View组件
                style={{
                    ...styles.container,
                    opacity: opacity,         // 将透明度指定为动画变量值
                }}
            >
                <Text style={styles.text}>{text}</Text>
            </Animated.View>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        // flex:1,
        position:'absolute',
        bottom:80,
        backgroundColor:'#ddd',
        borderRadius:10,
        height:'auto',
        padding:15,
        width:140,
        left:(width/2)-70
    },
    text: {
        textAlign: 'center'
    }
})

使用方法

import React, { Component } from 'react';
import { StyleSheet, Text, View, ToastAndroid, Animated, FlatList, Dimensions } from 'react-native';
import styles from './Style'
import ToastView from '../../../components/toast/Index'  // 引用

// 首页
export default class Index extends Component {
  constructor(props) {
    super(props);
    this.state = {
      showToast: false
    }
  }


  showtoast = () => {
    this.setState({
      showToast:true
    })
  }
  
  // ToastView 组件可以传三个值 showToast:是否显示(布尔值)   text:提示的内容(字符串)  duration:执行时间(毫秒)
  render() {
    let { list, showToast } = this.state
    return (
      <View style={styles.container}>
        <ToastView 
          showToast={showToast} 
          text="显示内容"
          duration={2000} />
          <Text onPress={() => this.showtoast()}>点击显示Toast框</Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
    },
    flatList: {
        flexDirection:'row'
    }
});

效果


效果

可以全局使用也可以单独引入

新建一个Toast文件

// Toast
import React from 'react';
import { Animated, Text, View, Easing, Dimensions, Modal,StyleSheet } from 'react-native';
const widthScreen = Dimensions.get('window').width


export default class Toast extends React.Component {
    state = {
        fadeAnim: new Animated.Value(0),  // 透明度初始值设为0
        text: '',  // 显示文本
        refToast: false,  // 是否通过ref绑定
        timer: '',  // 定时器
        callback:null,  // 回调  
    }


    //  执行动画
    shwoToast = (duration = 2000) => {
        Animated.sequence([
            Animated.timing(                  // 随时间变化而执行动画
                this.state.fadeAnim,            // 动画中的变量值
                {
                    easing: Easing.linear,
                    toValue: 1,                   // 透明度最终变为1,即完全不透明
                    duration: duration,              // 让动画持续一段时间
                }
            ),
        ]).start();                      // 开始执行动画
    }

    show = (text, duration,callback) => {
        this.setTiner(duration)
        this.setState({ text, refToast: true })
        this.state.fadeAnim.setValue(0) // 必须要每次执行都变更一次为0,才能使得动画从0隐藏到1显示
        this.shwoToast()  // 执行动画 
        callback&&callback()  // 回调
    }

    setTiner = () => {
        this.state.timer = setTimeout(() => {
            this.setState({ refToast: false })
            clearTimeout(this.state.timer)
        }, 2000);
    }



    render() {
        let { fadeAnim, refToast } = this.state
        let { showToast, duration } = this.props
        let { width, left } = styles.container
        let text = refToast ? this.state.text : this.props.text

        if (!refToast && showToast) {  //  布尔值判断是否显示Toast
            this.state.fadeAnim.setValue(0) // 必须要每次执行都变更一次为0,才能使得动画从0隐藏到1显示
            this.show(text, duration)  // 执行动画 
            showToast = false  // 执行之后要变为false,不在执行
        }

        //  检查显示文字内容过多宽度变大
        if (text && text.length > 14) {
            width = 200
            left = (widthScreen / 2) - 100
        } else {
            width = 140
            left = (widthScreen / 2) - 70
        }


        const opacity = fadeAnim.interpolate({
            inputRange: [0, 0.5, 1],    //  显示
            outputRange: [0, 5, 0]      //  隐藏
        });

        return (
            <View>
                <Modal
                    animationType="none"
                    transparent={refToast}
                    visible={refToast}
                >
                    <Animated.View                 // 使用专门的可动画化的View组件
                        style={{
                            ...styles.container,
                            opacity: opacity,         // 将透明度指定为动画变量值
                            width,
                            left
                        }}
                    >
                        <View style={styles.bodyView}>
                            <Text style={styles.bodyText}>{text}</Text>
                        </View>
                    </Animated.View>
                </Modal>
            </View>
        );
    }
}


const styles = StyleSheet.create({
    container: {
        position: 'absolute',
        bottom: 80,
        backgroundColor: '#ddd',
        borderRadius: 10,
        height: 'auto',
        padding: 15,
        width: 140,
        left: (widthScreen / 2) - 70,
        zIndex: 9991,
    },
    bodyView: {},
    bodyText: {
        textAlign: 'center'
    }
})

接下来我们需要在app.js中引用并且注册组件

1、我们需要引入toast文件

// app.js
import ToastView from './src/components/toast/Index';

2、在app.js组件中注册

// app.js
render() {
    return (
      <Provider store={store}>
        <View style={styles.app}>
          <ToastView ref="toast"  />
          <StackNavigator />
        </View>
      </Provider>
    );
  }

3、需要定义一个全局变量,用来全局使用

// app.js
global.Toast = ''   // 全局Toast弹框

4、在节点渲染完成之后需要获取refs的绑定

app.js
componentDidMount() {
    Toast = this.refs.toast  // 绑定Toast节点
}

5、完整代码,我这里包含了redux和路由

// app.js
import React, { Component } from 'react';
import { Platform, StyleSheet, Text, View, BackHandler, ToastAndroid, Dimensions } from 'react-native';
import StackNavigator from './src/router/StackNavigator';
import { Provider } from 'react-redux';
import store from './src/redux/store';
import ToastView from './src/components/toast/Index';  // 引入Toast文件
const screenWidth = Dimensions.get('window').width;
import PreventDoublePress from './src/utils/PreventDoublePress'

// 禁止警告
console.disableYellowBox = true;
console.warn('YellowBox is disabled.');
// 打包之后清除所有console警告
if (!__DEV__) {
  global.console = {
    info: () => { },
    log: () => { },
    warn: () => { },
    debug: () => { },
    error: () => { }
  };
}



// 全局
global.loginToken = ''  // 登录token
global.Toast = ''   // 全局Toast弹框

export default class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      lastBackPressed: 0
    }
  }

  //  页面创建的时候对物理按钮进行监听
  componentWillMount() {
    if (Platform.OS === 'android') {
      BackHandler.addEventListener('hardwareBackPress', this.BackHandler);
    }
  }
  //  页面摧毁的时候销毁监听
  componentWillUnmount() {
    if (Platform.OS === 'android') {
      BackHandler.removeEventListener('hardwareBackPress', this.BackHandler);
    }
  }

  componentDidMount() {
    Toast = this.refs.toast  // 绑定Toast节点
  }

  //  物理返回键 事件触发
  BackHandler = () => {
    if (this.state.lastBackPressed && this.state.lastBackPressed + 2000 >= Date.now()) {
      BackHandler.exitApp()
      return false
    }
    this.state.lastBackPressed = Date.now()
    ToastAndroid.show('再按一次退出应用', ToastAndroid.SHORT)
    return true
  }


  render() {
    return (
      <Provider store={store}>
        <View style={styles.app}>
          <ToastView ref="toast"  />
          <StackNavigator />
        </View>
      </Provider>
    );
  }
}

const styles = StyleSheet.create({
  app: {
    flex: 1,
    flexDirection: 'row'
  },
  toast: {
    position: 'absolute',
    bottom: 50,
    left: screenWidth / 2,
    backgroundColor: '#aaa',
    width: 100,
    height: 'auto',
    zIndex: 999
  },
  toastText: {
    // color:'#000'
  }
});



使用方法:在任意一个业务页面中 直接 Toast.show(显示内容)

import React, { Component } from 'react';
import { Text, View } from 'react-native';
import { getDate } from '../../../redux/actions'
import { connect } from 'react-redux';
import styles from './Style'


// 首页
class Index extends Component {

  showtoast = () => {
    Toast.show(`Toast显示内容`)
  }


  render() {
    return (
      <View style={styles.container}>
        <Text onPress={() => this.showtoast()}>点击显示Toast框</Text>
      </View>
    );
  }
}



const mapStateToProps = state => ({
  store: state.store
})

export default connect(mapStateToProps)(Index);

Toast.show() 里面可以提交有三个参数
参数1:显示的文本
参数2: 显示时长 默认2000毫秒
参数3:回调函数
使用方法:

Toast.show('Toast内容',2000,() => {
    // 做些什么
})

最终效果

toast.gif

也可以在当前页面单独引用

import React, { Component } from 'react';
import { Text, View } from 'react-native';
import { getDate } from '../../../redux/actions'
import { connect } from 'react-redux';
import styles from './Style'
import Toast from '../../../components/toast/Index'


// 首页
class Index extends Component {
  constructor(props) {
    super(props);
    this.state = {
      text:'',
      show:false
    }
  }


  showtoast = () => {
    this.setState({
      text:'Toast显示内容',
      show:true
    })
  }


  render() {
    return (
      <View style={styles.container}>
        <Toast
          text={this.state.text}
          show={this.state.show}
        />
        <Text onPress={() => this.showtoast()}>点击显示Toast框</Text>
      </View>
    );
  }
}



const mapStateToProps = state => ({
  store: state.store
})

export default connect(mapStateToProps)(Index);

Toast弹框需要显示图标、图标、背景颜色等等,只需要在Toast文件中自定义该样式就可以了。

<View style={styles.bodyView}>
     <Text style={styles.bodyText}>{text}</Text>
</View>

这个是弹框样式内容,自定义修改就可以了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342