網上查找資料:RN FlatList入場動畫,居然一個都沒搜索到,並且仍是百度+google雙大法。最後沒辦法,只能本身仔細看Animated庫怎麼使用,各類屬性都慢慢試了,才大體弄清楚動畫的實現。先看實現的效果圖:react
![android
gif有點卡,真機運行是很流暢的。ios
ReactNative的view動畫效果主要使用Animated庫來進行製做,經過start和stop來進行控制動畫的啓動。animate封裝了四個能夠動畫化的組件:View,Text,Image,ScrollView。也可使用Animated.createAnimatedComponent()封裝本身的動畫組件。spring
簡單的使用方式(中文網上的代碼),淡入動畫效果的視圖:json
import React from 'react';
import { Animated, Text, View } from 'react-native';
class FadeInView extends React.Component {
state = {
fadeAnim: new Animated.Value(0), // 透明度初始值設爲0
}
componentDidMount() {
Animated.timing( // 隨時間變化而執行動畫
this.state.fadeAnim, // 動畫中的變量值
{
toValue: 1, // 透明度最終變爲1,即徹底不透明
duration: 10000, // 讓動畫持續一段時間
}
).start(); // 開始執行動畫
}
render() {
let { fadeAnim } = this.state;
return (
<Animated.View // 使用專門的可動畫化的View組件
style={{
...this.props.style,
opacity: fadeAnim, // 將透明度指定爲動畫變量值
}}
>
{this.props.children}
</Animated.View>
);
}
}
// 而後你就能夠在組件中像使用`View`那樣去使用`FadeInView`了
export default class App extends React.Component {
render() {
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<FadeInView style={{width: 250, height: 50, backgroundColor: 'powderblue'}}>
<Text style={{fontSize: 28, textAlign: 'center', margin: 10}}>Fading in</Text>
</FadeInView>
</View>
)
}
}
複製代碼
它是線性動畫,在設定時間內移動到終點,中間的動畫能夠設置react-native
toValue:線性改變的數值
duration: 動畫持續時間 默認500.
easing:默認的實現Easing.in和Easing.out和Easing.inOut。
delay: 動畫延遲開始時間。
複製代碼
簡單的使用方式:數組
Animated.timing(
this.state.animatePress, {
toValue: 0.8,
duration: 200
}
).start();
複製代碼
相似彈簧的實現,動畫結束時會有個漸弱的擺動,它的屬性在源碼中能夠看到有不少:性能
interface SpringAnimationConfig extends AnimationConfig {
toValue: number | AnimatedValue | { x: number; y: number } | AnimatedValueXY;
overshootClamping?: boolean;
restDisplacementThreshold?: number;
restSpeedThreshold?: number;
velocity?: number | { x: number; y: number };
bounciness?: number;
speed?: number;
tension?: number;
friction?: number;
stiffness?: number;
mass?: number;
damping?: number;
delay?: number;
}
複製代碼
經常使用的有: toValue:線性改變的數值 friction:彈簧界限值,越小彈的越大,默認7 tension:彈簧張力,默認40,越大進入的速度越快flex
簡單的使用方式:優化
Animated.spring(this.state.animateItem, {
toValue: 1,
velocity: 0.1,
bounciness: 5,
friction:5
}),
複製代碼
動畫的速度逐漸變慢,最後中止,相似上面的轉場動畫
parallel:並行運行動畫
sequence:依次運行動畫,若是一個動畫被中止了,那麼下一個動畫就不會被執行。
stagger:每一個動畫延遲一段時間執行
這三個動畫的基本入參是動畫數組,stagger有額外的time(延時)參數。並且數組內仍然能夠傳入parallel,sequence,stagger等各類類型,安裝規則進行動畫播放。
栗子以下:
//動畫並行parallel
Animated.parallel(
[
//動畫順序執行
Animated.sequence(
[
Animated.delay(delay),
// Animated.spring(this.state.animateItem, {
// toValue: 1,
// velocity: 0.1,
// bounciness: 5,
// }),
Animated.timing(this.state.animateItem, {
toValue: 1,
duration: 500,
// delay: delay
})
]
),
Animated.sequence(
[
Animated.delay(delay),
Animated.timing(this.state.animateOpacity, {
toValue: 1,
duration: 1000
})
]
)
]
).start()
複製代碼
動畫的基本介紹完成後,在看看效果圖,其實就是動畫簡單的組合,使用了transform參數,另外加了個插值器。
核心代碼:
<Animated.View style={{
backgroundColor: "transparent",
margin: 5,
transform: [
{
scale: this.state.animatePress
},
{
translateX: this.state.animateItem.interpolate({
inputRange: [0, 1],
outputRange: [700, 1]
})
}
]
}}
複製代碼
對FlatList的子Item進行封裝,所有代碼以下(其中一個組件的文件):
import React, { Component } from 'react';
import {
Text,
View,
StyleSheet,
Image,
TouchableWithoutFeedback,
Animated
} from 'react-native';
export default class ListItem extends Component {
state = {
animatePress: new Animated.Value(1),
animateItem: new Animated.Value(0)
}
animateIn() {
Animated.timing(
this.state.animatePress, {
toValue: 0.8,
duration: 200
}
).start();
}
animateOut() {
Animated.timing(this.state.animatePress, {
toValue: 1,
duration: 200
}).start();
}
componentWillMount() {
const { index } = this.props;
const delay = index * 300
Animated.timing(this.state.animateItem, {
toValue: 1,
duration: 1000,
delay: delay
}).start();
}
render() {
const { text,index } = this.props;
return (
<TouchableWithoutFeedback
onPressIn={() => this.animateIn()}
onPressOut={() => this.animateOut()}
style={{
backgroundColor: "transparent",
}}
>
<Animated.View style={{
backgroundColor: "transparent",
margin: 5,
transform: [
{
scale: this.state.animatePress
},
{
translateX: this.state.animateItem.interpolate({
inputRange: [0, 1],
outputRange: [700, 1]
})
}
]
}}
>
<Text
style={{ width: 150, height: 100, backgroundColor: 'yellow', }}
>
{text}{index}
</Text>
</Animated.View>
</TouchableWithoutFeedback>
);
}
}
複製代碼
FlatList橫向排列,使用translateX參數,進行子item的入參動畫,從屏幕外進入到相應的位置。開發完成後,發如今ios表現正常,可是在android中存在問題,子item不會從屏幕外進入,而是在item的原先位置進行展現,看圖:
IOS正常表現:
android異常表現:
這個問題被折磨了2天時間,網上各類查找資料都沒有,後來在startoverflow上發現一個和我遇到一樣問題的人,可是並無獲得解決,地址以下:StackOverFlow上有人遇到一樣的錯誤
後來忽然想到FlatList有個優化屬性:getItemLayou,當我加入後,android端就表現正常了,對FlatList添加的代碼以下:
getItemLayout={(data, index) => (
{ length: 200, offset: 200 * index, index }
)}
複製代碼
getItemLayou的做用是爲了避免讓FlatList在渲染過程當中對內容尺寸作動態計算,在該方法中給予他設定的高度,可是使用它的前提是知道Item的寬高,若是item的寬高是固定的,使用這個方法就會大大優化列表性能,若是數據少,可能不明顯,可是數據量大的時候就會表現比較明顯。
加入這個方法可以解決這個問題,可是我不知道是什麼緣由致使的這樣,並且這個方法我這樣寫動畫也能渲染正常:
getItemLayout={(data, index) => (
{ length: 1, offset: 1 , index }
)}
複製代碼
因此這個地方很糾結,不過這個bug在最新的版本0.59.5中已近解決了,不須要使用getItemLayout也能正常播放動畫。
入場動畫bug版本:
0.59.4版本及以前的版本都存在此bug,不過在0.59.5版本中修復了該問題。在package.json中查看版本號:
"react-native": "0.59.4",
//該版本已修復入場動畫的bug
"react-native": "0.59.5"
複製代碼