《React-Native系列》5、RN实现弹出选择界面与动画效果

今天做了个弹出框,见文章最后图片所示,弹出的时候从屏幕下方弹入,用户选择后又从下方弹出,同时有遮罩效果。

首先,我们是怎么实现弹出框,我这边用的是absolute 绝对定位,包括弹层的覆盖,Dialog的绝对位置。

Dialog的布局在这儿就不说了,flex-box、盒子模型...

那么再来说说动画。

参考:RN动画入门Animated


1、创建一个Animated.Value

          
          

          
         

我这里创建了2个,一个是遮罩效果,一个是dialog。


2、Dialog样式编写

 此处略,见后面完整代码。

3、动画效果

首先是显示Dialog的动画:

  //显示动画
  in() {
    Animated.parallel([
      Animated.timing(
        this.state.opacity,
        {
          easing: Easing.linear,
          duration: 500,
          toValue: 0.8,
        }
      ),
      Animated.timing(
        this.state.offset,
        {
          easing: Easing.linear,
          duration: 500,
          toValue: 1,
        }
      )
    ]).start();
  }

动画还可以被更复杂地组合,通过一些辅助函数例如 sequence 或者 parallel (它们分别用于先后执行多个动画和同时执行多个动画)

由于我里设计成了2组动画,所以使用了parallel让他们同时执行。

duration:执行时间

easing:Easing的枚举值如下

  spring  //弹跳
  linear  //线性
  easeInEaseOut  //缓入缓出
  easeIn  //缓入
  easeOut  //缓出
  keyboard  //键入

介绍下 interpolate ,官网上说明如下:

interpolate(config: InterpolationConfigType) 

在更新属性之前对值进行插值。譬如:把0-1映射到0-10。


在这儿我们在translateY 输入从0到1,高度从height 到 (height-aHeight -34),只是简单的让Dialog在Y方式上移动。


明白了显示动画的原理,动画消失就更简单了。

原理:遮罩层由 0.8的透明度变味 0(不显示),高度再反过来就行(从哪里来,回哪里去) ,代码见下面。


最终实现的效果如下:



完整代码如下:(这里把Dialog封装成了组件,需要使用的话, 在你的页面直接导入,然后通过ref 的方式调用show方法即可)

当然你也可以根据你的需求,修改样式。这里只是说明了一种实现方案。

'use strict';
import React, { Component } from 'react';
import {
  StyleSheet,
  View,
  Image,
  Text,
  TouchableHighlight,
  Animated,
  Easing,
  Dimensions,
} from 'react-native';

import TimerMixin from 'react-timer-mixin';

const {width, height} = Dimensions.get('window');
const navigatorH = 64; // navigator height
const [aWidth, aHeight] = [300, 214];
const [left, top] = [0, 0];
const [middleLeft, middleTop] = [(width - aWidth) / 2, (height - aHeight) / 2 - navigatorH];

const styles = StyleSheet.create({
  container: {
    position:"absolute",
    width:width,
    height:height,
    left:left,
    top:top,
  },
  mask: {
    justifyContent:"center",
    backgroundColor:"#383838",
    opacity:0.8,
    position:"absolute",
    width:width,
    height:height,
    left:left,
    top:top,
  },
  tip: {
    width:aWidth,
    height:aHeight,
    left:middleLeft,
    backgroundColor:"#fff",
    alignItems:"center",
    justifyContent:"space-between",
  },
  tipTitleView: {
    height:55,
    flexDirection:'row',
    alignItems:'center',
    justifyContent:'center',
  },
  tipTitleText:{
    color:"#999999",
    fontSize:14,
  },
  tipContentView: {
    width:aWidth,
    borderTopWidth:0.5,
    borderColor:"#f0f0f0",
    height:45,
    flexDirection:'row',
    alignItems:'center',
    justifyContent:'center',
  },
  tipText:{
    color:"#e6454a",
    fontSize:17,
    textAlign:"center",
  },
  button: {
    height: 45,
    backgroundColor: '#fff',
    //borderColor: '#e6454a',
    //borderWidth: 1,
    //borderRadius: 4,
    alignSelf: 'stretch',
    justifyContent: 'center',
    //marginLeft: 10,
    //marginRight: 10,
  },
  buttonText: {
    fontSize:17,
    color:"#e6454a",
    textAlign:"center",
  },
  gap:{
    height:10,
    width:aWidth,
    backgroundColor:'#383838',
    opacity:0.8,
  },
});

//console.log('======');

export default class Alert extends Component {
  mixins = [TimerMixin];
  parent ={};

  constructor(props) {
    super(props);
    this.state = {
      offset: new Animated.Value(0),
      opacity: new Animated.Value(0),
      title: "",
      choose1: "",
      choose2: "",
      hide: true,
    };
  }

  render() {
    if(this.state.hide){
      return ()
    } else {
      return (
        
          
          

          
            
              {this.state.title}
            
            
              {this.state.choose1}
            

            
              {this.state.choose2}
            

            

            
              取消
            
          
        
      );
    }
  }

  componentDidMount() {
  }

  //显示动画
  in() {
    Animated.parallel([
      Animated.timing(
        this.state.opacity,
        {
          easing: Easing.linear,
          duration: 500,
          toValue: 0.8,
        }
      ),
      Animated.timing(
        this.state.offset,
        {
          easing: Easing.linear,
          duration: 500,
          toValue: 1,
        }
      )
    ]).start();
  }

  //隐藏动画
  out(){
    Animated.parallel([
      Animated.timing(
        this.state.opacity,
        {
          easing: Easing.linear,
          duration: 500,
          toValue: 0,
        }
      ),
      Animated.timing(
        this.state.offset,
        {
          easing: Easing.linear,
          duration: 500,
          toValue: 0,
        }
      )
    ]).start();

    setTimeout(
      () => this.setState({hide: true}),
      500
    );
  }

  //取消
  iknow(event) {
    if(!this.state.hide){
      this.out();
    }
  }

  //选择
  choose(msg) {
    //console.log(msg);
    if(!this.state.hide){
      this.out();
      this.parent.setState({sex:msg});
    }
  }

  show(title: string, choose1:string,choose2:string ,obj:Object) {
    this.parent = obj;
    if(this.state.hide){
      this.setState({title: title, choose1: choose1, choose2: choose2, hide: false}, this.in);
    }
  }
}



你可能感兴趣的:(ReactNative,ReactNative实战系列)