React Native предоставляет "из коробки" Image.prefetch, который может быть полезен для предварительной загрузки изображений, но в некоторых случаях этого недостаточно.

Вам действительно нужно, чтобы изображение присутствовало локально, чтобы избежать эффекта мерцания при его загрузке. Это также позволяет лучше контролировать кеш изображений. В приведенном ниже примере вы видите разницу между использованием Image.prefetch кэша изображений и локальным обслуживанием изображения.

Вам также может потребоваться отобразить ваше изображение в другом компоненте, отличном от <Image>. Например, в приведенном ниже примере мы используем <SVGImage>, который работает только для локальных путей и / или URI данных.

Используйте ExpoKit

Поэтому вам нужно хранить изображения локально. Если вы уже используете expo, из коробки есть Библиотека файловой системы. Если ваш проект отключен, кажется, что библиотека react-native-fetch-blob потеряла своего основного участника. В прошлый раз, когда я его использовал, возникли некоторые проблемы. Возможно, стоит добавить ExpoKit в качестве зависимости вашего проекта, даже если вы оторваны с самого начала. ExpoKit включает множество других замечательных компонентов, в том числе BlurView, которые можно анимировать в исходном виде, если вы также хотите реализовать прогрессивную загрузку изображений.

Работа с параллелизмом на Android

Многие компоненты могут одновременно отображать одно и то же изображение. И выполнение Filesystem.exists(localURI) операции на Android вернет истину, даже если загрузка файла не завершена. Это означает, что вам необходимо реализовать шаблон наблюдателя, чтобы загружать каждое изображение только один раз и получать уведомление, когда загрузка изображения завершена. В response-native-expo-image-cache API выглядит так:

import {CacheManager} from "react-native-expo-image-cache";

// Remote URI
const {uri} = this.props;
CacheManager.cache(uri, localURI => this.setState({ uri: localURI }));

Ниже представлена ​​реализация наблюдателя:

static async cache(uri: string, listener: Listener): Promise<void> {
    const {path, exists} = await getCacheEntry(uri);
    // Is the image is already downloading, we just listen
    if (isDownloading(uri)) {
        addListener(uri, listener);
    // If it's not downloading and it exists, we serve it
    } else if (exists) {
    listener(path);
    // Else, we download the image and notify everyone when done
    } else {
      addListener(uri, listener);
      await FileSystem.downloadAsync(uri, path);
      notifyAll(uri, path);
      unsubscribe(uri);
    }
}

Прогрессивная загрузка изображений

Каждый раз, когда URI изображения сохраняется в базе данных, сохраняйте его предварительный просмотр в формате base64 вместе с ним. Это позволит вам загружать изображение очень плавно. Ниже приведен пример:

{
  preview: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
  uri: "https://firebasestorage.googleapis.com/v0/b/react-native-e.appspot.com/o/b47b03a1e22e3f1fd884b5252de1e64a06a14126.png?alt=media&token=d636c423-3d94-440f-90c1-57c4de921641"
}

Теперь вы можете сразу отобразить размытую версию превью и уменьшить размытие до 0 при загрузке полной версии изображения. В iOS <BlurView> от ExpoKit удобно поддерживает собственный драйвер анимации. Однако Android по умолчанию не использует анимированный вид непрозрачности. Я сделал запрос на вытягивание к BlurView.android.js, чтобы протестировать воду и выяснить, будет ли команда открыта для реализации <BlurView>, которая была бы более симметричной на обеих платформах. А пока вы можете реализовать свои собственные. Вот как это выглядит в response-native-expo-image-cache:

// intensity is an Animated.Value
const opacity = intensity.interpolate({
    inputRange: [0, 100],
    outputRange: [0, 1]
});
{
    Platform.OS === "ios" && (
        <AnimatedBlurView
            tint="dark"
            style={computedStyle}
            {...{intensity}}
           />
    )
}
{
    Platform.OS === "android" && (
        <Animated.View
            style={[computedStyle, { backgroundColor: "rgba(0, 0, 0, 0.5)", opacity }]}
        />
    )
}

В ‹Image› есть серьезная ошибка.

"Вот этот". У вас может возникнуть соблазн установить URI в состоянии вашего компонента изображения: установите его на URI данных предварительного просмотра, а затем на полное изображение при загрузке. Проблема в том, что иногда изображение не обновляется. Вы можете использовать key для принудительного обновления, но оно будет мерцать. Решение? Наложите полное изображение на предварительный просмотр. Опять же, вот как это выглядит в response-native-expo-image-cache:

{
    // If show the preview if it exists
    hasPreview && (
        <RNImage
        source={{ uri: preview }}
        resizeMode="cover"
        style={computedStyle}
    />
  )
}
{
    // If the image is loaded, we show it on top
    // this.onLoadEnd is used to start the deblurring animation
    (uri && uri !== preview) && (
        <RNImage
            source={{ uri }}
        resizeMode="cover"
        style={computedStyle}
        onLoadEnd={this.onLoadEnd}
    />
    )
}

Это все, ребята!

Мы никогда не закончим говорить об изображениях в React Native. Это мой список 5 лучших. Мне не хватает каких-то предметов? Пожалуйста, дайте знать, а пока удачного взлома 🎉

Ищете красивые комплекты пользовательского интерфейса? Я реализую все экраны и компоненты из Sketch Elements в React Native. Вы можете получить его здесь". Я также публикую сеансы программирования в реальном времени и обучающие программы на моем канале Youtube.