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

如何为Angular自定义相机的video元素设置竖屏全高模式

Angular自定义相机竖屏拍摄落地方案

问题核心原因

仅通过CSS旋转video元素属于视觉层面的调整,摄像头输出的原始媒体流方向没有改变,调用drawImage取帧生成图片时,读取的是原始流的横屏画面,自然输出横屏结果。
移动端后置摄像头传感器本身多为横向安装,部分机型即使指定了摄像头参数,依然会默认输出横屏流,需要从流配置、帧校正两个层面处理。


分步实现

1. 初始化摄像头时强制指定竖屏流约束

调用getUserMedia时不要只传facingMode: 'environment',要明确添加竖屏比例、尺寸约束,尽可能让摄像头直接输出竖屏流:

async initCustomCamera() {
  const cameraConstraints = {
    video: {
      facingMode: { ideal: 'environment' }, // 优先调用后置摄像头
      aspectRatio: { ideal: 9 / 16 }, // 强制要求9:16竖屏比例
      width: { ideal: 1080 }, // 竖屏短边理想宽度
      height: { ideal: 1920 }, // 竖屏长边理想高度
    },
    audio: false
  };
  // 绑定流到video元素
  this.mediaStream = await navigator.mediaDevices.getUserMedia(cameraConstraints);
  const videoEl: HTMLVideoElement = this.videoRef.nativeElement;
  videoEl.srcObject = this.mediaStream;
  await videoEl.play();
}

2. 元数据加载时判断是否需要旋转校正

监听video的loadedmetadata事件,读取流的实际宽高,判断是否为横屏输出的流:

// 绑定到video的loadedmetadata事件
onVideoLoaded() {
  const videoEl: HTMLVideoElement = this.videoRef.nativeElement;
  const streamWidth = videoEl.videoWidth;
  const streamHeight = videoEl.videoHeight;
  // 流宽度大于高度说明是横屏输出,拍摄时需要旋转校正
  this.needRotateFrame = streamWidth > streamHeight;
  this.streamWidth = streamWidth;
  this.streamHeight = streamHeight;
}

3. 拍摄时通过Canvas校正方向输出竖屏图片

不要直接截取video画面,通过Canvas做帧绘制,需要旋转时做坐标变换,保证输出的是完整竖屏画面:

async captureIdCardPhoto() {
  const videoEl: HTMLVideoElement = this.videoRef.nativeElement;
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');

  if (this.needRotateFrame) {
    // 横屏流转竖屏:canvas宽高对调
    canvas.width = this.streamHeight;
    canvas.height = this.streamWidth;
    // 坐标变换:原点移到画布中心,旋转90度后绘制画面
    ctx.translate(canvas.width / 2, canvas.height / 2);
    ctx.rotate(Math.PI / 2);
    ctx.drawImage(
      videoEl,
      -this.streamWidth / 2,
      -this.streamHeight / 2,
      this.streamWidth,
      this.streamHeight
    );
  } else {
    // 原生竖屏流直接绘制
    canvas.width = this.streamWidth;
    canvas.height = this.streamHeight;
    ctx.drawImage(videoEl, 0, 0, canvas.width, canvas.height);
  }

  // 导出最终竖屏图片,可直接用于上传、识别
  return new Promise<Blob>((resolve) => {
    canvas.toBlob((blob) => {
      resolve(blob);
    }, 'image/jpeg', 0.92);
  });
}

4. 预览层CSS适配

给video元素加适配样式,保证预览画面和最终输出画面比例一致,叠加身份证参考线时不会出现错位:

.camera-preview {
  width: 100%;
  height: 100vh;
  object-fit: cover; /* 画面铺满容器不拉伸 */
}
/* 身份证参考线直接用绝对定位叠加在video上层即可 */
.id-card-guide {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 80vw;
  aspect-ratio: 85.6 / 54; /* 标准身份证长宽比 */
  border: 2px dashed #fff;
  border-radius: 8px;
}

避坑提示

  • 不要仅靠transform: rotate(90deg)旋转video元素做预览,这个操作不会改变原始媒体流方向,拍摄时依然会得到横屏图片
  • 部分安卓机型需要在HTTPS环境下才能正常调用后置摄像头,本地调试可以用localhost
  • 拍摄完成后如果需要在页面展示结果,给img标签加image-orientation: from-image样式,避免部分浏览器自动旋转图片导致方向错误

内容的提问来源于stack exchange,提问作者Titus Sutio Fanpula

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 21:54:29