forked from trent_larson/crowd-funder-for-time-pwa
Update: front-facing camera mirrored preview
This commit is contained in:
@@ -94,6 +94,19 @@
|
|||||||
<strong>Platform:</strong>
|
<strong>Platform:</strong>
|
||||||
{{ platformCapabilities.isMobile ? "Mobile" : "Desktop" }}
|
{{ platformCapabilities.isMobile ? "Mobile" : "Desktop" }}
|
||||||
</p>
|
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -109,10 +122,19 @@
|
|||||||
<video
|
<video
|
||||||
ref="videoElement"
|
ref="videoElement"
|
||||||
class="camera-video w-full h-full object-cover"
|
class="camera-video w-full h-full object-cover"
|
||||||
|
:class="{ 'mirror-video': shouldMirrorVideo }"
|
||||||
autoplay
|
autoplay
|
||||||
playsinline
|
playsinline
|
||||||
muted
|
muted
|
||||||
></video>
|
></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">
|
<div :class="cameraControlsClasses">
|
||||||
<button
|
<button
|
||||||
:class="cameraControlButtonClasses"
|
:class="cameraControlButtonClasses"
|
||||||
@@ -444,6 +466,22 @@ export default class ImageMethodDialog extends Vue {
|
|||||||
return "max-h-[50vh] max-w-[90vw] object-contain";
|
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
|
// Props
|
||||||
@Prop({ default: true }) isRegistered!: boolean;
|
@Prop({ default: true }) isRegistered!: boolean;
|
||||||
@Prop({
|
@Prop({
|
||||||
@@ -482,8 +520,15 @@ export default class ImageMethodDialog extends Vue {
|
|||||||
this.crop = !!crop;
|
this.crop = !!crop;
|
||||||
this.imageCallback = setImageFn;
|
this.imageCallback = setImageFn;
|
||||||
this.visible = true;
|
this.visible = true;
|
||||||
|
|
||||||
|
// Use provided default camera mode, or intelligently choose based on platform
|
||||||
this.currentFacingMode = this.defaultCameraMode as "environment" | "user";
|
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
|
// Start camera preview immediately
|
||||||
logger.debug("Starting camera preview from open()");
|
logger.debug("Starting camera preview from open()");
|
||||||
this.startCameraPreview();
|
this.startCameraPreview();
|
||||||
@@ -646,7 +691,19 @@ export default class ImageMethodDialog extends Vue {
|
|||||||
canvas.width = videoElement.videoWidth;
|
canvas.width = videoElement.videoWidth;
|
||||||
canvas.height = videoElement.videoHeight;
|
canvas.height = videoElement.videoHeight;
|
||||||
const ctx = canvas.getContext("2d");
|
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(
|
canvas.toBlob(
|
||||||
(blob) => {
|
(blob) => {
|
||||||
@@ -833,4 +890,9 @@ export default class ImageMethodDialog extends Vue {
|
|||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Mirror video for front-facing camera */
|
||||||
|
.mirror-video {
|
||||||
|
transform: scaleX(-1);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user