Kurs React Native · Moduł 6: Zdjęcia, kamera i dźwięk

expo-image - Optymalne wyświetlanie obrazów

2 min czytania ·
W tej lekcji6

Witaj w Kryształowej Galerii! Nauczysz się obsługiwać multimedia w aplikacjach mobilnych.

Instalacja expo-image

npx expo install expo-image

Podstawowe użycie

import { Image } from 'expo-image';
import { StyleSheet, View } from 'react-native';

function ImageGallery() {
  return (
    <View style={styles.container}>
      {/* Obraz z URL */}
      <Image
        source={{ uri: 'https://atlantyda.app/images/crystal.jpg' }}
        style={styles.image}
        contentFit="cover"
        transition={300}
      />

      {/* Obraz lokalny */}
      <Image
        source={require('@/assets/images/pearl.png')}
        style={styles.image}
        contentFit="contain"
      />

      {/* Z placeholderem i blur */}
      <Image
        source={{ uri: 'https://atlantyda.app/images/large.jpg' }}
        placeholder={blurhash}
        style={styles.image}
        contentFit="cover"
        transition={500}
      />
    </View>
  );
}

const blurhash = 'L6PZfSi_.AyE_3t7t7R**0o#DgR4';

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  image: {
    width: '100%',
    height: 200,
    borderRadius: 12,
  },
});

contentFit (dawniej resizeMode)

// Różne tryby dopasowania
<Image source={source} contentFit="cover" />   // Wypełnia, może przycinać
<Image source={source} contentFit="contain" /> // Cały widoczny, może być padding
<Image source={source} contentFit="fill" />    // Rozciąga do wymiarów
<Image source={source} contentFit="none" />    // Oryginalne wymiary
<Image source={source} contentFit="scale-down" /> // contain lub none (mniejszy)

Blurhash i placeholder

import { Image } from 'expo-image';

// Blurhash - kompaktowy placeholder
const blurhash = 'L6PZfSi_.AyE_3t7t7R**0o#DgR4';

function OptimizedImage() {
  return (
    <Image
      source={{ uri: 'https://example.com/image.jpg' }}
      placeholder={blurhash}
      placeholderContentFit="cover"
      style={styles.image}
      transition={500}
    />
  );
}

Cachowanie

import { Image } from 'expo-image';

// Ustawienia cache
<Image
  source={{ uri: imageUrl }}
  cachePolicy="memory-disk" // memory, disk, memory-disk, none
  style={styles.image}
/>

// Prefetching
import { Image } from 'expo-image';

async function preloadImages() {
  const urls = [
    'https://example.com/image1.jpg',
    'https://example.com/image2.jpg',
  ];

  await Promise.all(urls.map((url) => Image.prefetch(url)));
}

Lista obrazów z FlatList

interface ImageItem {
  id: string;
  uri: string;
  title: string;
}

function ImageList() {
  const [images, setImages] = useState<ImageItem[]>([]);

  return (
    <FlatList
      data={images}
      numColumns={2}
      keyExtractor={(item) => item.id}
      renderItem={({ item }) => (
        <Pressable style={styles.imageCard}>
          <Image
            source={{ uri: item.uri }}
            style={styles.gridImage}
            contentFit="cover"
            transition={200}
          />
          <Text style={styles.imageTitle}>{item.title}</Text>
        </Pressable>
      )}
    />
  );
}

expo-image jest znacznie wydajniejszy niż standardowy Image z React Native!

Kod do tej lekcji: App.tsx
import React, { useState } from 'react';
import { View, Text, FlatList, Pressable, StyleSheet } from 'react-native';
import { Image } from 'expo-image';

interface ImageItem {
  id: string;
  uri: string;
  title: string;
}

export default function App() {
  const [images] = useState<ImageItem[]>([
    { id: '1', uri: 'https://picsum.photos/200?1', title: 'Kryształ' },
    { id: '2', uri: 'https://picsum.photos/200?2', title: 'Perła' },
  ]);

  return (
    <FlatList
      data={images}
      numColumns={2}
      keyExtractor={(item) => item.id}
      contentContainerStyle={styles.grid}
      renderItem={({ item }) => (
        <Pressable style={styles.card}>
          {/* TODO: dodaj <Image> z expo-image, który:
              - ma source={{ uri: item.uri }}
              - korzysta ze stylu styles.image
              - ma contentFit="cover"
              - ma transition={200} dla płynnego pojawiania się */}

          <Text style={styles.title}>{item.title}</Text>
        </Pressable>
      )}
    />
  );
}

const styles = StyleSheet.create({
  grid: { padding: 8 },
  card: { flex: 1, margin: 4 },
  image: {
    width: '100%',
    height: 150,
    borderRadius: 12,
  },
  title: {
    color: '#aed6f1',
    textAlign: 'center',
    marginTop: 6,
  },
});

Hasła z dokumentacji w tej lekcji

Widzisz błąd w tej lekcji?

Sprawdź się

Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.

  1. 1. Która wartość contentFit wypełnia kontener, potencjalnie przycinając obraz?

  2. 2. Do czego służy blurhash w expo-image?

Zadania praktyczne w grze

  • Układanie w poziomie

    Ułóż prefetching obrazu:

  • Edytor kodu

    Uzupełnij kod FlatList z expo-image.

Przydatne artykuły