ReactNative之FlatList-列表入場動畫

FlatList-列表入場動畫

前言

網上查找資料:RN FlatList入場動畫,居然一個都沒搜索到,並且仍是百度+google雙大法。最後沒辦法,只能本身仔細看Animated庫怎麼使用,各類屬性都慢慢試了,才大體弄清楚動畫的實現。先看實現的效果圖:react

效果圖1

merge.gif

效果圖2

![android

mergeX.gif

效果圖3

x.gif

項目中的效果圖

project.gif

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>
    )
  }
}
複製代碼

Animated的動畫基本配置方法有timing,spring,decay;

一、timing

它是線性動畫,在設定時間內移動到終點,中間的動畫能夠設置react-native

toValue:線性改變的數值

duration: 動畫持續時間 默認500.

easing:默認的實現Easing.in和Easing.out和Easing.inOut。

delay: 動畫延遲開始時間。
複製代碼

簡單的使用方式:數組

Animated.timing(
        this.state.animatePress, {
            toValue: 0.8,
            duration: 200
        }
    ).start();
複製代碼

二、spring

相似彈簧的實現,動畫結束時會有個漸弱的擺動,它的屬性在源碼中能夠看到有不少:性能

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
                    }),
複製代碼

三、decay

動畫的速度逐漸變慢,最後中止,相似上面的轉場動畫

Animated的動畫組合方式有:parallel,sequence,stagger,

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>
        );

    }

}
複製代碼

Animated使用中遇到的問題

FlatList橫向排列,使用translateX參數,進行子item的入參動畫,從屏幕外進入到相應的位置。開發完成後,發如今ios表現正常,可是在android中存在問題,子item不會從屏幕外進入,而是在item的原先位置進行展現,看圖:

IOS正常表現:

g3vhq.gif

android異常表現:

l8tic.gif

這個問題被折磨了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"
複製代碼

代碼

效果圖的代碼在此處

個人簡書地址

最後感謝

RN中文網

相關文章
相關標籤/搜索