React Native 實現 App Store Today頁效果

React Native 動效系列

歡迎你們Star咱們的倉庫react-native-showcase,記錄了各類React Native的交互、動畫效果。javascript

本文介紹如何實現利用共享UI元素的動畫,實現相似蘋果App Store的Today頁面的動畫效果,咱們先看最終的效果:前端

第一步:完成靜態UI佈局

UI佈局很簡單,就是一個普通的ScrollView,咱們直接看代碼:java

<ScrollView style={{ flex: 1,  paddingTop: 20, }}>
    {
        Images.map((image, index) => {
            return (
                <View style={{ width: SCREEN_WIDTH, height: SCREEN_HEIGHT - 150, padding: 15 }}> <Image source={image} style={{ height: null, width: null, borderRadius: 20, flex: 1, resizeMode: 'cover', }} /> </View> ); }) } </ScrollView>
複製代碼

也能夠直接打開expo project查看第一步效果。react

第二步:完成打開詳情頁動畫

在開始寫代碼以前咱們先分先一下動畫的幾個特色:git

  • 首先咱們要明確從列表"頁"到詳情"頁",其實並非真正意義的頁面跳轉(至少現有的不管是react-navigation仍是react-native-navigation等都沒法實現這種複用頁面UI元素的跳轉動畫),所以所謂的列表"頁"、詳情"頁"只是同一個頁面的兩種不一樣狀態的Viewgithub

  • 有了上面的結論,咱們在本身分析動畫效果:其實整個打開詳情動畫有兩部分組成:react-native

    1. 詳情"頁"圖片從當前點擊位置和大小移動到最終頂部位置
      2. 在此過程當中圖片介紹文案位置從屏幕視覺外移動到最終位置
    複製代碼

下面咱們來看看具體代碼如何實現:app

首先完成詳情"頁"佈局:佈局

<ScrollView style={{ flex: 1,  paddingTop: 20, }}>
    ...
</ScrollView>
<View style={StyleSheet.absoluteFill}>
    <View style={{ flex: 2 }}>
    <Image
        source={ this.state.activeImage ? this.state.activeImage : null }
        style={{ top: 0, right: 0, height: null, width: null, resizeMode: 'cover' }}
    />
    <TouchableWithoutFeedback>
        <View style={{ position: 'absolute', right: 20, top: 30 }}>
        <Text style={{ fontSize: 20, color: '#fff' }}>X</Text>
        </View>
    </TouchableWithoutFeedback>
    </View>
    <View style={{ flex: 1, backgroundColor: '#fff', }}>
        <Text style={{ fontSize: 24 }}>這裏是圖片的title</Text>
        <Text>這裏是圖片的詳情,詳細介紹圖片的狀況</Text>
    </View>
</View>
複製代碼

咱們先隱藏列表頁,詳情頁以下圖所示:post

接下來咱們須要給:圖片、文字介紹內容區域、關閉按鈕等分別添加動畫效果。

圖片動畫效果分析:當用戶點擊列表圖片時,從用戶當前點擊圖片的位置、大小變化到最終詳情"頁"中圖片位置和大小。

咱們能夠利用當前點擊圖片Ref的measure方法中拿到它的位置和大小信息,代碼以下:

constructor(props) {
    // 存貯全部的圖片ref,動畫時獲取當前圖片的位置信息
    this.imageRef = [];
    this.state = {
      activeImage: null,
    }
}

function openImage(index) {
    this.imageRef[index].measure((x, y, width, height, pageX, pageY) => {
        // pageX、pageY 圖片在屏幕中的座標位置
        // width、height 圖片的寬高
        // ...... 有了圖片的位置和大小信息咱們就能夠開始動畫了
    });
}

<TouchableWithoutFeedback onPress={() => { this.openImage(index); }}>
    <Image source={image} ref={(image) => { this.imageRef[index] = image; }} style={{ height: null, width: null, borderRadius: 20, flex: 1, resizeMode: 'cover', }} /> </TouchableWithoutFeedback>

複製代碼

下面咱們來完成圖片的動畫效果:位置、大小、圓角等變化:

constructor(props) {
    this.position = new Animated.ValueXY();
    this.measure = new Animated.ValueXY();
    this.animation = new Animated.Value(0);
}

function openImage(index) {
    // 獲取點擊圖片的信息
    this.imageRef[index].measure((x, y, width, height, pageX, pageY) => {
        this.position.setValue({ x: pageX, y: pageY });
        this.measure.setValue({ x: width, y: height });
        this.setState(
            () => { return { activeImage: Images[index] } },
            () => {
                // 獲取目標位置信息
                this.imageContainer.measure((x, y, width, height, pageX, pageY) => {
                    Animated.parallel([
                        Animated.timing(this.position.x, {
                            toValue: pageX,
                            duration: 350,
                            // 增長一個彈性效果
                            easing: Easing.back(1),
                        }),
                        Animated.timing(this.position.y, {
                            toValue: pageY,
                            duration: 350,
                            easing: Easing.back(1),
                        }),
                        Animated.timing(this.measure.x, {
                            toValue: width,
                            duration: 350,
                            easing: Easing.back(1),
                        }),
                        Animated.timing(this.measure.y, {
                            toValue: height,
                            duration: 350,
                            easing: Easing.back(1),
                        }),
                        Animated.timing(this.animation, {
                            toValue: 1,
                            duration: 350,
                            easing: Easing.back(1),
                        }),
                    ]).start();
                });
            }
        );
    });
}

const imageBorderRadiusAnimation = this.animation.interpolate({
    inputRange: [0, 1],
    outputRange: [20, 0]
});

const imageAnimationStyle = {
    left: this.position.x,
    top: this.position.y,
    width: this.measure.x,
    height: this.measure.y,
    borderRadius: imageBorderRadiusAnimation,
};

<TouchableWithoutFeedback onPress={() => { this.openImage(index); }}> <Image source={image} ref={(image) => { this.imageRef[index] = image; }} style={[ { height: null, width: null, borderRadius: 20, flex: 1, resizeMode: 'cover', }, imageAnimationStyle } /> </TouchableWithoutFeedback> 複製代碼

這樣咱們就完成了圖片的動畫效果,一樣添加文字區域和關閉按鈕的動畫效果,完整代碼能夠參考這裏

第三步:完成關閉詳情頁動畫

其實關閉詳情動畫徹底就是打開詳情動畫的反轉:

  1. 圖片頂部位置還原到點擊前位置,所以咱們須要在打開詳情時,保存點擊圖片在列表中的位置、大小信息
  2. 詳情文字介紹區域從底層消失
  3. 關閉按鈕逐漸變爲透明消失

代碼和打開詳情比較相似,能夠直接參考最終版

廣告

歡迎你們star咱們的人人貸大前端團隊博客,全部的文章還會同步更新到知乎專欄掘金帳號,咱們每週都會分享幾篇高質量的大前端技術文章。

相關文章
相關標籤/搜索