在上一篇文章中,我們學(xué)習(xí)了React Native實(shí)現(xiàn)動(dòng)畫的幾種方式,其中重點(diǎn)介紹了LayoutAnimation杰捂。文章的末尾也提到秸讹,如果你需要更強(qiáng)大的動(dòng)畫功能檀咙,就需要使用高級(jí)API—Animated。
如果你還不了解LayoutAnimation璃诀,建議先閱讀下上一篇文章[React Native]動(dòng)畫-LayoutAnimation弧可,其中的一些概念能讓你更好的理解本篇文章的內(nèi)容。
本篇文章會(huì)一步步介紹Animated的用法劣欢,如果有誤之處棕诵,歡迎指正~
動(dòng)畫類型:
- spring:基礎(chǔ)的單次彈跳物理模型
- timing:從時(shí)間范圍映射到漸變的值
- decay:以一個(gè)初始速度開始并且逐漸減慢停止
創(chuàng)建動(dòng)畫的參數(shù):
- value:AnimatedValue | AnimatedValueXY(X軸或Y軸 | X軸和Y軸)
- config:SpringAnimationConfig | TimingAnimationConfig | DecayAnimationConfig(動(dòng)畫的參數(shù)配置)
組件類型:
- Animated.Text
- Animated.Image
- Animated.View:可以用來包裹任意視圖
- Animated.createAnimatedComponent():其它組件(較少用,用Animated.View包裹可以達(dá)到同樣的效果)
讓我們來看一個(gè)示例:圖片透明度2秒內(nèi)從不透明到全透明凿将,線性變化校套。
class Demo8 extends Component {
// 構(gòu)造
constructor(props) {
super(props);
// 初始狀態(tài)
this.state = {
fadeOutOpacity: new Animated.Value(0),
};
}
render() {
return (
<Animated.View // 可選的基本組件類型: Image, Text, View(可以包裹任意子View)
style = {{flex: 1,alignItems: 'center',justifyContent: 'center',
opacity: this.state.fadeOutOpacity,}}>
<Image source = {{uri: 'http://i.imgur.com/XMKOH81.jpg'}}
style = {{width: 400,height: 400}}/>
</Animated.View >
);
}
startAnimation() {
this.state.fadeOutOpacity.setValue(1);
Animated.timing(this.state.fadeOutOpacity, {
toValue: 0,
duration: 2000,
easing: Easing.linear,// 線性的漸變函數(shù)
}).start();
}
componentDidMount() {
this.startAnimation();
}
}
AppRegistry.registerComponent('Demo8', () = >Demo8);
效果圖如下:
值類型:
- AnimatedValue:?jiǎn)蝹€(gè)值
- AnimatedValueXY:向量值
多數(shù)情況下,AnimatedValue可以滿足需求(上面的示例)牧抵,但有些情況下我們可能會(huì)需要AnimatedValueXY笛匙。
比如:我們需要圖片沿著X軸和Y軸交叉方向,向右下角移動(dòng)一小段距離犀变。
class Demo8 extends Component {
// 構(gòu)造
constructor(props) {
super(props);
// 初始狀態(tài)
this.state = {
translateValue: new Animated.ValueXY({x:0, y:0}), // 二維坐標(biāo)
};
}
render() {
return (
<Animated.View // 可選的基本組件類型: Image, Text, View(可以包裹任意子View)
style = {{flex: 1,alignItems: 'center',justifyContent: 'center',
transform: [
{translateX: this.state.translateValue.x}, // x軸移動(dòng)
{translateY: this.state.translateValue.y}, // y軸移動(dòng)
]
}}>
<Image source = {{uri: 'http://i.imgur.com/XMKOH81.jpg'}}
style = {{width: 400,height: 400}}/>
</Animated.View >
);
}
startAnimation() {
this.state.translateValue.setValue({x:0, y:0});
Animated.decay( // 以一個(gè)初始速度開始并且逐漸減慢停止妹孙。 S=vt-(at^2)/2 v=v - at
this.state.translateValue,
{
velocity: 10, // 起始速度,必填參數(shù)获枝。
deceleration: 0.8, // 速度衰減比例蠢正,默認(rèn)為0.997。
}
).start();
}
componentDidMount() {
this.startAnimation();
}
}
AppRegistry.registerComponent('Demo8', () = >Demo8);
其中省店,transform
是一個(gè)變換數(shù)組嚣崭,常用的有scale, scaleX, scaleY, translateX, translateY, rotate, rotateX, rotateY, rotateZ
:
...
transform: [ // scale, scaleX, scaleY, translateX, translateY, rotate, rotateX, rotateY, rotateZ
{scale: this.state.bounceValue}, // 縮放
{rotate: this.state.rotateValue.interpolate({ // 旋轉(zhuǎn),使用插值函數(shù)做值映射
inputRange: [0, 1],
outputRange: ['0deg', '360deg']})},
{translateX: this.state.translateValue.x}, // x軸移動(dòng)
{translateY: this.state.translateValue.y}, // y軸移動(dòng)
],
...
插值函數(shù):
將輸入值范圍轉(zhuǎn)換為輸出值范圍懦傍,如下:將0-1
數(shù)值轉(zhuǎn)換為0deg-360deg
角度雹舀,旋轉(zhuǎn)View
時(shí)你會(huì)用到
this.state.rotateValue.interpolate({ // 旋轉(zhuǎn),使用插值函數(shù)做值映射
inputRange: [0, 1],
outputRange: ['0deg', '360deg']})
組合動(dòng)畫:
- parallel:同時(shí)執(zhí)行
- sequence:順序執(zhí)行
- stagger:錯(cuò)峰谎脯,其實(shí)就是插入了delay的parrllel
- delay:組合動(dòng)畫之間的延遲方法葱跋,嚴(yán)格來講,不算是組合動(dòng)畫
讓我們來看一個(gè)示例:圖片首先縮小80%,2秒之后娱俺,旋轉(zhuǎn)360度稍味,之后沿著X軸與Y軸交叉方向向右下角移動(dòng)一段距離,最后消失變成全透明
startAnimation() {
this.state.bounceValue.setValue(1.5); // 設(shè)置一個(gè)較大的初始值
this.state.rotateValue.setValue(0);
this.state.translateValue.setValue({x: 0,y: 0});
this.state.fadeOutOpacity.setValue(1);
Animated.sequence([
Animated.sequence([ // 組合動(dòng)畫 parallel(同時(shí)執(zhí)行)荠卷、sequence(順序執(zhí)行)模庐、stagger(錯(cuò)峰,其實(shí)就是插入了delay的parrllel)和delay(延遲)
Animated.spring( // 基礎(chǔ)的單次彈跳物理模型
this.state.bounceValue, {
toValue: 0.8,
friction: 1,// 摩擦力油宜,默認(rèn)為7.
tension: 40,// 張力掂碱,默認(rèn)40。
}),
Animated.delay(2000), // 配合sequence慎冤,延遲2秒
Animated.timing( // 從時(shí)間范圍映射到漸變的值疼燥。
this.state.rotateValue, {
toValue: 1,
duration: 800,// 動(dòng)畫持續(xù)的時(shí)間(單位是毫秒),默認(rèn)為500
easing: Easing.out(Easing.quad),// 一個(gè)用于定義曲線的漸變函數(shù)
delay: 0,// 在一段時(shí)間之后開始動(dòng)畫(單位是毫秒)蚁堤,默認(rèn)為0醉者。
}),
Animated.decay( // 以一個(gè)初始速度開始并且逐漸減慢停止。 S=vt-(at^2)/2 v=v - at
this.state.translateValue, {
velocity: 10,// 起始速度披诗,必填參數(shù)撬即。
deceleration: 0.8,// 速度衰減比例,默認(rèn)為0.997呈队。
}),
]),
Animated.timing(this.state.fadeOutOpacity, {
toValue: 0,
duration: 2000,
easing: Easing.linear,// 線性的漸變函數(shù)
})
]).start();
}
效果圖如下:
循環(huán)執(zhí)行動(dòng)畫:
start
方法可以接受一個(gè)函數(shù)剥槐,通過監(jiān)聽動(dòng)畫結(jié)束,再調(diào)用startAnimation
可以重復(fù)執(zhí)行動(dòng)畫宪摧,例如:
startAnimation() {
this.state.translateValue.setValue({x:0, y:0});
Animated.decay( // 以一個(gè)初始速度開始并且逐漸減慢停止粒竖。 S=vt-(at^2)/2 v=v - at
this.state.translateValue,
{
velocity: 10, // 起始速度,必填參數(shù)几于。
deceleration: 0.8, // 速度衰減比例温圆,默認(rèn)為0.997。
}
).start(() => this.startAnimation());
}
監(jiān)聽當(dāng)前的動(dòng)畫值:
- addListener(callback):動(dòng)畫執(zhí)行過程中的值
- stopAnimation(callback):動(dòng)畫執(zhí)行結(jié)束時(shí)的值
監(jiān)聽AnimatedValueXY類型translateValue
的值變化:
this.state.translateValue.addListener((value) => {
console.log("translateValue=>x:" + value.x + " y:" + value.y);
});
this.state.translateValue.stopAnimation((value) => {
console.log("translateValue=>x:" + value.x + " y:" + value.y);
});
監(jiān)聽AnimatedValue類型rotateValue
的值變化:
this.state.rotateValue.addListener((state) => {
console.log("rotateValue=>" + state.value);
});
this.state.rotateValue.stopAnimation((state) => {
console.log("rotateValue=>" + state.value);
});
好了孩革,到這里我們把Animated的常用方法都介紹了,也給出了代碼示例(略微有點(diǎn)多)得运。建議大家動(dòng)手嘗試下每個(gè)效果膝蜈,這樣,可以理解的更加深刻
本文的源碼地址:Demo8