Last active
August 17, 2021 22:19
-
-
Save b4n92uid/ff2eae85828388ce0da74d47261cda5b to your computer and use it in GitHub Desktop.
[Vuetify] PictureField
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<v-card | |
class="v-picture-field rounded-lg" | |
:loading="loading > 0" | |
outlined | |
:class="{ | |
'--has-content': previewSrc !== null | |
}" | |
@click.stop="sourceDialog = true" | |
> | |
<input | |
ref="field" | |
type="file" | |
class="__input" | |
accept="image/*" | |
@click.stop | |
@change="onFileSelected" | |
/> | |
<div v-if="previewSrc"> | |
<div class="py-4 rounded-lg"> | |
<v-img | |
:src="previewSrc" | |
:aspect-ratio="16 / 9" | |
contain | |
@error="previewSrc = null" | |
> | |
</v-img> | |
</div> | |
<div class="d-flex justify-space-between"> | |
<v-btn color="primary" text small @click.stop="choose"> | |
<v-icon left small>mdi-camera</v-icon> | |
Choisir | |
</v-btn> | |
<v-btn color="red" text small @click.stop="clear"> | |
<v-icon left small>mdi-delete</v-icon> | |
Supprimer | |
</v-btn> | |
</div> | |
</div> | |
<div v-else> | |
<v-responsive | |
:aspect-ratio="16 / 9" | |
class="d-flex justify-center align-center" | |
> | |
<div class="text-center pa-4 grey--text"> | |
<v-icon color="grey" size="128">mdi-camera</v-icon> | |
<div>Sélectionner ou prenez une photo</div> | |
</div> | |
</v-responsive> | |
</div> | |
<v-bottom-sheet v-model="sourceDialog" inset> | |
<v-sheet tile> | |
<v-list> | |
<v-list-item @click.stop="fromCamera"> | |
<v-list-item-icon> | |
<v-icon>mdi-camera</v-icon> | |
</v-list-item-icon> | |
<v-list-item-title>Prendre une photo</v-list-item-title> | |
</v-list-item> | |
<v-list-item @click.stop="fromDevice"> | |
<v-list-item-icon> | |
<v-icon>mdi-folder-open</v-icon> | |
</v-list-item-icon> | |
<v-list-item-title>Choisir depuis l'appareil</v-list-item-title> | |
</v-list-item> | |
</v-list> | |
</v-sheet> | |
</v-bottom-sheet> | |
</v-card> | |
</template> | |
<script> | |
import imageCompression from "browser-image-compression"; | |
import { Plugins, CameraResultType, CameraSource } from "@capacitor/core"; | |
export default { | |
props: { | |
src: { | |
type: String, | |
default: null | |
} | |
}, | |
data() { | |
return { | |
loading: 0, | |
sourceDialog: false, | |
previewSrc: this.src | |
}; | |
}, | |
watch: { | |
src(v) { | |
this.previewSrc = v; | |
} | |
}, | |
methods: { | |
isImageSizeValid(src, { width, height }) { | |
return new Promise(resolve => { | |
const loader = new Image(); | |
loader.onload = () => | |
resolve(loader.width <= width && loader.height <= height); | |
loader.src = src; | |
}); | |
}, | |
async readAsDataURL(f) { | |
return new Promise(resolve => { | |
const reader = new FileReader(); | |
reader.addEventListener("load", () => resolve(reader.result), false); | |
reader.readAsDataURL(f); | |
}); | |
}, | |
async onFileSelected() { | |
if (!this.$refs.field.files.length) return; | |
let [file] = this.$refs.field.files; | |
this.loading++; | |
file = await imageCompression(file, { | |
maxSizeMB: 1 | |
}); | |
this.previewSrc = await this.readAsDataURL(file); | |
this.$emit("change", file); | |
this.$refs.field.files.value = ""; | |
this.loading--; | |
}, | |
async fromCamera() { | |
this.sourceDialog = false; | |
const { Camera } = Plugins; | |
try { | |
const image = await Camera.getPhoto({ | |
quality: 80, | |
width: 1024, | |
resultType: CameraResultType.Uri, | |
source: CameraSource.Camera | |
}); | |
this.loading++; | |
this.previewSrc = image.webPath; | |
const blob = await fetch(image.webPath).then(r => r.blob()); | |
const file = new File([blob], "camera.jpeg"); | |
this.$emit("change", file); | |
} catch (error) { | |
// TODO: Handle error | |
} | |
this.loading--; | |
}, | |
fromDevice() { | |
this.sourceDialog = false; | |
this.$nextTick(() => this.$refs.field.click()); | |
}, | |
async choose() { | |
if (process.env.VUE_APP_IS_CAPACITOR) this.sourceDialog = true; | |
else this.fromDevice(); | |
}, | |
clear() { | |
this.previewSrc = null; | |
this.$refs.field.files.value = ""; | |
this.$emit("change", null); | |
} | |
} | |
}; | |
</script> | |
<style lang="scss"> | |
.v-picture-field.v-card { | |
> .__input { | |
display: none; | |
} | |
border: thin dashed rgba(0, 0, 0, 0.12); | |
&.--has-content { | |
border: thin solid rgba(0, 0, 0, 0.12); | |
} | |
} | |
</style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment