Files
app/src/pages/profile/flight/PhotoEditorPage.vue
T
2023-10-27 22:30:47 +02:00

268 lines
8.8 KiB
Vue

<script setup>
import {useRoute} from "vue-router";
import {computed, onMounted, ref, watch} from "vue";
import {usePhotosStore} from "stores/Photo";
import {useAppStore} from "stores/App";
import {flightName} from "src/composables/objects/flight";
import VuePictureCropper, {cropper} from 'vue-picture-cropper';
import {croppedAreaForPreview, getPreviewUrl} from "src/composables/photos/editor/preview";
import {compare, onResize, splitter, splitterWidth} from "src/composables/photos/editor/splitter";
import {aspectRatioOptions, croppedArea, cropperReady, selectedAspectRatio, setCropData} from "src/composables/photos/editor/crop";
import {fetchPhoto, loading, photo, photoAspectRatio} from "src/composables/photos/editor/photo";
import {debounce, useQuasar} from "quasar";
import {successObject} from "src/composables/dialog";
const $route = useRoute();
const $q = useQuasar();
const photoId = parseInt($route.params.id);
const noCacheTimestamp = ref((new Date()).getTime());
const editorTab = ref("colors");
// adjustments
const defaultAdjustments = {
saturation: 1,
brightness: 1,
contrast: 1,
sharpness: 1,
rotate: 0,
};
const adjustment = ref({...defaultAdjustments});
const croppedAdjusted = ref("");
const uncroppedAdjusted = ref("");
const croppedUnadjusted = ref("");
watch(() => [adjustment.value, croppedAreaForPreview.value], debounce(() => {
croppedAdjusted.value = getPreviewUrl(photoId, {...adjustment.value, ...croppedAreaForPreview.value});
uncroppedAdjusted.value = getPreviewUrl(photoId, {...adjustment.value});
croppedUnadjusted.value = getPreviewUrl(photoId, {rotate: adjustment.value.rotate, ...croppedAreaForPreview.value});
}, 100), {deep: true, immediate: true});
watch(() => adjustment.value.rotate, (newVal) => {
if (!cropper) return;
cropper.rotateTo(newVal);
});
const savePhotoAdjustment = (photoId) => {
const adjustmentInput = {...adjustment.value, crop: croppedArea.value};
usePhotosStore().adjust(photoId, adjustmentInput).then((data) => {
noCacheTimestamp.value = (new Date()).getTime();
photo.value = data;
$q.notify(successObject("Fotka úspěšně upravena"));
});
};
onMounted(() => {
fetchPhoto(photoId).then(() => {
const flightNameFormatted = flightName(photo.value.flight);
if (photo.value.adjustment) {
const photoAdjustment = photo.value.adjustment;
const src = photoAdjustment || defaultAdjustments;
for (const i in defaultAdjustments) {
adjustment.value[i] = src[i];
}
if (photoAdjustment) {
croppedArea.value = {
left: photoAdjustment.cropLeft,
top: photoAdjustment.cropTop,
width: photoAdjustment.cropWidth,
height: photoAdjustment.cropHeight
};
}
}
useAppStore().setBreadcrumbs([
{to: {name: "myFlights"}, label: "Moje lety"},
{to: {name: "flightDetail", params: {id: photo.value.flightId}}, label: flightNameFormatted},
{to: {name: "flightEdit", params: {id: photo.value.flightId, step: 'photos'}}, label: "Fotografie"},
{label: "Foto editor"}
]);
})
useAppStore().setPageTitle("Foto editor");
});
</script>
<template>
<q-resize-observer @resize="onResize" :debounce="100"/>
<template v-if="loading">Nacitani</template>
<template v-if="!loading">
<q-card :style="`width: ${splitterWidth}px; margin: 0 auto`">
<VuePictureCropper
v-if="editorTab === 'rotate_crop'"
:img="uncroppedAdjusted"
:boxStyle="{
width: '100%',
height: '100%',
backgroundColor: '#f8f8f8',
margin: 'auto',
}"
:options="{
viewMode: 1,
autoCropArea: 1,
dragMode: 'none',
zoomable: false,
draggable: false,
}"
@cropend="setCropData"
@ready="cropperReady"
/>
<div class="overflow-hidden" v-else>
<q-img fit="contain" :src="croppedAdjusted" v-show="!compare"/>
<q-splitter
v-show="compare"
v-model="splitter"
:limits="[0, 100]"
:style="{
height: Math.round(splitterWidth / photoAspectRatio) + 'px',
width: splitterWidth.value + 'px'
}"
before-class="overflow-hidden"
after-class="overflow-hidden">
<template #before>
<img
:width="splitterWidth"
class="absolute-top-left"
:src="croppedUnadjusted"
alt="before"
/>
</template>
<template #after>
<img
:width="splitterWidth"
class="absolute-top-right"
:src="croppedAdjusted"
alt="after"
/>
</template>
</q-splitter>
<div class="text-right q-pt-md q-pr-md">
<q-btn flat icon="compare" @click="compare = !compare" :text-color="compare ? 'primary' : 'grey-5'">
<q-tooltip>Klikni pro porovnání</q-tooltip>
</q-btn>
</div>
</div>
<q-card-section>
<q-separator/>
<q-tabs v-model="editorTab">
<q-tab name="colors" icon="palette" label="Barvy"/>
<q-tab name="details" icon="deblur" label="Detialy"/>
<q-tab name="rotate_crop" icon="crop_rotate" label="Oříznout a otočit"/>
</q-tabs>
<q-separator/>
<q-tab-panels v-model="editorTab" animated>
<q-tab-panel name="colors">
<q-list>
<q-item>
<q-item-section avatar>
<q-icon name="contrast"/>
</q-item-section>
<q-item-section>
<q-slider
v-model="adjustment.contrast"
:min="0.5"
:max="1.5"
:step="0.01"
@dblclick="adjustment.contrast=1"
:label="true"
/>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-icon name="brightness_6"/>
</q-item-section>
<q-item-section>
<q-slider
v-model="adjustment.brightness"
:min="0.5"
:max="1.5"
:step="0.01"
:label="true"
@dblclick="adjustment.brightness = 1"
/>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-icon name="palette"/>
</q-item-section>
<q-item-section>
<q-slider
v-model="adjustment.saturation"
:min="0"
:max="2"
:step="0.05"
:label="true"
@dblclick="adjustment.saturation = 1"
/>
</q-item-section>
</q-item>
</q-list>
</q-tab-panel>
<q-tab-panel name="details">
<q-list>
<q-item>
<q-item-section avatar>
<q-icon name="deblur"/>
</q-item-section>
<q-item-section>
<q-slider
v-model="adjustment.sharpness"
:min="-3"
:max="8"
:step="1"
:label="true"
@dblclick="adjustment.sharpness=1"
/>
</q-item-section>
</q-item>
</q-list>
</q-tab-panel>
<q-tab-panel name="rotate_crop">
<q-btn-toggle
no-caps
spread
v-model="selectedAspectRatio"
toggle-color="primary"
:options="aspectRatioOptions"
class="q-ma-lg"
/>
<q-list>
<q-item>
<q-item-section avatar>
<q-icon name="flip_camera_android"/>
</q-item-section>
<q-item-section>
<q-slider
v-model="adjustment.rotate"
:min="-90"
:max="90"
:step="0.1"
:label="true"
@dblclick="adjustment.rotate=0"
/>
</q-item-section>
</q-item>
</q-list>
</q-tab-panel>
</q-tab-panels>
</q-card-section>
<q-card-actions>
<q-space/>
<q-btn color="primary" icon="save" @click="savePhotoAdjustment(photoId)">Uložit</q-btn>
</q-card-actions>
</q-card>
</template>
</template>