react-native-fast-image实现骨架屏:加载状态过渡效果

react-native-fast-image实现骨架屏:加载状态过渡效果

【免费下载链接】react-native-fast-image 🚩 FastImage, performant React Native image component. 【免费下载链接】react-native-fast-image 项目地址: https://gitcode.com/gh_mirrors/re/react-native-fast-image

在移动应用开发中,图片加载过程中的空白或闪烁问题会严重影响用户体验。本文将介绍如何使用react-native-fast-image(高性能React Native图片组件)实现骨架屏效果,通过平滑的加载状态过渡提升应用质感。

核心原理与实现基础

骨架屏(Skeleton Screen)是一种在内容加载过程中显示的占位UI,通过模拟最终内容的大致结构,减少用户等待感知。react-native-fast-image提供的加载状态回调和缓存控制能力,为实现骨架屏提供了关键支持。

关键API解析

react-native-fast-imagesrc/index.tsx定义了以下核心属性:

  • 加载状态回调onLoadStart(开始加载)、onProgress(加载进度)、onLoad(加载完成)、onError(加载失败)
  • 占位图支持defaultSource属性可设置加载过程中的默认图片
  • 缓存控制:通过cacheControl属性控制图片缓存策略,减少重复加载
// 组件属性定义(src/index.tsx 第83-131行)
export interface FastImageProps extends AccessibilityProps, ViewProps {
  source?: Source | ImageRequireSource;
  defaultSource?: ImageRequireSource; // 占位图
  onLoadStart?(): void; // 加载开始回调
  onProgress?(event: OnProgressEvent): void; // 进度回调
  onLoad?(event: OnLoadEvent): void; // 加载完成回调
  onError?(): void; // 加载失败回调
  // ...其他属性
}

进度监听示例

ReactNativeFastImageExample/src/ProgressExample.tsx展示了如何监听图片加载进度,这是实现骨架屏过渡动画的基础:

// 进度监听实现(ProgressExample.tsx 第32-51行)
<FastImage
  style={styles.image}
  source={{ uri: url }}
  onLoadStart={() => setState((s) => ({ ...s, start: Date.now() }))}
  onProgress={(e) => {
    const p = Math.round(100 * (e.nativeEvent.loaded / e.nativeEvent.total));
    setState((s) => ({ ...s, progress: p }));
  }}
  onLoad={() => setState((s) => ({ ...s, end: Date.now() }))}
/>

实现步骤

1. 基础骨架屏组件

以下是一个基础的骨架屏实现,通过状态管理控制骨架屏与图片的显示切换:

import React, { useState } from 'react';
import { View, StyleSheet } from 'react-native';
import FastImage from 'react-native-fast-image';

const SkeletonImage = ({ source, style }) => {
  const [isLoading, setIsLoading] = useState(true);

  return (
    <View style={[styles.container, style]}>
      {/* 骨架屏占位 */}
      {isLoading && <View style={[styles.skeleton, StyleSheet.absoluteFill]} />}
      
      {/* 实际图片 */}
      <FastImage
        style={StyleSheet.absoluteFill}
        source={source}
        onLoadStart={() => setIsLoading(true)}
        onLoad={() => setIsLoading(false)}
        onError={() => setIsLoading(false)}
        resizeMode={FastImage.resizeMode.cover}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    backgroundColor: '#e1e1e1',
    overflow: 'hidden',
  },
  skeleton: {
    backgroundColor: '#f2f2f2',
    // 添加骨架屏动画效果
  },
});

export default SkeletonImage;

2. 添加骨架屏动画

为骨架屏添加渐变动画效果,增强视觉体验:

import React, { useState, useEffect } from 'react';
import { View, StyleSheet, Animated } from 'react-native';
import FastImage from 'react-native-fast-image';

const SkeletonImage = ({ source, style }) => {
  const [isLoading, setIsLoading] = useState(true);
  const [animation] = useState(new Animated.Value(0));

  // 骨架屏动画
  useEffect(() => {
    if (isLoading) {
      Animated.loop(
        Animated.sequence([
          Animated.timing(animation, {
            toValue: 1,
            duration: 1000,
            useNativeDriver: true,
          }),
          Animated.timing(animation, {
            toValue: 0,
            duration: 1000,
            useNativeDriver: true,
          }),
        ])
      ).start();
    }
  }, [isLoading, animation]);

  // 计算渐变位置
  const translateX = animation.interpolate({
    inputRange: [0, 1],
    outputRange: [-100, 200],
  });

  return (
    <View style={[styles.container, style]}>
      {isLoading && (
        <Animated.View
          style={[
            styles.skeleton,
            StyleSheet.absoluteFill,
            {
              transform: [{ translateX }],
            },
          ]}
        />
      )}
      
      <FastImage
        style={StyleSheet.absoluteFill}
        source={source}
        onLoadStart={() => setIsLoading(true)}
        onLoad={() => setIsLoading(false)}
        onError={() => setIsLoading(false)}
        resizeMode={FastImage.resizeMode.cover}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    backgroundColor: '#e1e1e1',
    overflow: 'hidden',
  },
  skeleton: {
    backgroundColor: '#f2f2f2',
    background: 'linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%)',
    backgroundSize: '200% 100%',
  },
});

export default SkeletonImage;

3. 高级用法:结合进度显示

利用onProgress回调,实现带进度条的骨架屏:

import React, { useState } from 'react';
import { View, StyleSheet, Animated, Text } from 'react-native';
import FastImage from 'react-native-fast-image';

