Files
web/app/components/media/Camera.vue
2026-06-07 11:17:10 +07:00

147 lines
4.2 KiB
Vue

<template>
<div>
<div class="block rounded-md is-clipped">
<video
ref="video"
height="480"
autoplay
class="w-full is-block"
></video>
</div>
<div class="fixed-grid has-12-cols mb-0">
<div class="grid">
<div class="cell is-flex is-justify-content-center is-align-items-center">
<button
@click="$emit('close')"
class="button is-light rounded-full"
>
<span class="icon">
<Icon
name="material-symbols:close-rounded"
:size="20"
/>
</span>
</button>
</div>
<div class="cell is-col-span-10 has-text-centered">
<button
@click="capture"
class="button is-primary is-medium rounded-full"
>
<span class="icon">
<Icon
name="material-symbols:photo-camera-outline-rounded"
:size="24"
/>
</span>
</button>
</div>
<div class="cell is-flex is-justify-content-center is-align-items-center">
<button
@click="flip"
class="button is-light is-primary rounded-full"
>
<span class="icon">
<Icon
name="material-symbols:flip-camera-ios-outline-rounded"
:size="20"
/>
</span>
</button>
</div>
</div>
</div>
<canvas
v-show="false"
ref="canvas"
width="640"
height="480"
></canvas>
</div>
</template>
<script>
export default {
data() {
return {
video: {},
canvas: {},
current: "front",
};
},
mounted() {
this.openCamera();
},
beforeDestroy() {
var vidTrack = this.video.srcObject.getVideoTracks();
vidTrack.forEach((track) => {
track.stop();
track.enabled = false;
});
},
methods: {
openCamera() {
this.video = this.$refs.video;
const facingConstraint = {
facingMode: this.current === "front" ? "user" : { exact: "environment" },
};
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices
.getUserMedia({ video: facingConstraint, audio: false })
.then((stream) => {
this.video.srcObject = stream;
this.video.play();
})
.catch((err) => {
if (this.current === "back" && (err.name === "OverconstrainedError" || err.name === "NotFoundError")) {
this.current = "front";
this.$snackbar("Không tìm thấy camera sau", "Error");
this.openCamera(); // retry front cam
}
});
}
},
capture() {
this.canvas = this.$refs.canvas;
const scale = 600 / this.video.videoWidth;
const w = this.video.videoWidth * scale;
const h = this.video.videoHeight * scale;
this.canvas.width = w;
this.canvas.height = h;
this.canvas.getContext("2d").drawImage(this.video, 0, 0, w, h);
this.canvas.toBlob((blob) => this.saveAs(blob));
},
async saveAs(blob) {
const form = new FormData();
const name = `${this.$id()}.png`;
this.fileName = `${this.$dayjs(new Date()).format("YYYYMMDDhhmmss")}-${name}`;
form.append("filename", this.fileName);
form.append("name", name);
form.append("file", blob);
form.append("type", "image");
form.append("size", 100);
form.append("user", this.$store.login.id || 1);
const result = await this.$insertapi("upload", { data: form });
if (result === "error") return;
const row = result.rows[0];
const file = new File([blob], name, { type: "image/png" });
row.source = { file };
this.$emit("modalevent", { name: "selectimage", data: row });
this.$emit("close");
},
flip() {
this.current = this.current === "front" ? "back" : "front";
const vidTrack = this.video.srcObject.getVideoTracks();
vidTrack.forEach((track) => {
track.stop();
track.enabled = false;
});
this.openCamera();
},
},
};
</script>