- 原文地址:Animated Transition in React Native!
- 原文作者:Jiří Otáhal
- 譯文出自:掘金翻譯計劃
- 本文永久連結:github.com/xitu/gold-m…
- 譯者:talisk
- 校對者:foxxnuaa
這篇文章有近 15k 的瀏覽量。對某些人來說,這可能沒什麼,但對我來說是一個很大的動力。這正是我決定構建 Pineapple — Financial Manager 的原因。僅僅 20 天,我已經完成了 iOS 版,Android 版以及網頁版,花費 300 美金,並寫了幾篇關於它的文章。我無法用言語表達我多麼享受這段時間。你也應該試試!
最近我試圖為下一個動畫挑戰尋找靈感。我們開始吧 —— 由 Ivan Parfenov 建立。我很好奇我是否能夠用 React Native 來做這個過渡效果。你可以在我的 expo 帳戶中檢視結果!為什麼我們還需要這樣的動畫?來讀讀 Pablo Stanley 寫的絕佳的 UI 動畫技巧。
Ivan Parfenov 設計的 PLΛTES。
我們可以看到有幾個動畫。工具欄(顯示/隱藏),底欄(顯示/隱藏),移動所選專案,隱藏所有其他專案,顯示詳細資訊專案甚至更多。
動畫時間線。
過渡動畫的難點在於同步所有這些動畫。因為我們需要等到所有動畫都完成,我們無法真正移除列表頁面並顯示詳細資訊頁面。此外,我對整潔的程式碼有所追求。程式碼要易於維護,如果您曾嘗試為專案實現動畫,則程式碼通常會變得混亂。到處都是輔助變數,各種計算等。這正是我想介紹 react-native-motion 的原因。
對 react-native-motion 的一個小想法
你能看到工具欄標題的動畫嗎?你只需稍微移動標題並將不透明度設定為 0 或 1。這很簡單!但正因為如此,你需要編寫這樣的程式碼,甚至在你真正開始為該元件編寫 UI 之前。
class TranslateYAndOpacity extends PureComponent {
constructor(props) {
// ...
this.state = {
opacityValue: new Animated.Value(opacityMin),
translateYValue: new Animated.Value(translateYMin),
};
// ...
}
componentDidMount() {
// ...
this.show(this.props);
// ...
}
componentWillReceiveProps(nextProps) {
if (!this.props.isHidden && nextProps.isHidden) {
this.hide(nextProps);
}
if (this.props.isHidden && !nextProps.isHidden) {
this.show(nextProps);
}
}
show(props) {
// ...
Animated.parallel([
Animated.timing(opacityValue, { /* ... */ }),
Animated.timing(translateYValue, { /* ... */ }),
]).start();
}
hide(props) {
// ...
Animated.parallel([
Animated.timing(opacityValue, { /* ... */ }),
Animated.timing(translateYValue, { /* ... */ }),
]).start();
}
render() {
const { opacityValue, translateYValue } = this.state;
const animatedStyle = {
opacity: opacityValue,
transform: [{ translateY: translateYValue }],
};
return (
<Animated.View style={animatedStyle}>{this.props.children}</Animated.View>
);
}
}
複製程式碼
現在讓我們來看看如果用 react-native-motion 實現這個動畫,要怎麼做。我知道動畫經常是非常具體的。我知道 React Native 提供了非常強大的動畫 API。無論如何,擁有一個帶有基本動畫的庫會很棒。
import { TranslateYAndOpacity } from 'react-native-motion';
class ToolbarTitle extends PureComponent {
render() {
return (
<TranslateYAndOpacity duration={250}>
<View>
// ...
</View>
</TranslateYAndOpacity>
);
}
}
複製程式碼
共享的元素
這一挑戰的最大問題是移動選定的列表項。列表頁面和詳細資訊頁面之間共享的專案。當元素實際上沒有絕對定位時,如何將專案從 FlatList 移動到 Detail 的頁面頂部?使用 react-native-motion 非常容易。
// List items page with source of SharedElement
import { SharedElement } from 'react-native-motion';
class ListPage extends Component {
render() {
return (
<SharedElement id="source">
<View>{listItemNode}</View>
</SharedElement>
);
}
}
複製程式碼
我們在 ListPage 上指定了 SharedElement 的 source 元素。現在我們需要對 DetailPage 上的目標元素執行幾乎相同的操作,來知道我們想要移動共享元素的位置。
// Detail page with a destination shared element
import { SharedElement } from 'react-native-motion';
class DetailPage extends Component {
render() {
return (
<SharedElement sourceId="source">
<View>{listItemNode}</View>
</SharedElement>
);
}
}
複製程式碼
黑科技在哪裡?
我們如何將相對定位的元素從一個頁面移動到另一個頁面?實際上我們做不到。SharedElement 的工作方式如下:
- 獲取源 element 的位置
- 獲取目標 element 的位置(顯然,沒有這一步,動畫不能被初始化)
- 建立共享的 element(黑科技!)
- 在螢幕上方渲染一個新圖層
- 渲染 element 元素,將覆蓋源 element(在源 element 的位置上)
- 開始移動到目標 element 位置
- 一旦移動到目標 element 位置後,刪除克隆 element
你可以想象在同一時刻同一個 React Node 有 3 個 element。這是因為在移動動畫期間,DetailPage 會覆蓋列表頁面。這就是為什麼我們可以看到所有 3 個元素。但是我們想要創造一種我們實際移動了原始 element 的幻覺。
SharedElement 的時間線。
您可以看到 A 點和 B 點。這是移動正在執行的時間段。您還可以看到 SharedElement 觸發一些有用的事件。在這種情況下,我們使用 WillStart 和 DidFinish 事件。在啟動移動目標 element 時,將源 element 和目標 element 的不透明度設定為 0,並在動畫完成後將目標 element 的不透明度設定為 1。
你覺得怎麼樣?
社群這裡一直不斷在維護和更新:react-native-motion。這絕不是這個庫的最終和穩定版本。但是一個好的開始 :) 我很想聽聽你怎麼想!
我一直在尋找新的挑戰和機會。如果您需要幫助,請告訴我,我很樂意討論它。
如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。
掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。