ReactNative實現AppStoreToday頁效果

NO IMAGE

React Native 動效系列

歡迎大家Star我們的倉庫react-native-showcase,記錄了各種React Native的交互、動畫效果。

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

ReactNative實現AppStoreToday頁效果

第一步:完成靜態UI佈局

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

<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-navigation還是react-native-navigation等都無法實現這種複用頁面UI元素的跳轉動畫),因此所謂的列表”頁”、詳情”頁”只是同一個頁面的兩種不同狀態的View

  • 有了上面的結論,我們在自己分析動畫效果:其實整個打開詳情動畫有兩部分組成:

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

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

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

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

我們先隱藏列表頁,詳情頁如下圖所示:

ReactNative實現AppStoreToday頁效果

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

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

我們可以利用當前點擊圖片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我們的人人貸大前端團隊博客,所有的文章還會同步更新到知乎專欄掘金賬號,我們每週都會分享幾篇高質量的大前端技術文章。

相關文章

Gin(九):生成restful接口

JSI小試牛刀——Native同步調用JS代碼

iOSDeepLink調研與實踐

canvas+js從0開始擼一個俄羅斯方塊