const ProgressSkeletonImage = ({ source, style }) => {
  const [isLoading, setIsLoading] = useState(true);
  const [progress, setProgress] = useState(0);

  return (
    <View style={[styles.container, style]}>
      {/* 骨架屏背景 */}
      {isLoading && (
        <>
          <View style={[styles.skeleton, StyleSheet.absoluteFill]} />
          {/* 进度条 */}
          <View style={styles.progressContainer}>
            <View 
              style={[styles.progressBar, { width: `${progress}%` }]} 
            />
          </View>
          <Text style={styles.progressText}>{Math.round(progress)}%</Text>
        </>
      )}
      
      {/* 实际图片 */}
      <FastImage
        style={StyleSheet.absoluteFill}
        source={source}
        onLoadStart={() => {
          setIsLoading(true);
          setProgress(0);
        }}
        onProgress={(e) => {
          const p = (e.nativeEvent.loaded / e.nativeEvent.total) * 100;
          setProgress(p);
        }}
        onLoad={() => setIsLoading(false)}
        onError={() => setIsLoading(false)}
        resizeMode={FastImage.resizeMode.cover}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    backgroundColor: '#e1e1e1',
    overflow: 'hidden',
  },
  skeleton: {
    backgroundColor: '#f2f2f2',
  },
  progressContainer: {
    position: 'absolute',
    bottom: 0,
    left: 0,
    right: 0,
    height: 3,
    backgroundColor: 'rgba(0,0,0,0.1)',
  },
  progressBar: {
    height: '100%',
    backgroundColor: '#007AFF',
  },
  progressText: {
    position: 'absolute',
    bottom: 5,
    right: 5,
    fontSize: 12,
    color: 'white',
    backgroundColor: 'rgba(0,0,0,0.5)',
    paddingHorizontal: 4,
    paddingVertical: 1,
    borderRadius: 2,
  },
});

export default ProgressSkeletonImage;

性能优化策略

1. 图片预加载

使用FastImage.preload方法预加载关键图片,减少骨架屏显示时间:

// 预加载图片(src/index.tsx 第248-249行)
FastImage.preload = (sources: Source[]) =>
  NativeModules.FastImageView.preload(sources);

// 使用示例
FastImage.preload([
  { uri: 'https://example.com/image1.jpg', priority: FastImage.priority.high },
  { uri: 'https://example.com/image2.jpg', priority: FastImage.priority.normal },
]);

2. 缓存策略优化

合理设置缓存策略,减少重复网络请求:

<FastImage
  source={{
    uri: 'https://example.com/image.jpg',
    cacheControl: FastImage.cacheControl.immutable, // 永久缓存
  }}
  // ...其他属性
/>

根据src/index.tsx第37-46行定义,缓存策略包括:

  • immutable:永久缓存,仅在URI变化时重新加载
  • web:遵循HTTP缓存头
  • cacheOnly:仅使用缓存,不发起网络请求

完整示例:图片网格中的骨架屏

结合ReactNativeFastImageExample/src/FastImageGrid.tsx的实现思路,以下是一个带骨架屏的图片网格组件:

import React from 'react';
import { FlatList, View, StyleSheet } from 'react-native';
import SkeletonImage from './SkeletonImage';

const IMAGE_URLS = [
  'https://example.com/image1.jpg',
  'https://example.com/image2.jpg',
  'https://example.com/image3.jpg',
  // ...更多图片URL
];

const ImageGridWithSkeleton = () => {
  return (
    <FlatList
      data={IMAGE_URLS}
      numColumns={2}
      keyExtractor={(item, index) => `${item}-${index}`}
      renderItem={({ item }) => (
        <View style={styles.gridItem}>
          <SkeletonImage
            source={{ uri: item }}
            style={styles.image}
          />
        </View>
      )}
    />
  );
};

const styles = StyleSheet.create({
  gridItem: {
    flex: 1,
    padding: 5,
  },
  image: {
    aspectRatio: 1, // 保持正方形
    borderRadius: 8,
  },
});

export default ImageGridWithSkeleton;

常见问题与解决方案

1. 骨架屏闪烁问题

问题:图片加载完成后,骨架屏消失时出现闪烁。

解决方案:使用opacity过渡动画实现平滑切换:

{isLoading ? (
  <Animated.View style={[styles.skeleton, { opacity: 1 }]} />
) : (
  <Animated.View style={[styles.skeleton, { opacity: 0 }]} />
)}

2. 列表滚动时骨架屏重绘

问题:在FlatList中快速滚动时,骨架屏频繁重绘。

解决方案:使用removeClippedSubviews优化,并为每个item设置固定尺寸:

<FlatList
  removeClippedSubviews={true}
  getItemLayout={(data, index) => ({
    length: ITEM_HEIGHT,
    offset: ITEM_HEIGHT * index,
    index,
  })}
  // ...其他属性
/>

总结

使用react-native-fast-image实现骨架屏,关键在于利用其提供的加载状态回调和占位图机制,结合React Native的动画API创建平滑过渡效果。通过合理的缓存策略和预加载优化,可以进一步提升用户体验。

完整实现代码可参考项目示例:

通过这种方式实现的骨架屏,能够有效减少用户等待焦虑,提升应用的专业感和流畅度。

【免费下载链接】react-native-fast-image 🚩 FastImage, performant React Native image component. 【免费下载链接】react-native-fast-image 项目地址: https://gitcode.com/gh_mirrors/re/react-native-fast-image

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值