react native 彈出編輯對話方塊
阿新 • • 發佈:2019-02-17
類似於蘋果手機的彈出效果,動畫很流暢,效果如圖所示:http://www.jianshu.com/p/36ec413f7098
效果圖
分析實現過程
1.設定應用到的元件狀態
this.state = {
isShow: false,
inputText: '',
opacityAnimationValue: new Animated.Value(0),
scaleAnimationValue: new Animated.Value(0)
}
opacityAnimationValue:表示彈出框的透明度;
scaleAnimationValue:表示彈出框的縮放倍數,模擬它從小變大的動畫;
2.設定開啟對話方塊的動畫效果
// 開啟對話方塊
show() {
this.setState({
isShow: true,
inputText: this.props.inputText
});
//Animated.parallel == 同時執行多個動畫
Animated.parallel([
//Animated.timing == 推動一個值按照一個過渡曲線而隨時間變化
Animated.timing(this.state.opacityAnimationValue, {
toValue : 1,
duration: 200 + 100
}),
//Animated.spring == 產生一個基於Rebound和Origami實現的Spring動畫。它會在toValue值更新的同時跟蹤當前的速度狀態,以確保動畫連貫,比timing動畫連貫流暢
Animated.spring(this.state.scaleAnimationValue, {
toValue: 1,
duration: 200,
friction: 5
})
]).start ();
}
注意:Animated.parallel表示輔助函式,可以同時執行多個動畫;
Animated.timing和Animated.spring有一些動畫效果上的區別,其中Animated.spring動畫效果更連貫漂亮;
3.設定關閉對話方塊的方法
// 關閉對話方塊
_close() {
this.setState({isShow: false});
this.state.opacityAnimationValue.setValue(0);
this.state.scaleAnimationValue.setValue(0);
}
4.重點看一下render方法
render() {
// 如果消失,返回null 用於控制顯示隱藏
if (!this.state.isShow) return null;
// 向父元件開放兩個介面,自定義標題文字和點選確定後傳送的事件
const {ensureCallback,titleTxt} = this.props;
return (
// 最外層是一個半透明的黑色蒙版背景,點選的時候對話方塊也會消失
<Animated.View style={[styles.container, {opacity: this.state.opacityAnimationValue}]}>
<TouchableOpacity
activeOpacity={1}
style={{flex: 1, alignItems: 'center', paddingTop: 100}}
// 點選外層的黑色蒙版,對話方塊也會消失
onPress={this._close}
>
<Animated.View
style={[styles.contentContainer, {transform: [{scale: this.state.scaleAnimationValue}]}]}
>
<TouchableOpacity
activeOpacity={1}
style={styles.promptContainer}
>
<Text style={{fontSize: 15, color: 'black'}}>{titleTxt}</Text>
<View style={{flexDirection: 'row', margin: 15}}>
<View style={[styles.center, {width: 230}]}>
<TextInput
style={{fontSize: 16, color: '#999',width:150,padding:0}}
value={this.state.inputText}
autoFocus={true}
underlineColorAndroid="transparent"
onChangeText={text => this.setState({inputText:text})}
/>
</View>
<TouchableOpacity
onPress={() => this.setState({inputText: ''})}
style={[styles.center, {width: 20}]}>
<Image
source={require('../../../assets/img/close.png')}
style={{width: 18, height: 18}}
/>
</TouchableOpacity>
</View>
</TouchableOpacity>
<View style={styles.buttonContainer}>
<TouchableOpacity
activeOpacity={0.75}
style={[styles.center, {flex: 4.5}]}
onPress={this._close}
>
<Text style={{fontSize: 16, color: 'black'}}>取消</Text>
</TouchableOpacity>
<View style={[styles.line]}/>
<TouchableOpacity
activeOpacity={0.75}
style={[styles.center, {flex: 4.5}]}
onPress={() => {
this._close();
// 子元件傳遞資料到父元件
ensureCallback(this.state.inputText);
}}
>
<Text style={{fontSize: 16, color: 'black'}}>確定</Text>
</TouchableOpacity>
</View>
</Animated.View>
</TouchableOpacity>
</Animated.View>
)
}
著重說一下transform樣式的書寫,這裡用到了陣列的形式:
transform [{perspective: number}, {rotate: string}, {rotateX: string}, {rotateY: string}, {rotateZ: string}, {scale: number}, {scaleX: number}, {scaleY: number}, {translateX: number}, {translateY: number}, {skewX: string}, {skewY: string}]
5.來看樣式:
const styles = StyleSheet.create({
container: {
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
backgroundColor: 'rgba(1, 1, 1, 0.5)'
},
contentContainer: {
justifyContent: 'center',
alignItems: 'center',
borderColor: '#d9d9d9',
borderWidth: 1,
height: 150,
width: screenW * 0.75,
backgroundColor: 'rgb(234, 234, 235)',
borderRadius: 5,
},
promptContainer: {
height: 100,
width: screenW * 0.75,
alignItems: 'center',
justifyContent: 'center'
},
buttonContainer: {
height: 50,
width: screenW * 0.75,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
borderTopWidth: 1,
borderColor: '#d9d9d9'
},
line: {
height: 46,
width: 1,
backgroundColor: '#d9d9d9'
},
center: {
justifyContent: 'center',
alignItems: 'center'
}
})
5.如何引用到專案中
1.引入元件
import EditView from './EditView';
2.把元件放到合適的位置,最好是最外層容器的裡邊
<EditView
// 在元件中使用this.editView即可訪拿到EditView元件
ref={editView => this.editView = editView}
inputText={this.state.name}
titleTxt={'修改XXX'}
ensureCallback={name => this.setState({name})}
/>
3.呼叫它出現的方法
...
<TouchableOpacity onPress={()=>this.editView.show()} style={[styles.receiveViewStyle,{marginTop:20}]}>
<View style={[styles.receiveViewStyle,{backgroundColor:'red'}]}>
<Text style={styles.receiveTxtStyle}>點選編輯</Text>
</View>
</TouchableOpacity>
...
4.或者直接這樣呼叫:
<EditView
// 在元件中使用this.editView即可訪拿到EditView元件
ref="editView"
inputText={this.state.name}
titleTxt={'修改XXX'}
ensureCallback={name => this.setState({name})}
/>
...
<TouchableOpacity onPress={()=>this.refs.editView.show()} style={[styles.receiveViewStyle,{marginTop:20}]}>
<View style={[styles.receiveViewStyle,{backgroundColor:'red'}]}>
<Text style={styles.receiveTxtStyle}>點選編輯</Text>
</View>
</TouchableOpacity>
最後,貼出EditView.js的原始碼
/**
* Created by gewd on 2017/5/2.
*/
import React, {Component} from 'react';
import {
StyleSheet,
View,
Text,
TouchableOpacity,
Animated,
Platform,
TextInput,
Image,
Dimensions
} from 'react-native';
const screenW = Dimensions.get('window').width;
export default class EditView extends Component {
constructor(props) {
super(props);
this.show = this.show.bind(this);
this._close = this._close.bind(this);
this.state = {
isShow: false,
inputText: '',
opacityAnimationValue: new Animated.Value(0),
scaleAnimationValue: new Animated.Value(0)
}
}
// 開啟對話方塊
show() {
this.setState({
isShow: true,
inputText: this.props.inputText
});
//Animated.parallel == 同時執行多個動畫
Animated.parallel([
//Animated.timing == 推動一個值按照一個過渡曲線而隨時間變化
Animated.timing(this.state.opacityAnimationValue, {
toValue: 1,
duration: 200 + 100
}),
//Animated.spring == 產生一個基於Rebound和Origami實現的Spring動畫。它會在toValue值更新的同時跟蹤當前的速度狀態,以確保動畫連貫,比timing動畫連貫流暢
Animated.spring(this.state.scaleAnimationValue, {
toValue: 1,
duration: 200,
friction: 5
})
]).start();
}
// 關閉對話方塊
_close() {
this.setState({isShow: false});
this.state.opacityAnimationValue.setValue(0);
this.state.scaleAnimationValue.setValue(0);
}
render() {
if (!this.state.isShow) return null;
const {ensureCallback,titleTxt} = this.props;
return (
// 最外層是一個半透明的黑色蒙版背景,點選的時候對話方塊也會消失
<Animated.View style={[styles.container, {opacity: this.state.opacityAnimationValue}]}>
<TouchableOpacity
activeOpacity={1}
style={{flex: 1, alignItems: 'center', paddingTop: 100}}
onPress={this._close}
>
<Animated.View
style={[styles.contentContainer, {transform: [{scale: this.state.scaleAnimationValue}]}]}
>
<TouchableOpacity
activeOpacity={1}
style={styles.promptContainer}
>
<Text style={{fontSize: 15, color: 'black'}}>{titleTxt}</Text>
<View style={{flexDirection: 'row', margin: 15}}>
<View style={[styles.center, {width: 230}]}>
<TextInput
style={{fontSize: 16, color: '#999',width:150,padding:0}}
value={this.state.inputText}
autoFocus={true}
underlineColorAndroid="transparent"
onChangeText={text => this.setState({inputText:text})}
/>
</View>
<TouchableOpacity
onPress={() => this.setState({inputText: ''})}
style={[styles.center, {width: 20}]}>
<Image
source={require('../../../assets/img/close.png')}
style={{width: 18, height: 18}}
/>
</TouchableOpacity>
</View>
</TouchableOpacity>
<View style={styles.buttonContainer}>
<TouchableOpacity
activeOpacity={0.75}
style={[styles.center, {flex: 4.5}]}
onPress={this._close}
>
<Text style={{fontSize: 16, color: 'black'}}>取消</Text>
</TouchableOpacity>
<View style={[styles.line]}/>
<TouchableOpacity
activeOpacity={0.75}
style={[styles.center, {flex: 4.5}]}
onPress={() => {
this._close();
// 子元件傳遞資料到父元件
ensureCallback(this.state.inputText);
}}
>
<Text style={{fontSize: 16, color: 'black'}}>確定</Text>
</TouchableOpacity>
</View>
</Animated.View>
</TouchableOpacity>
</Animated.View>
)
}
}
const styles = StyleSheet.create({
container: {
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
backgroundColor: 'rgba(1, 1, 1, 0.5)'
},
contentContainer: {
justifyContent: 'center',
alignItems: 'center',
borderColor: '#d9d9d9',
borderWidth: 1,
height: 150,
width: screenW * 0.75,
backgroundColor: 'rgb(234, 234, 235)',
borderRadius: 5,
},
promptContainer: {
height: 100,
width: screenW * 0.75,
alignItems: 'center',
justifyContent: 'center'
},
buttonContainer: {
height: 50,
width: screenW * 0.75,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
borderTopWidth: 1,
borderColor: '#d9d9d9'
},
line: {
height: 46,
width: 1,
backgroundColor: '#d9d9d9'
},
center: {
justifyContent: 'center',
alignItems: 'center'
}
})