Skip to content
Snippets Groups Projects
CropImageModal.vue 4.43 KiB
Newer Older
<script setup lang="ts">
import { Cropper } from 'vue-advanced-cropper';
import 'vue-advanced-cropper/dist/style.css';
import 'vue-advanced-cropper/dist/theme.compact.css';
import { useWindowSize, useVModels } from '@vueuse/core';
import type { IImageMainInfo } from '@/app/interfaces';
import type { IImage } from '@/app/interfaces/entities';

interface Props {
  isVisible: boolean;
  imageInfo: IImageMainInfo | IImage;
}
const props = defineProps<Props>();
const emit = defineEmits(['update:isVisible', 'saveImage']);
const { isVisible, imageInfo } = useVModels(props, emit);

const imageInstance = new Image();
const finalImageUrl = ref<string>('');
const { width: windowWidth, height: windowHeight } = useWindowSize();

const stageSize = ref({
  width: 200,
  height: 200
});

watch(
  () => isVisible.value,
malyusgun's avatar
malyusgun committed
  () => {
    if (imageInfo.value.imageUrl && isVisible.value) {
      imageInstance.src = imageInfo.value.imageUrl;
      imageInstance.onload = () => {
        const imageHeight = imageInfo.value.image_height;
        const imageWidth = imageInfo.value.image_width;
malyusgun's avatar
malyusgun committed
        imageInstance.src = imageInfo.value.imageUrl;
        if (imageWidth < (imageHeight * windowWidth.value) / windowHeight.value) {
          imageInstance.onload = () => {
            if (imageHeight < windowHeight.value * 0.75 - 20) {
              stageSize.value.width = imageWidth;
              stageSize.value.height = imageHeight;
              imageInstance.width = stageSize.value.width;
              imageInstance.height = stageSize.value.height;
            } else {
              stageSize.value.height = windowHeight.value * 0.75 - 20;
              imageInstance.height = stageSize.value.height;
              imageInstance.width = +(stageSize.value.height / imageHeight).toFixed(2) * imageWidth;
              stageSize.value.width = imageInstance.width;
            }
          };
        } else {
          imageInstance.onload = () => {
            if (imageWidth < windowWidth.value * 0.63 - 60) {
              stageSize.value.width = imageWidth;
              stageSize.value.height = imageHeight;
              imageInstance.width = stageSize.value.width;
              imageInstance.height = stageSize.value.height;
            } else {
              stageSize.value.width = windowWidth.value * 0.63 - 60;
              imageInstance.width = stageSize.value.width;
              imageInstance.height = +(stageSize.value.width / imageWidth).toFixed(2) * imageHeight;
              stageSize.value.height = imageInstance.height;
            }
          };
        }
      };
    }
  },
  {
    immediate: true
  }
);

const modalWidth = computed(() => {
  if (
    stageSize.value.width < windowWidth.value * 0.63 - 60 ||
    stageSize.value.width <= stageSize.value.height
  ) {
    return (100 * (stageSize.value.width + 60)) / windowWidth.value;
  } else return 63;
});
malyusgun's avatar
malyusgun committed
const onCropperChange = async ({ canvas }) => {
  imageInstance.width = canvas.width;
  imageInstance.height = canvas.height;
  console.log('on change size: ', imageInstance.width, imageInstance.height);
  finalImageUrl.value = canvas.toDataURL();
malyusgun's avatar
malyusgun committed
};
const submitForm = () => {
  emit('saveImage', finalImageUrl.value, imageInstance.width, imageInstance.height);
  finalImageUrl.value = '';
malyusgun's avatar
malyusgun committed
};
</script>

<template>
  <div>
    <Dialog
      v-model:visible="isVisible"
      :draggable="false"
      :style="`width: ${modalWidth}%; position: relative`"
    >
      <template #header>
        <div class="mx-auto select-none">
          <i
            class="pi pi-upload mr-4"
            :style="stageSize.width < 400 ? 'font-size: 1rem' : 'font-size: 1.5rem'"
          ></i>
          <span
            :class="[
              'font-bold text-2xl',
              {
                'text-lg': stageSize.width < 400
              }
            ]"
            >Crop image</span
          >
        </div>
      </template>
      <button
        class="absolute top-0 left-2 -translate-y-full bg-blue-600 p-2 px-4 text-lg font-bold transition-all border-0 border-t-2 border-t-solid border-t-black rounded-t-lg select-none"
malyusgun's avatar
malyusgun committed
        @click.prevent="submitForm"
      >
        Save image
      </button>
      <div
        :style="`position: relative; width: ${stageSize.width}px; height: ${stageSize.height}px;`"
      >
        <Cropper
          :src="imageInstance.src"
          :minWidth="200"
          :minHeight="200"
malyusgun's avatar
malyusgun committed
          @change="onCropperChange"
        ></Cropper>
      </div>
    </Dialog>
  </div>
</template>

<style scoped></style>