網速是可變的,尤其是當你使用移動設備時。作為開發人員,我們經常忘記,許多用戶正在功能較差的設備上以很慢的網速運行我們的應用程序。到山裡去,試著訪問你的應用程序,看看它表現如何。
應用程序中最耗費的東西之一就是要載入遠程圖像。 它們需要時間來載入,尤其是大圖片。
今天我們將構建一個組件,它允許我們: - 傳遞要顯示的全尺寸圖像(就像普通圖像組件一樣) - 在載入完整大小的圖像時,傳遞一個縮略圖以顯示 - 自動在即將下載的圖像的位置顯示一個佔位符,以指示將有內容存在 - 每個狀態之間的動畫
要開始創建一個新的 React Native 應用程序(通過 React-Native init、 create-React-Native-app 或 expo cli) ,並將以下內容添加到 App.js。
React-Native init
create-React-Native-app
expo
import React from react; import { StyleSheet, View, Dimensions, Image } from react-native;
const w = Dimensions.get(window);
const styles = StyleSheet.create({ container: { flex: 1, alignItems: center, justifyContent: center, }, });
export default class App extends React.Component { render() { return ( <View style={styles.container}> <Image source={{ uri: `https://images.pexels.com/photos/671557/pexels-photo-671557.jpeg?w=${w.width * 2}&buster=${Math.random()}` }} style={{ width: w.width, height: w.width }} resizeMode="cover" /> </View> ); } }
該代碼塊只顯示一個圖像。 它從 Pexels 請求2倍屏幕大小的圖像(我們想要大的圖像,因此圖片載入緩慢) ,buster 查詢參數將幫助我們不緩存圖像,以便我們能夠充分看到發生了什麼。 你不會想在你的實際應用程序中這麼做的。
buster
正如我之前所說,作為一名開發人員,你可能擁有相當不錯的網速。
讓我們改變這一點。
如果你是 Mac 電腦,你可以安裝一個叫做 Network Link Conditioner 的工具(這裡是如何安裝它)。 我確信Windows 和 Linux 上也有類似的東西(如果你有建議的工具,請在下面附上)。
它將允許您模擬幾乎任何您需要的網路條件。 記得在打開流媒體視頻之前把它關掉。
啟用它並將其設置為"3G"。
這裡對比了 Network Link Conditioner 關閉和打開3g時的狀態。
為了替換 Image 組件,我們將創建一個名為 ProgressiveImage 的新組件。 這個組件的目標是讓它能夠像正常的 Image 組件一樣工作,只是增加了一些額外的特性: - 在圖像所在的位置填充彩色背景 - 傳遞縮略圖的能力
Image
ProgressiveImage
首先,讓我們打好基礎:
// ProgressiveImage.js import React from react; import { View, StyleSheet, Image } from react-native; const styles = StyleSheet.create({ }); class ProgressiveImage extends React.Component { render() { return <Image {...this.props} /> } } export default ProgressiveImage;
我們使用擴展運算符來傳遞所有 this.props 到 Image 組件,這樣一切都能按照預期工作,而不需要我們手動定義每個 prop。
this.props
然後用新的 ProgressiveImage 組件替換 App.js 中的 Image。
App.js
// App.js import React from react; import { StyleSheet, View, Dimensions } from react-native; import ProgressiveImage from ./ProgressiveImage; // ... export default class App extends React.Component { render() { return ( <View style={styles.container}> <ProgressiveImage source={{ uri: `https://images.pexels.com/photos/671557/pexels-photo-671557.jpeg?w=${w.width * 2}&buster=${Math.random()}` }} style={{ width: w.width, height: w.width }} resizeMode="cover" /> </View> ); } }
一切都應該和以前一模一樣。
載入遠程圖像時,需要指定要呈現的圖像的寬度和高度。 我們將利用這個要求來方便的設置一個默認的背景顏色。
import React from react; import { View, StyleSheet, Image } from react-native; const styles = StyleSheet.create({ imageOverlay: { position: absolute, left: 0, right: 0, bottom: 0, top: 0, }, container: { backgroundColor: #e1e4e8, }, }); class ProgressiveImage extends React.Component { render() { return ( <View style={styles.container}> <Image {...this.props} /> </View> ); } } export default ProgressiveImage;
首先,我們為背景顏色創建一個名為 container 樣式,然後將 Image 組件包裝到視圖中(使用分配給它的新樣式)。
container
這是漸進式圖像載入的第一階段。
接下來,我們將著手顯示圖像的縮略版。 生成此圖像超出了本教程的範圍,因此我們假設您已經獲得了全尺寸圖像和縮略圖版本。
首先,對於 ProgressiveImage 實例,我們將添加一個 thumbnailSource prop,它將獲取與典型 Image 源prop完全相同的信息。在其中,我們將傳遞一個較小版本的圖像(本例中w為50,使用任何您想要的)和用來清除緩存的變數 buster (僅用於演示目的)。
thumbnailSource
// App.js // ... export default class App extends React.Component { render() { return ( <View style={styles.container}> <ProgressiveImage thumbnailSource={{ uri: `https://images.pexels.com/photos/671557/pexels-photo-671557.jpeg?w=50&buster=${Math.random()}` }} source={{ uri: `https://images.pexels.com/photos/671557/pexels-photo-671557.jpeg?w=${w.width * 2}&buster=${Math.random()}` }} style={{ width: w.width, height: w.width }} resizeMode="cover" /> </View> ); } }
然後我們將修改 ProgressiveImage 組件。 首先在樣式對象中添加 imageOverlay 樣式。
imageOverlay
// ProgressiveImage.js // ... const styles = StyleSheet.create({ imageOverlay: { position: absolute, left: 0, right: 0, bottom: 0, top: 0, }, container: { backgroundColor: #e1e4e8, }, }); // ...
然後我們將渲染兩個 Image 組件。 在此之前,我們將使用對象解構來從 this.props 中取出一些prop ,因為我們將覆蓋 / 組合它們。
// ProgressiveImage.js // ... class ProgressiveImage extends React.Component { render() { const { thumbnailSource, source, style, ...props } = this.props; return ( <View style={styles.container}> <Image {...props} source={thumbnailSource} style={style} /> <Image {...props} source={source} style={[styles.imageOverlay, style]} /> </View> ); } } export default ProgressiveImage;
你可以看到我們得到了 thumbnailSource,source 和 style的 prop。 然後我們使用 "rest" 語法來捕捉其餘的 props。 這使得我們可以將所有通用 prop 轉發到兩個圖像中,而只將所需的 prop 轉發到正確的組件中(比如合適的源)。
source
style
你會注意到我們在全尺寸圖像中結合了傳入的樣式和 styles.imageOverlay 樣式。 這樣,通過絕對定位,圖像會掩蓋縮略版。
styles.imageOverlay
結果如下:
注意: 你會注意到縮略圖是相當像素化。 你可以把 blurRadius 屬性傳遞給縮略圖來模糊它。我做了一個屏幕截圖,這樣你就可以看到區別了(例如,我使用的是 blurRadius 為2的模型)。
blurRadius
你還會注意到,如果我們不將 thumbnailSource 傳遞給 ProgressiveImage 組件,則一切正常,這意味著即使沒有縮略圖,我們也可以對所有遠程圖像使用此組件。
最後我們要做的是平滑過渡到背景顏色,縮略圖和全尺寸圖像。 要做到這一點,我們將使用 React Native 中的 Animated 依賴。
Animated
一旦你導入了 Animated,你就需要用 Animated.image 替換 ProgressiveImage 中的 Image 組件。
Animated.image
您還需要在組件上創建兩個新的動畫變數,將它們默認設置為0。
// ProgressiveImage.js import React from react; import { View, StyleSheet, Animated } from react-native; // ... class ProgressiveImage extends React.Component { thumbnailAnimated = new Animated.Value(0); imageAnimated = new Animated.Value(0); render() { const { thumbnailSource, source, style, ...props } = this.props; return ( <View style={styles.container}> <Animated.Image {...props} source={thumbnailSource} style={style} blurRadius={2} /> <Animated.Image {...props} source={source} style={[styles.imageOverlay, style]} /> </View> ); } }
這些 Animated.Value 將用於驅動圖像的不透明度。 當縮略圖載入時,我們將設置 thumbnailAnimated 為1。 當載入完整大小的圖像時,我們將 imageAnimated 設置為1。
Animated.Value
thumbnailAnimated
imageAnimated
// ProgressiveImage.js // ... class ProgressiveImage extends React.Component { thumbnailAnimated = new Animated.Value(0); imageAnimated = new Animated.Value(0); handleThumbnailLoad = () => { Animated.timing(this.thumbnailAnimated, { toValue: 1, }).start(); } onImageLoad = () => { Animated.timing(this.imageAnimated, { toValue: 1, }).start(); } // ... }
這些函數將通過 Animated.Image 組件的 onLoad prop調用。
Animated.Image
onLoad
// ProgressiveImage.js // ... class ProgressiveImage extends React.Component { // ... render() { const { thumbnailSource, source, style, ...props } = this.props; return ( <View style={styles.container}> <Animated.Image {...props} source={thumbnailSource} style={[style, { opacity: this.thumbnailAnimated }]} onLoad={this.handleThumbnailLoad} blurRadius={1} /> <Animated.Image {...props} source={source} style={[styles.imageOverlay, { opacity: this.imageAnimated }, style]} onLoad={this.onImageLoad} /> </View> ); } } export default ProgressiveImage;
最終的漸進式圖像載入的結果。
代碼可以在 Github 上找到。
原文地址:https://medium.com/react-native-training/progressive-image-loading-in-react-native-e7a01827feb7
請關注我們的公眾號: FENews