fix: 拍照提问摄像头画面倒置、下拉菜单换成前后切换按钮
真机拍照实测画面上下颠倒(Mac mini 的苹果标志倒着)。CameraCapture.vue 的 <video> 加 transform: rotate(180deg) 转正预览;capture() 画到 canvas 时同步做相同的 180° 旋转(translate 到右下角再 rotate(Math.PI)), 不然预览是正的、实际拍下来的照片还是倒的。 摄像头选择从下拉菜单换成一个悬浮在预览右上角的圆形切换按钮 (switchCamera(),在设备列表里循环切换,只有 >1 个摄像头时才显示)。 默认摄像头沿用之前"取设备列表第一个"的逻辑不变——真机实测这个正好是 朝外拍摄的那颗(另一颗是朝向操作者的),语义上更接近用户要的默认 "后置摄像头",暂不改动。 真机验证:预览转正、切换按钮能真实切到另一颗摄像头且画面同样是正的、 拍照后的照片方向跟预览一致(不会出现预览正但存图倒的情况)。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
8054fcdad1
commit
0d19be58c3
@ -72,10 +72,23 @@ function capture(): void {
|
||||
canvas.height = video.videoHeight
|
||||
const ctx = canvas.getContext('2d')
|
||||
if (!ctx) return
|
||||
// 这台设备的摄像头画面是倒置的(见下方 .cam-preview-video 的 CSS 旋转),
|
||||
// 拍出来的照片也要同步转正,不然预览是正的、存下来的图是倒的
|
||||
ctx.save()
|
||||
ctx.translate(canvas.width, canvas.height)
|
||||
ctx.rotate(Math.PI)
|
||||
ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
|
||||
ctx.restore()
|
||||
capturedDataUrl.value = canvas.toDataURL('image/jpeg', 0.85)
|
||||
}
|
||||
|
||||
function switchCamera(): void {
|
||||
if (devices.value.length < 2) return
|
||||
const idx = devices.value.findIndex((d) => d.deviceId === selectedDeviceId.value)
|
||||
const nextIdx = (idx + 1) % devices.value.length
|
||||
switchDevice(devices.value[nextIdx].deviceId)
|
||||
}
|
||||
|
||||
function retake(): void {
|
||||
capturedDataUrl.value = ''
|
||||
}
|
||||
@ -114,22 +127,23 @@ onUnmounted(() => stopStream())
|
||||
<div v-if="errorMessage" class="cam-error">{{ errorMessage }}</div>
|
||||
|
||||
<div v-show="!capturedDataUrl" class="cam-preview-wrap">
|
||||
<video ref="videoRef" class="cam-preview" autoplay muted playsinline />
|
||||
<video ref="videoRef" class="cam-preview cam-preview-video" autoplay muted playsinline />
|
||||
<div v-if="starting" class="cam-loading">正在打开摄像头...</div>
|
||||
<button
|
||||
v-if="devices.length > 1"
|
||||
class="cam-switch-btn"
|
||||
title="切换前后摄像头"
|
||||
@click="switchCamera"
|
||||
>
|
||||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="17 1 21 5 17 9"/><path d="M3 11V9a4 4 0 0 1 4-4h14"/><polyline points="7 23 3 19 7 15"/><path d="M21 13v2a4 4 0 0 1-4 4H3"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<img v-if="capturedDataUrl" :src="capturedDataUrl" class="cam-preview" alt="拍摄预览" />
|
||||
|
||||
<canvas ref="canvasRef" style="display:none" />
|
||||
|
||||
<div v-if="devices.length > 1 && !capturedDataUrl" class="cam-device-row">
|
||||
<label class="fl">摄像头</label>
|
||||
<select class="s-inp" :value="selectedDeviceId" @change="switchDevice(($event.target as HTMLSelectElement).value)">
|
||||
<option v-for="d in devices" :key="d.deviceId" :value="d.deviceId">
|
||||
{{ d.label || `摄像头 ${d.deviceId.slice(0, 6)}` }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
@ -154,17 +168,25 @@ onUnmounted(() => stopStream())
|
||||
width: 100%; max-height: 60vh; object-fit: contain;
|
||||
background: #000; border-radius: var(--radius-sm);
|
||||
}
|
||||
/* 这台设备的摄像头画面是倒置的,转正预览;capture() 里对拍照结果做了同样的旋转 */
|
||||
.cam-preview-video { transform: rotate(180deg); }
|
||||
.cam-loading {
|
||||
position: absolute; inset: 0; display: flex;
|
||||
align-items: center; justify-content: center;
|
||||
color: #fff; font-size: 15px; background: rgba(0,0,0,.35);
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
.cam-switch-btn {
|
||||
position: absolute; top: 14px; right: 14px;
|
||||
width: 44px; height: 44px; border-radius: 50%;
|
||||
border: none; background: rgba(0,0,0,.45); color: #fff;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
.cam-switch-btn:hover { background: rgba(0,0,0,.65); }
|
||||
.cam-error {
|
||||
color: var(--red); background: #FBEAE8;
|
||||
border-radius: var(--radius-sm); padding: 12px 14px; font-size: 15px;
|
||||
}
|
||||
.cam-device-row { display: flex; align-items: center; gap: 12px; }
|
||||
.cam-device-row .fl { flex-shrink: 0; font-size: 15px; color: var(--ink-2); }
|
||||
.cam-actions { display: flex; justify-content: flex-end; gap: 12px; }
|
||||
</style>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user