|
|
@ -94,6 +94,19 @@ |
|
|
|
<strong>Platform:</strong> |
|
|
|
{{ platformCapabilities.isMobile ? "Mobile" : "Desktop" }} |
|
|
|
</p> |
|
|
|
<p><strong>Camera Mode:</strong> {{ currentFacingMode }}</p> |
|
|
|
<p> |
|
|
|
<strong>Video Mirrored:</strong> |
|
|
|
{{ shouldMirrorVideo ? "Yes" : "No" }} |
|
|
|
</p> |
|
|
|
<p> |
|
|
|
<strong>Mirror Logic:</strong> |
|
|
|
{{ |
|
|
|
platformCapabilities.isMobile |
|
|
|
? "Mobile: user mode only" |
|
|
|
: "Desktop: always mirrored" |
|
|
|
}} |
|
|
|
</p> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
@ -109,10 +122,19 @@ |
|
|
|
<video |
|
|
|
ref="videoElement" |
|
|
|
class="camera-video w-full h-full object-cover" |
|
|
|
:class="{ 'mirror-video': shouldMirrorVideo }" |
|
|
|
autoplay |
|
|
|
playsinline |
|
|
|
muted |
|
|
|
></video> |
|
|
|
<!-- Mirror indicator --> |
|
|
|
<div |
|
|
|
v-if="shouldMirrorVideo" |
|
|
|
class="absolute top-2 left-2 bg-black/50 text-white px-2 py-1 rounded text-xs" |
|
|
|
> |
|
|
|
<font-awesome icon="mirror" class="w-[1em] mr-1" /> |
|
|
|
Mirrored |
|
|
|
</div> |
|
|
|
<div :class="cameraControlsClasses"> |
|
|
|
<button |
|
|
|
:class="cameraControlButtonClasses" |
|
|
@ -444,6 +466,22 @@ export default class ImageMethodDialog extends Vue { |
|
|
|
return "max-h-[50vh] max-w-[90vw] object-contain"; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Computed property to determine if video should be mirrored |
|
|
|
* Mirrors video for front-facing camera (user-facing) but not back camera |
|
|
|
* This applies to both desktop and mobile when using front-facing cameras |
|
|
|
*/ |
|
|
|
get shouldMirrorVideo(): boolean { |
|
|
|
// On desktop, most webcams face the user, so we should mirror regardless of facingMode |
|
|
|
// On mobile, only mirror when using front-facing camera (user mode) |
|
|
|
if (this.platformCapabilities.isMobile) { |
|
|
|
return this.currentFacingMode === "user"; |
|
|
|
} else { |
|
|
|
// Desktop: mirror by default since webcams typically face the user |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Props |
|
|
|
@Prop({ default: true }) isRegistered!: boolean; |
|
|
|
@Prop({ |
|
|
@ -482,8 +520,15 @@ export default class ImageMethodDialog extends Vue { |
|
|
|
this.crop = !!crop; |
|
|
|
this.imageCallback = setImageFn; |
|
|
|
this.visible = true; |
|
|
|
|
|
|
|
// Use provided default camera mode, or intelligently choose based on platform |
|
|
|
this.currentFacingMode = this.defaultCameraMode as "environment" | "user"; |
|
|
|
|
|
|
|
// Log camera mode for debugging |
|
|
|
logger.debug("Camera facing mode:", this.currentFacingMode); |
|
|
|
logger.debug("Should mirror video:", this.shouldMirrorVideo); |
|
|
|
logger.debug("Platform capabilities:", this.platformCapabilities); |
|
|
|
|
|
|
|
// Start camera preview immediately |
|
|
|
logger.debug("Starting camera preview from open()"); |
|
|
|
this.startCameraPreview(); |
|
|
@ -646,7 +691,19 @@ export default class ImageMethodDialog extends Vue { |
|
|
|
canvas.width = videoElement.videoWidth; |
|
|
|
canvas.height = videoElement.videoHeight; |
|
|
|
const ctx = canvas.getContext("2d"); |
|
|
|
ctx?.drawImage(videoElement, 0, 0, canvas.width, canvas.height); |
|
|
|
|
|
|
|
if (!ctx) { |
|
|
|
throw new Error("Could not get canvas context"); |
|
|
|
} |
|
|
|
|
|
|
|
// If video is mirrored, flip the canvas horizontally |
|
|
|
// to capture the image in the correct orientation |
|
|
|
if (this.shouldMirrorVideo) { |
|
|
|
ctx.scale(-1, 1); |
|
|
|
ctx.translate(-canvas.width, 0); |
|
|
|
} |
|
|
|
|
|
|
|
ctx.drawImage(videoElement, 0, 0, canvas.width, canvas.height); |
|
|
|
|
|
|
|
canvas.toBlob( |
|
|
|
(blob) => { |
|
|
@ -833,4 +890,9 @@ export default class ImageMethodDialog extends Vue { |
|
|
|
white-space: pre-wrap; |
|
|
|
word-break: break-all; |
|
|
|
} |
|
|
|
|
|
|
|
/* Mirror video for front-facing camera */ |
|
|
|
.mirror-video { |
|
|
|
transform: scaleX(-1); |
|
|
|
} |
|
|
|
</style> |
|
|
|