1. 问题背景

在使用 React Native 开发时,如果列表中的图片采用动态尺寸,并且使用 contain 模式进行缩放,同时配合 FlashList 渲染,可能会遇到一个比较隐蔽的问题:图片被重复加载两次,导致外层容器在计算尺寸、给图片添加圆角时出现明显的抖动。

2. 问题原因分析

这个问题的根源在于 FlashList 的渲染机制。FlashList 为了优化长列表的性能,会对超出可视区域的列表项进行回收和复用。当列表项被回收后重新进入可视区域时,图片组件可能会被重新挂载,从而触发图片的重新加载。

具体来说,当图片使用 contain 模式时,图片的实际渲染尺寸需要等图片加载完成后才能确定。如果图片被重复加载,外层容器在等待图片尺寸的过程中,会先按默认尺寸渲染,等图片加载完成后再调整尺寸。这个尺寸调整的过程,就会导致圆角出现抖动。

3. 解决方案思路

解决这个问题的核心思路是:避免外层容器在图片加载过程中反复调整尺寸。具体做法是:

  • 把原先包裹图片的外层容器移到兄弟层,不再直接包裹图片。
  • 在兄弟层上加一个很重的纯色 shadow。
  • 利用纯色 shadow“切”出一个圆角效果。

4. 具体实现步骤

下面通过代码示例来说明具体的实现方式。

4.1 原来的写法(有问题)

// 原来的写法:外层容器直接包裹图片
function ImageItem({ uri }) {
  return (
    <View style={styles.container}>
      <Image
        source={{ uri }}
        style={styles.image}
        resizeMode="contain"
        onLoad={() => console.log('图片加载')}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    borderRadius: 12,
    overflow: 'hidden',
    backgroundColor: '#f0f0f0',
  },
  image: {
    width: '100%',
    height: 200,
  },
});

4.2 优化后的写法(解决问题)

// 优化后的写法:外层容器移到兄弟层,用 boxShadow 切圆角
function ImageItem({ uri }) {
  return (
    <View style={styles.wrapper}>
      {/* 兄弟层:负责圆角 boxShadow */}
      <View style={styles.shadow} />
      {/* 图片层:不再被圆角容器包裹 */}
      <Image
        source={{ uri }}
        style={styles.image}
        resizeMode="contain"
        onLoad={() => console.log('图片加载')}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  wrapper: {
    position: 'relative',
  },
  shadow: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
    borderRadius: 12,
    boxShadow: '0 0 0 30px #f0f0f0', // 很重的纯色 boxShadow
  },
  image: {
    width: '100%',
    height: 200,
  },
});

5. 原理说明

优化后的写法之所以能解决抖动问题,是因为:

  • 图片不再被带有 borderRadiusoverflow: hidden 的容器包裹,图片加载过程中不会触发容器的尺寸重算。
  • 圆角效果由兄弟层的纯色 shadow 实现,shadow 层不依赖图片的加载状态,因此不会出现抖动。
  • 图片加载完成后,直接覆盖在 shadow 层上方,视觉上仍然呈现圆角效果。

6. 注意事项

  • 纯色 shadow 的颜色需要与页面背景色一致,否则会看到明显的色块。
  • 如果图片本身有透明区域,这种方案可能不适用,因为透明区域会露出背景色。
  • 如果列表项数量不多,也可以考虑使用 FlatList 替代 FlashList,从根源上避免回收复用带来的重复加载问题。

7. 总结

通过把外层容器移到兄弟层,并利用纯色 shadow“切”出圆角,可以有效解决 FlashList 中图片重复加载导致的圆角抖动问题。这种方案实现简单、性能开销小,适合在动态尺寸图片场景下使用。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