如何判断图片是否加载完成?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
相关产品推荐
相关产品推荐

