Created
March 12, 2021 00:13
-
-
Save b4n92uid/84ed75bbdd6a2af4bc9b220d08604533 to your computer and use it in GitHub Desktop.
[Vuetify] PictureField Base64
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': value !== null | |
}" | |
@click.stop="sourceDialog = true" | |
> | |
<input | |
ref="field" | |
type="file" | |
class="__input" | |
accept="image/*" | |
@click.stop | |
@change="onFileSelected" | |
/> | |
<div v-if="value"> | |
<div class="py-4 rounded-lg"> | |
<v-img :src="value" :aspect-ratio="16 / 9" contain> </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: { | |
value: { | |
type: String, | |
default: null | |
} | |
}, | |
data() { | |
return { | |
loading: 0, | |
sourceDialog: false | |
}; | |
}, | |
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 | |
}); | |
const dataUri = await this.readAsDataURL(file); | |
this.$emit("input", dataUri); | |
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.$emit("input", image.webPath); | |
} 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.$refs.field.files.value = ""; | |
this.$emit("input", 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