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

React视频适配容器+圆角实现问题及JS方案异常排查

解决方案:动态适配视频容器并解决videoWidth/videoHeight为0的问题

核心问题分析

你遇到的videoWidth/videoHeight始终为0的原因是:视频元数据尚未加载完成时就执行了尺寸计算逻辑;同时当前代码没有监听视频流的动态尺寸变化(比如getUserMedia的流分辨率调整),也没处理容器尺寸变化的场景。

修正后的代码实现

const DisplayVideo = ({
  videoSrc = null,
  // other props
}) => {
  const videoRef = useRef(null);
  const borderRef = useRef(null);
  const [fillMode, setFillMode] = useState(''); // fill-width or fill-height

  // 计算并设置填充模式的核心函数
  const updateFillMode = () => {
    const video = videoRef.current;
    const border = borderRef.current;
    if (!video || !border) return;

    // 确保视频元数据已加载,避免width/height为0
    if (video.videoWidth === 0 || video.videoHeight === 0) return;

    const vAspectRatio = video.videoWidth / video.videoHeight;
    const bAspectRatio = border.clientWidth / border.clientHeight;
    setFillMode(vAspectRatio > bAspectRatio ? 'fill-width' : 'fill-height');
  };

  useEffect(() => {
    const video = videoRef.current;
    if (!video || !videoSrc) return;

    // 更新视频源
    if (video.srcObject !== videoSrc) {
      video.srcObject = videoSrc;
    }

    // 监听视频元数据加载完成事件
    video.addEventListener('loadedmetadata', updateFillMode);
    // 监听视频流尺寸变化事件(适配getUserMedia动态分辨率)
    video.addEventListener('resize', updateFillMode);
    // 监听窗口resize,处理容器尺寸变化
    window.addEventListener('resize', updateFillMode);

    video.play();

    // 组件卸载时移除监听
    return () => {
      video.removeEventListener('loadedmetadata', updateFillMode);
      video.removeEventListener('resize', updateFillMode);
      window.removeEventListener('resize', updateFillMode);
    };
  }, [videoSrc]);

  // 额外监听容器尺寸变化(如果容器尺寸可能通过非窗口resize改变)
  useEffect(() => {
    const border = borderRef.current;
    if (!border) return;

    // 使用ResizeObserver监听容器尺寸变化
    const resizeObserver = new ResizeObserver(updateFillMode);
    resizeObserver.observe(border);

    return () => {
      resizeObserver.unobserve(border);
    };
  }, []);

  return (
    <div className="display-video-wrapper" ref={borderRef}>
      <video className={`display-video ${fillMode}`} ref={videoRef} />
      {/* other stuff */}
    </div>
  );
};

const mapStateToProps = (state, ownProps) => {
  // stuff
  return { videoSrc: remoteVideoSrcs[mid] };
};

export default connect(mapStateToProps)(DisplayVideo);

关键修改点说明

  • 提取updateFillMode独立函数,复用尺寸计算逻辑
  • 监听视频的loadedmetadata事件:确保视频元数据加载完成后再获取videoWidth/videoHeight
  • 监听视频的resize事件:适配getUserMedia流的动态分辨率变化(比如摄像头切换、网络带宽调整导致的分辨率缩放)
  • 使用ResizeObserver监听容器尺寸变化:覆盖容器通过非窗口resize改变尺寸的场景(比如父元素布局变化)
  • 清理事件监听:避免组件卸载后内存泄漏
  • 优化useEffect依赖:仅在videoSrc变化时重新绑定视频相关监听

配套CSS示例

确保你的CSS能配合fillMode生效:

.display-video-wrapper {
  border-radius: 12px;
  overflow: hidden; /* 关键:确保视频圆角生效 */
  position: relative;
  width: 100%;
  height: 100%;
}

.display-video {
  display: block;
  object-fit: cover; /* 配合fillMode,确保视频填满容器且保持比例 */
}

.display-video.fill-width {
  width: 100%;
  height: auto;
}

.display-video.fill-height {
  height: 100%;
  width: auto;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 11:14:56