1.实现的效果
- 点击按钮,出现弹框,最好有动画;
- 点击弹框周围,弹框消失;
- 点击Android返回键,弹框消失;
- 有“取消”和“确定”按钮,回调函数,巴拉巴拉巴。。。。
2.实现思路
- 弹框外层组件使用Modal,免得监听安卓返回键;
- 动画使用Animated;
- 最外层(绝对定位)存在一个宽高等于手机屏幕的activeOpacity=1的TouchableOpacity,点击能够使Modal消失;
3.源码
Popover.js
/**
* 弹框
* @flow
*/
import React, { Component } from 'react';
import {
Platform,
StyleSheet,
Text,
View,
BackHandler,
Modal,
Button,
TouchableOpacity,
Animated,
Image,
StatusBar
} from 'react-native';
import PropTypes from 'prop-types';
import theme from '../utils/theme';
const {screenWidth, screenHeight, segment} = theme;
export default class Popover extends Component {
constructor(props){
super(props);
this.state = {
show: false,
pageX: 0,
pageY: 0,
height: 0,
scaleAnimationValue: new Animated.Value(0)
}
}
// 打开弹框
showModal() {
this.setState({
show: true,
});
if(this.props.useAnimation){
Animated.spring(this.state.scaleAnimationValue, {
toValue: 1,
duration: 200,
}).start();
}
}
// 关闭弹框
onRequestClose() {
if(this.props.useAnimation){
this.state.scaleAnimationValue.setValue(0);
}
this.setState({
show: false
});
}
render() {
const {hasCancelBtn, cancelCallback, title, content, ensureCallback, useAnimation, takePicsCallback, albumCallback, wordsCallback, hasWordBtn} = this.props;
return (
<View style={styles.modalContainer}>
<Modal
animationType={"none"}
transparent={true}
visible={this.state.show}
onRequestClose={() => this.onRequestClose()}>
<TouchableOpacity
activeOpacity={1}
style={styles.modalContainer}
onPress={() => this.onRequestClose()}
>
<View style={styles.outerContainer} />
</TouchableOpacity>
<Animated.View style={[styles.container, {transform: [{scale: useAnimation ? this.state.scaleAnimationValue : 1}]}]}>
<View
style={styles.promptContainer}
>
{
this.props.onlyInChose ?
<View style={styles.choseContainer}>
<TouchableOpacity activeOpacity={0.75} onPress={takePicsCallback}>
<View style={styles.choseItem}>
<Image source={require('../img/camera_small.png')} style={{width: theme.scaleSize(50),height: theme.scaleSize(40), resizeMode:'contain'}} />
<Text style={{color: '#343434',fontSize: theme.setSpText(32)}}>拍摄照片</Text>
<View style={{width: theme.scaleSize(48),height: theme.scaleSize(40)}} />
</View>
</TouchableOpacity>
<TouchableOpacity activeOpacity={0.75} onPress={albumCallback}>
<View style={[styles.choseItem, {borderBottomWidth: hasWordBtn ? segment.width : 0}]}>
<Image source={require('../img/page_small.png')} style={{width: theme.scaleSize(50),height: theme.scaleSize(40), resizeMode:'contain'}} />
<Text style={{color: '#343434',fontSize: theme.setSpText(32)}}>相册选择</Text>
<View style={{width: theme.scaleSize(48),height: theme.scaleSize(40)}} />
</View>
</TouchableOpacity>
{
hasWordBtn &&
<TouchableOpacity activeOpacity={0.75} onPress={wordsCallback}>
<View style={[styles.choseItem, {borderBottomWidth: 0}]}>
<Image source={require('../img/Text_small.png')} style={{width: theme.scaleSize(50),height: theme.scaleSize(40), resizeMode:'contain'}} />
<Text style={{color: '#343434',fontSize: theme.setSpText(32)}}>文字说说</Text>
<View style={{width: theme.scaleSize(48),height: theme.scaleSize(40)}} />
</View>
</TouchableOpacity>
}
</View>
:
<View>
<View style={styles.titleContainer}>
<Text style={{color: '#333333',fontSize: theme.setSpText(38)}}>{title}</Text>
</View>
<View style={styles.contentContainer}>
<Text style={{color: '#343434',fontSize: theme.setSpText(34)}}>{content}</Text>
</View>
</View>
}
</View>
{
this.props.onlyInChose ?
null
:
<View style={styles.buttonContainer}>
{
hasCancelBtn &&
<TouchableOpacity
activeOpacity={0.75}
style={[styles.center, {flex: 4.5}]}
onPress={() => {
this._close();
// 取消后的事件
cancelCallback && cancelCallback()
}}
>
<Text style={{fontSize: theme.setSpText(38), color: '#666666'}}>取消</Text>
</TouchableOpacity>
}
<View style={[styles.line]}/>
<TouchableOpacity
activeOpacity={0.75}
style={[styles.center, {flex: 4.5}]}
onPress={() => {
this.onRequestClose();
// 子组件传递数据到父组件
ensureCallback && ensureCallback();
}}
>
<Text style={{fontSize: theme.setSpText(38), color: '#3382C7'}}>确定</Text>
</TouchableOpacity>
</View>
}
</Animated.View>
</Modal>
</View>
);
}
_close() {
this.onRequestClose();
}
ensureCallback() {
this.props.navigation.goBack(null);
}
}
Popover.propTypes = {
hasCancelBtn: PropTypes.bool, // 是否保留“取消”按钮
onlyInChose: PropTypes.bool, // 是否应用在发布说说时
cancelCallback: PropTypes.func, // 点击取消后的回调
ensureCallback: PropTypes.func, // 点击确定后的回调
wordsCallback: PropTypes.func, // 点击发布说说回调
albumCallback: PropTypes.func, // 选取相册回调
takePicsCallback: PropTypes.func, // 拍摄照片回调
title: PropTypes.string, // 提示标题
content: PropTypes.string, // 提示内容文字
useAnimation: PropTypes.bool, // 是否应用动画
hasWordBtn: PropTypes.bool // 是否包含说说
}
Popover.defaultProps = {
hasCancelBtn: true,
title: '未定义标题',
content: '未定义内容',
onlyInChose: false,
useAnimation: false,
hasWordBtn: true
}
const styles = StyleSheet.create({
modalContainer: {
position: 'absolute',
width: screenWidth,
zIndex: -1,
height: Platform.OS == 'android' ? screenHeight - StatusBar.currentHeight : screenHeight - 20,
},
outerContainer: {
position: 'absolute',
width: screenWidth,
height: screenHeight,
backgroundColor: 'rgba(1, 1, 1, 0.5)'
},
container: {
width: theme.scaleSize(660),
backgroundColor: '#fff',
borderRadius: 8,
justifyContent: 'center',
alignItems: 'center',
position: 'absolute',
top: theme.scaleSize(410),
left: (screenWidth - theme.scaleSize(660)) / 2,
},
promptContainer: {
width: theme.scaleSize(660),
alignItems: 'center',
},
choseContainer:{
},
choseItem:{
height: theme.scaleSize(120),
width: theme.scaleSize(660),
paddingHorizontal: theme.scaleSize(160),
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
borderBottomWidth: segment.width,
borderBottomColor: '#999999',
},
titleContainer:{
width: theme.scaleSize(660),
height: theme.scaleSize(116),
borderBottomColor: '#F2F2F2',
borderBottomWidth: segment.width,
justifyContent: 'center',
alignItems: 'center'
},
contentContainer:{
width: theme.scaleSize(660),
height: theme.scaleSize(246),
padding:theme.scaleSize(40),
// justifyContent: 'center',
alignItems: 'center'
},
buttonContainer: {
height: theme.scaleSize(100),
width: theme.scaleSize(660),
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
borderTopWidth: segment.width,
borderColor: '#999999'
},
center: {
alignItems: 'center',
justifyContent: 'center',
},
line: {
height: theme.scaleSize(100),
width: segment.width,
backgroundColor: '#999999'
}
});