網速是可變的,尤其是當你使用移動設備時。作為開發人員,我們經常忘記,許多用戶正在功能較差的設備上以很慢的網速運行我們的應用程序。到山裡去,試著訪問你的應用程序,看看它表現如何。

應用程序中最耗費的東西之一就是要載入遠程圖像。 它們需要時間來載入,尤其是大圖片。

今天我們將構建一個組件,它允許我們: - 傳遞要顯示的全尺寸圖像(就像普通圖像組件一樣) - 在載入完整大小的圖像時,傳遞一個縮略圖以顯示 - 自動在即將下載的圖像的位置顯示一個佔位符,以指示將有內容存在 - 每個狀態之間的動畫

開始

要開始創建一個新的 React Native 應用程序(通過 React-Native initcreate-React-Native-appexpo cli) ,並將以下內容添加到 App.js。

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 查詢參數將幫助我們不緩存圖像,以便我們能夠充分看到發生了什麼。 你不會想在你的實際應用程序中這麼做的。

感受痛苦

正如我之前所說,作為一名開發人員,你可能擁有相當不錯的網速。

讓我們改變這一點。

如果你是 Mac 電腦,你可以安裝一個叫做 Network Link Conditioner 的工具(這裡是如何安裝它)。 我確信Windows 和 Linux 上也有類似的東西(如果你有建議的工具,請在下面附上)。

它將允許您模擬幾乎任何您需要的網路條件。 記得在打開流媒體視頻之前把它關掉。

啟用它並將其設置為"3G"。

Network Link Conditioner

這裡對比了 Network Link Conditioner 關閉和打開3g時的狀態。

Network Link Conditioner 關閉

Network Link Conditioner 打開3g

ProgressiveImage 組件

為了替換 Image 組件,我們將創建一個名為 ProgressiveImage 的新組件。 這個組件的目標是讓它能夠像正常的 Image 組件一樣工作,只是增加了一些額外的特性: - 在圖像所在的位置填充彩色背景 - 傳遞縮略圖的能力

首先,讓我們打好基礎:

// 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.propsImage 組件,這樣一切都能按照預期工作,而不需要我們手動定義每個 prop。

然後用新的 ProgressiveImage 組件替換 App.js 中的 Image

// 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 組件包裝到視圖中(使用分配給它的新樣式)。

這是漸進式圖像載入的第一階段。

圖像載入時的背景顏色

顯示縮略圖

接下來,我們將著手顯示圖像的縮略版。 生成此圖像超出了本教程的範圍,因此我們假設您已經獲得了全尺寸圖像和縮略圖版本。

首先,對於 ProgressiveImage 實例,我們將添加一個 thumbnailSource prop,它將獲取與典型 Image 源prop完全相同的信息。在其中,我們將傳遞一個較小版本的圖像(本例中w為50,使用任何您想要的)和用來清除緩存的變數 buster (僅用於演示目的)。

// 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 樣式。

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

你可以看到我們得到了 thumbnailSourcesourcestyle的 prop。 然後我們使用 "rest" 語法來捕捉其餘的 props。 這使得我們可以將所有通用 prop 轉發到兩個圖像中,而只將所需的 prop 轉發到正確的組件中(比如合適的源)。

你會注意到我們在全尺寸圖像中結合了傳入的樣式和 styles.imageOverlay 樣式。 這樣,通過絕對定位,圖像會掩蓋縮略版。

結果如下:

顯示縮略圖和全尺寸圖像

注意: 你會注意到縮略圖是相當像素化。 你可以把 blurRadius 屬性傳遞給縮略圖來模糊它。我做了一個屏幕截圖,這樣你就可以看到區別了(例如,我使用的是 blurRadius 為2的模型)。

不帶 blurRadius

帶 blurRadius

你還會注意到,如果我們不將 thumbnailSource 傳遞給 ProgressiveImage 組件,則一切正常,這意味著即使沒有縮略圖,我們也可以對所有遠程圖像使用此組件。

動畫過渡

最後我們要做的是平滑過渡到背景顏色,縮略圖和全尺寸圖像。 要做到這一點,我們將使用 React Native 中的 Animated 依賴。

一旦你導入了 Animated,你就需要用 Animated.image 替換 ProgressiveImage 中的 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。

// 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調用。

// 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 上找到。

原文地址:medium.com/react-native

請關注我們的公眾號: FENews


推薦閱讀:
相关文章