Uprava editoru
This commit is contained in:
@@ -69,11 +69,6 @@ const copyLinkToClipboard = () => {
|
||||
<FlightDetailSkeleton v-if="loading || !flight"/>
|
||||
|
||||
<q-card class="flight-detail-card" v-else>
|
||||
<!-- <PhotoCarousel-->
|
||||
<!-- :cover-photo-url="flight.coverPhoto?.url"-->
|
||||
<!-- :carousel-title="flightName(flight)"-->
|
||||
<!-- :photos="flight.photos"-->
|
||||
<!-- />-->
|
||||
<q-img :src="flight.coverPhoto.url" v-if="flight.coverPhoto">
|
||||
<div class="absolute-bottom custom-caption">
|
||||
<div class="text-h5"> {{ flightName(flight) }} </div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
import {useRoute} from "vue-router";
|
||||
import {computed, onMounted, ref, watch} from "vue";
|
||||
import {onMounted, ref, watch} from "vue";
|
||||
import {usePhotosStore} from "stores/Photo";
|
||||
import {useAppStore} from "stores/App";
|
||||
import {flightName} from "src/composables/objects/flight";
|
||||
@@ -9,14 +9,14 @@ import {croppedAreaForPreview, getPreviewUrl} from "src/composables/photos/edito
|
||||
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 {debounce, throttle, 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");
|
||||
const editorTab = ref("rotate");
|
||||
|
||||
// adjustments
|
||||
const defaultAdjustments = {
|
||||
@@ -28,19 +28,16 @@ const defaultAdjustments = {
|
||||
};
|
||||
const adjustment = ref({...defaultAdjustments});
|
||||
const croppedAdjusted = ref("");
|
||||
const croppedAdjustedUnrotated = ref("");
|
||||
const uncroppedAdjusted = ref("");
|
||||
const croppedUnadjusted = ref("");
|
||||
|
||||
watch(() => [adjustment.value, croppedAreaForPreview.value], debounce(() => {
|
||||
croppedAdjusted.value = getPreviewUrl(photoId, {...adjustment.value, ...croppedAreaForPreview.value});
|
||||
croppedAdjustedUnrotated.value = getPreviewUrl(photoId, {...adjustment.value, rotate: 0, ...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);
|
||||
});
|
||||
}, 200), {deep: true, immediate: true});
|
||||
|
||||
const savePhotoAdjustment = (photoId) => {
|
||||
const adjustmentInput = {...adjustment.value, crop: croppedArea.value};
|
||||
@@ -64,6 +61,7 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
if (photoAdjustment) {
|
||||
rotate.value = photoAdjustment.rotate;
|
||||
croppedArea.value = {
|
||||
left: photoAdjustment.cropLeft,
|
||||
top: photoAdjustment.cropTop,
|
||||
@@ -92,51 +90,57 @@ onMounted(() => {
|
||||
<template v-if="!loading">
|
||||
<q-card :style="`width: ${splitterWidth}px; margin: 0 auto`">
|
||||
<VuePictureCropper
|
||||
v-if="editorTab === 'rotate_crop'"
|
||||
:img="uncroppedAdjusted"
|
||||
:boxStyle="{
|
||||
v-if="editorTab === '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"
|
||||
:options="{
|
||||
viewMode: 1,
|
||||
autoCropArea: 1,
|
||||
dragMode: 'none',
|
||||
zoomable: false,
|
||||
draggable: false,
|
||||
rotatable: false,
|
||||
}"
|
||||
@cropend="setCropData"
|
||||
@ready="cropperReady"
|
||||
/>
|
||||
|
||||
<div class="overflow-hidden relative-position" v-else-if="editorTab === 'rotate'">
|
||||
<div class="rotate-overlay"></div>
|
||||
<q-img :src="croppedAdjustedUnrotated" fit="contain" :style="`transform: rotate(${adjustment.rotate}deg)`"/>
|
||||
</div>
|
||||
|
||||
<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="{
|
||||
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">
|
||||
before-class="overflow-hidden"
|
||||
after-class="overflow-hidden">
|
||||
<template #before>
|
||||
<img
|
||||
:width="splitterWidth"
|
||||
class="absolute-top-left"
|
||||
:src="croppedUnadjusted"
|
||||
alt="before"
|
||||
:width="splitterWidth"
|
||||
class="absolute-top-left"
|
||||
:src="croppedUnadjusted"
|
||||
alt="before"
|
||||
/>
|
||||
</template>
|
||||
<template #after>
|
||||
<img
|
||||
:width="splitterWidth"
|
||||
class="absolute-top-right"
|
||||
:src="croppedAdjusted"
|
||||
alt="after"
|
||||
:width="splitterWidth"
|
||||
class="absolute-top-right"
|
||||
:src="croppedAdjusted"
|
||||
alt="after"
|
||||
/>
|
||||
</template>
|
||||
</q-splitter>
|
||||
@@ -154,7 +158,8 @@ onMounted(() => {
|
||||
<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-tab name="rotate" icon="rotate" label="Otočit"/>
|
||||
<q-tab name="crop" icon="crop" label="Oříznout"/>
|
||||
</q-tabs>
|
||||
<q-separator/>
|
||||
|
||||
@@ -167,12 +172,13 @@ onMounted(() => {
|
||||
</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"
|
||||
v-model="adjustment.contrast"
|
||||
:min="0.5"
|
||||
:max="1.5"
|
||||
:step="0.01"
|
||||
label-always
|
||||
@dblclick="adjustment.contrast=1"
|
||||
:label="true"
|
||||
/>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
@@ -183,12 +189,13 @@ onMounted(() => {
|
||||
</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"
|
||||
v-model="adjustment.brightness"
|
||||
:min="0.5"
|
||||
:max="1.5"
|
||||
:step="0.01"
|
||||
:label="true"
|
||||
label-always
|
||||
@dblclick="adjustment.brightness = 1"
|
||||
/>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
@@ -199,12 +206,13 @@ onMounted(() => {
|
||||
</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"
|
||||
v-model="adjustment.saturation"
|
||||
:min="0"
|
||||
:max="2"
|
||||
:step="0.05"
|
||||
:label="true"
|
||||
label-always
|
||||
@dblclick="adjustment.saturation = 1"
|
||||
/>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
@@ -218,44 +226,52 @@ onMounted(() => {
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-slider
|
||||
v-model="adjustment.sharpness"
|
||||
:min="-3"
|
||||
:max="8"
|
||||
:step="1"
|
||||
:label="true"
|
||||
@dblclick="adjustment.sharpness=1"
|
||||
v-model="adjustment.sharpness"
|
||||
:min="-3"
|
||||
:max="8"
|
||||
:step="1"
|
||||
:label="true"
|
||||
label-always
|
||||
snap
|
||||
@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-tab-panel name="rotate">
|
||||
<q-list>
|
||||
<q-item>
|
||||
<q-item-section avatar>
|
||||
<q-icon name="flip_camera_android"/>
|
||||
<q-btn round flat icon="rotate_left" @click="adjustment.rotate -= 0.1"/>
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-slider
|
||||
<q-slider
|
||||
v-model="adjustment.rotate"
|
||||
:min="-90"
|
||||
:max="90"
|
||||
:min="-45"
|
||||
:max="45"
|
||||
:step="0.1"
|
||||
:label="true"
|
||||
label-always
|
||||
@dblclick="adjustment.rotate=0"
|
||||
/>
|
||||
/>
|
||||
</q-item-section>
|
||||
<q-item-section avatar>
|
||||
<q-btn round flat icon="rotate_right" @click="adjustment.rotate += 0.1"/>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-tab-panel>
|
||||
<q-tab-panel name="crop">
|
||||
<q-btn-toggle
|
||||
no-caps
|
||||
spread
|
||||
v-model="selectedAspectRatio"
|
||||
toggle-color="primary"
|
||||
:options="aspectRatioOptions"
|
||||
class="q-ma-lg"
|
||||
/>
|
||||
</q-tab-panel>
|
||||
</q-tab-panels>
|
||||
</q-card-section>
|
||||
<q-card-actions>
|
||||
@@ -265,3 +281,17 @@ onMounted(() => {
|
||||
</q-card>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
|
||||
<style>
|
||||
.rotate-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
z-index: 888;
|
||||
background-size: 60px 60px;
|
||||
background-image: linear-gradient(to right, rgba(61, 4, 4, 0.5) 1px, transparent 1px),
|
||||
linear-gradient(to bottom, rgba(61, 4, 4, 0.5) 1px, transparent 1px);
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user