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

React视频组件加载完成前如何显示骨架屏/占位符并解决尺寸跳变问题?

我来帮你搞定这个问题!要实现视频加载完成前显示骨架屏/占位符,核心是利用HTML5 video元素的加载事件,结合React的状态管理来控制骨架屏的显示与隐藏。下面是具体的实现方案:

改进后的React视频组件
import { useState, useRef, useEffect } from 'react';

const Video = ({ src, type, skeletonStyle }) => {
  const videoRef = useRef();
  // 控制加载状态:初始为true,视频加载完成后切换为false
  const [isLoading, setIsLoading] = useState(true);
  // 存储视频实际尺寸,避免骨架屏和最终视频尺寸差异导致的布局跳动
  const [videoSize, setVideoSize] = useState({ width: '100%', height: '280px' });

  useEffect(() => {
    const videoElement = videoRef.current;
    if (!videoElement) return;

    // 监听视频元数据加载完成事件:此时已获取到视频的宽高、时长等核心信息
    const handleMetadataLoaded = () => {
      setVideoSize({
        width: videoElement.videoWidth,
        height: videoElement.videoHeight
      });
      setIsLoading(false);
    };

    // 监听视频可播放事件:确保视频已加载足够数据可以正常播放
    const handleCanPlay = () => {
      setIsLoading(false);
    };

    // 监听加载错误事件:避免视频加载失败时骨架屏一直显示
    const handleLoadError = () => {
      console.error('视频加载失败,请检查地址或格式');
      setIsLoading(false);
    };

    // 绑定事件监听
    videoElement.addEventListener('loadedmetadata', handleMetadataLoaded);
    videoElement.addEventListener('canplay', handleCanPlay);
    videoElement.addEventListener('error', handleLoadError);

    // 组件卸载时清理事件监听
    return () => {
      videoElement.removeEventListener('loadedmetadata', handleMetadataLoaded);
      videoElement.removeEventListener('canplay', handleCanPlay);
      videoElement.removeEventListener('error', handleLoadError);
    };
  }, [src, type]);

  return (
    <>
      {/* 加载中显示骨架屏 */}
      {isLoading ? (
        <div 
          style={{
            width: videoSize.width,
            height: videoSize.height,
            backgroundColor: '#f5f5f5',
            borderRadius: '8px',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            color: '#999',
            ...skeletonStyle // 允许外部自定义骨架屏样式
          }}
        >
          视频加载中...
        </div>
      ) : (
        {/* 加载完成显示视频 */}
        <video 
          ref={videoRef} 
          autoPlay 
          loop 
          controls
          style={{
            width: videoSize.width,
            height: videoSize.height,
            borderRadius: '8px'
          }}
        >
          <source src={src} type={type} />
        </video>
      )}
    </>
  );
};

export default Video;
关键细节说明
  • 状态与事件联动:

    • loadedmetadata事件是核心:它会在视频的元数据(宽高、时长等)加载完成后触发,此时我们能拿到视频的真实尺寸,完美解决“默认播放器和实际视频尺寸差异”的问题。
    • canplay作为补充:确保视频已经加载足够数据可以正常播放,避免元数据加载完成但视频仍无法播放的情况。
    • error事件兜底:处理视频加载失败的异常场景,防止骨架屏一直挂着。
  • 布局优化:
    通过videoSize状态存储视频真实尺寸,让骨架屏和最终视频的尺寸完全一致,彻底消除布局跳动的问题。如果提前知道视频的固定尺寸,也可以直接传入组件,不用等待元数据加载。

  • 自定义扩展:
    组件支持传入skeletonStyle属性,你可以根据自己的设计需求自定义骨架屏的样式,比如添加渐变加载动画、调整背景色等。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 19:13:05