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

如何在不影响控件的情况下水平翻转HTML5摄像头视频流?

解决摄像头流水平镜像且不影响控件的方案

不用Canvas的实现方法

核心思路是分离视频显示和控件:用一个视频元素显示翻转后的画面(隐藏控件),另一个视频元素只显示控件(隐藏画面),通过JS同步两者的播放状态。

代码示例

HTML

<div class="video-wrapper">
  <!-- 用于显示翻转后的视频,隐藏控件 -->
  <video id="video-display" autoplay playsinline controls="false"></video>
  <!-- 用于提供原生控件,隐藏视频画面 -->
  <video id="video-controls" autoplay playsinline controls muted></video>
</div>

CSS

.video-wrapper {
  position: relative;
  display: inline-block;
}
#video-display {
  /* 水平翻转视频内容 */
  transform: scaleX(-1);
}
#video-controls {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  /* 默认隐藏控件,hover时显示 */
  opacity: 0;
  transition: opacity 0.2s;
}
#video-controls:hover {
  opacity: 1;
}

JavaScript

const displayVideo = document.getElementById('video-display');
const controlsVideo = document.getElementById('video-controls');

// 获取摄像头流并同时绑定到两个视频元素
navigator.mediaDevices.getUserMedia({ video: true })
  .then(stream => {
    displayVideo.srcObject = stream;
    controlsVideo.srcObject = stream;
  })
  .catch(err => console.error('获取流失败:', err));

// 同步播放/暂停状态
controlsVideo.addEventListener('play', () => displayVideo.play());
controlsVideo.addEventListener('pause', () => displayVideo.pause());

// 同步音量设置
controlsVideo.addEventListener('volumechange', () => {
  displayVideo.volume = controlsVideo.volume;
});

// 同步播放进度
controlsVideo.addEventListener('timeupdate', () => {
  displayVideo.currentTime = controlsVideo.currentTime;
});

使用Canvas的实现方法

如果需要对视频做更多自定义处理(比如叠加滤镜、裁剪),可以用Canvas绘制翻转后的视频流,控件部分同样通过叠加原生视频元素实现。

代码示例

HTML

<div class="canvas-video-wrapper">
  <!-- 用于显示翻转后的视频 -->
  <canvas id="video-canvas"></canvas>
  <!-- 隐藏的视频元素,用于获取流数据 -->
  <video id="hidden-video" autoplay playsinline muted></video>
  <!-- 用于提供原生控件 -->
  <video id="canvas-controls" autoplay playsinline controls></video>
</div>

CSS

.canvas-video-wrapper {
  position: relative;
  display: inline-block;
}
#video-canvas {
  display: block;
}
#hidden-video {
  display: none;
}
#canvas-controls {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  transition: opacity 0.2s;
}
#canvas-controls:hover {
  opacity: 1;
}

JavaScript

const canvas = document.getElementById('video-canvas');
const ctx = canvas.getContext('2d');
const hiddenVideo = document.getElementById('hidden-video');
const controlsVideo = document.getElementById('canvas-controls');

navigator.mediaDevices.getUserMedia({ video: true })
  .then(stream => {
    hiddenVideo.srcObject = stream;
    controlsVideo.srcObject = stream;

    // 视频元数据加载完成后初始化Canvas尺寸
    hiddenVideo.addEventListener('loadedmetadata', () => {
      canvas.width = hiddenVideo.videoWidth;
      canvas.height = hiddenVideo.videoHeight;
      // 开始循环绘制视频帧
      drawFlippedFrame();
    });
  })
  .catch(err => console.error('获取流失败:', err));

function drawFlippedFrame() {
  // 保存当前上下文状态,避免影响后续绘制
  ctx.save();
  // 水平翻转画布坐标系
  ctx.scale(-1, 1);
  // 绘制视频帧(因为坐标系翻转,所以X轴取负宽度)
  ctx.drawImage(hiddenVideo, -canvas.width, 0, canvas.width, canvas.height);
  // 恢复上下文状态
  ctx.restore();

  // 循环调用,实现实时绘制
  requestAnimationFrame(drawFlippedFrame);
}

// 同步控件状态(可选,同一流状态会自动同步,但监听更保险)
controlsVideo.addEventListener('play', () => hiddenVideo.play());
controlsVideo.addEventListener('pause', () => hiddenVideo.pause());

关键说明

直接对<video>元素应用transform: scaleX(-1)会连同控件一起翻转,导致控件操作反向(比如播放按钮显示为反向),所以必须分离视频显示和控件层。两种方案的核心都是用一个元素负责显示翻转后的画面,另一个元素提供正常的原生控件,通过状态同步保证功能正常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 03:23:22