美文网首页
react native通过Animate封装兼容android

react native通过Animate封装兼容android

作者: smallzip | 来源:发表于2019-06-28 18:11 被阅读0次

通过添加根节点的形式实现的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>

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

相关文章

网友评论

      本文标题:react native通过Animate封装兼容android

      本文链接:https://www.haomeiwen.com/subject/igbecctx.html