You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何判断图片是否加载完成?React轮播组件加载状态实现

为支持children的轮播组件实现加载状态判断

问题场景

你开发了支持children属性的Carroussel轮播组件,当前以图片作为子元素,轮播逻辑通过pageIndex切换DOM中显示的子元素。现在需要为图片加载过程添加加载状态,但不清楚如何将onload事件与轮播组件关联,以及如何判断元素是否加载完成。

解决方案

方法1:针对当前显示元素的加载状态监听

在Carroussel组件内部,为每个子元素(图片)添加加载监听,维护已加载的索引集合,根据当前pageIndex判断是否显示加载状态:

import { useState, cloneElement } from 'react';
import { AnimatePresence, motion } from 'framer-motion';

const Carroussel = ({ children, pageIndex, direction, ...rest }) => {
  // 用Set存储已加载的元素索引
  const [loadedIndices, setLoadedIndices] = useState(new Set());

  // 处理元素加载完成的回调
  const handleLoad = (index) => {
    setLoadedIndices(prev => new Set([...prev, index]));
  };

  // 遍历子元素,为图片添加onload/onerror监听
  const processedChildren = React.Children.map(children, (child, index) => {
    if (child.type === 'img') {
      return cloneElement(child, {
        onLoad: () => handleLoad(index),
        // 可选:处理加载失败的情况,标记为已加载或单独维护失败状态
        onError: () => handleLoad(index)
      });
    }
    // 如果是其他媒体元素(如video),可添加对应加载事件监听
    if (child.type === 'video') {
      return cloneElement(child, {
        onLoadedData: () => handleLoad(index)
      });
    }
    return child;
  });

  const currentChild = processedChildren[pageIndex];
  const isCurrentLoaded = loadedIndices.has(pageIndex);

  return (
    <AnimatePresence mode="popLayout" initial={false} custom={direction}>
      <motion.div {...rest}>
        {/* 显示加载状态或当前元素 */}
        {!isCurrentLoaded ? (
          <div className="carousel-loading">加载中...</div>
        ) : (
          currentChild
        )}
      </motion.div>
    </AnimatePresence>
  );
};

方法2:预加载所有子元素(适合图片数量不多的场景)

如果需要等所有图片加载完成后再显示轮播,可以在组件挂载时预加载所有图片,维护全局加载状态:

import { useState, useEffect } from 'react';
import { AnimatePresence, motion } from 'framer-motion';

const Carroussel = ({ children, pageIndex, direction, ...rest }) => {
  const [allLoaded, setAllLoaded] = useState(false);
  const [loadedCount, setLoadedCount] = useState(0);

  useEffect(() => {
    const imgChildren = React.Children.toArray(children).filter(child => child.type === 'img');
    const total = imgChildren.length;

    if (total === 0) {
      setAllLoaded(true);
      return;
    }

    // 预加载每张图片
    const handleLoad = () => {
      setLoadedCount(prev => {
        const newCount = prev + 1;
        if (newCount === total) setAllLoaded(true);
        return newCount;
      });
    };

    imgChildren.forEach(child => {
      const img = new Image();
      img.src = child.props.src;
      img.onload = handleLoad;
      img.onerror = handleLoad;
    });
  }, [children]);

  return (
    <div {...rest}>
      {!allLoaded ? (
        <div className="carousel-global-loading">所有图片加载中...</div>
      ) : (
        <AnimatePresence mode="popLayout" initial={false} custom={direction}>
          <motion.div>
            {React.Children.toArray(children)[pageIndex]}
          </motion.div>
        </AnimatePresence>
      )}
    </div>
  );
};

针对自定义子组件的加载判断

如果子元素是自定义组件,可通过props传递回调函数,让子组件在加载完成时触发:

// 自定义媒体子组件
const CustomMedia = ({ onLoad }) => {
  useEffect(() => {
    // 模拟加载完成逻辑,比如资源加载完成后调用onLoad
    const loadComplete = () => {
      onLoad();
    };
    // 实际场景中替换为对应资源的加载事件,如音频的oncanplay
    setTimeout(loadComplete, 1000);
  }, [onLoad]);

  return <div>自定义媒体内容</div>;
};

// 在Carroussel中处理
const processedChildren = React.Children.map(children, (child, index) => {
  // 判断子组件是否接受onLoad回调
  if (child.props.onLoad) {
    return cloneElement(child, {
      onLoad: () => handleLoad(index)
    });
  }
  return child;
});

内容的提问来源于stack exchange,提问作者Tim Overbeek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 10:42:08