kopia lustrzana https://github.com/elk-zone/elk
				
				
				
			
		
			
				
	
	
		
			97 wiersze
		
	
	
		
			2.6 KiB
		
	
	
	
		
			Vue
		
	
	
			
		
		
	
	
			97 wiersze
		
	
	
		
			2.6 KiB
		
	
	
	
		
			Vue
		
	
	
| <script setup lang="ts">
 | |
| import type { Boundaries } from 'vue-advanced-cropper'
 | |
| import { Cropper } from 'vue-advanced-cropper'
 | |
| import 'vue-advanced-cropper/dist/style.css'
 | |
| 
 | |
| const { stencilAspectRatio = 1 / 1, stencilSizePercentage = 0.9 } = defineProps<{
 | |
|   /** Crop frame aspect ratio (width/height), default 1/1 */
 | |
|   stencilAspectRatio?: number
 | |
|   /** The ratio of the longest edge of the cut box to the length of the cut screen, default 0.9, not more than 1 */
 | |
|   stencilSizePercentage?: number
 | |
| }>()
 | |
| 
 | |
| const file = defineModel<File | null>()
 | |
| 
 | |
| const cropperDialog = ref(false)
 | |
| const cropper = ref<InstanceType<typeof Cropper>>()
 | |
| const cropperFlag = ref(false)
 | |
| const cropperImage = reactive({
 | |
|   src: '',
 | |
|   type: 'image/jpg',
 | |
| })
 | |
| 
 | |
| function stencilSize({ boundaries }: { boundaries: Boundaries }) {
 | |
|   return {
 | |
|     width: boundaries.width * stencilSizePercentage,
 | |
|     height: boundaries.height * stencilSizePercentage,
 | |
|   }
 | |
| }
 | |
| 
 | |
| watch(file, (file, _, onCleanup) => {
 | |
|   let expired = false
 | |
|   onCleanup(() => expired = true)
 | |
| 
 | |
|   if (file && !cropperFlag.value) {
 | |
|     cropperDialog.value = true
 | |
|     const reader = new FileReader()
 | |
|     reader.readAsDataURL(file)
 | |
|     reader.onload = (e) => {
 | |
|       if (expired)
 | |
|         return
 | |
|       cropperImage.src = e.target?.result as string
 | |
|       cropperImage.type = file.type
 | |
|     }
 | |
|   }
 | |
|   cropperFlag.value = false
 | |
| })
 | |
| 
 | |
| function cropImage() {
 | |
|   if (cropper.value && file.value) {
 | |
|     cropperFlag.value = true
 | |
|     cropperDialog.value = false
 | |
|     const { canvas } = cropper.value.getResult()
 | |
|     canvas?.toBlob((blob) => {
 | |
|       file.value = new File([blob as any], `cropped${file.value?.name}` as string, { type: blob?.type })
 | |
|     }, cropperImage.type)
 | |
|   }
 | |
| }
 | |
| </script>
 | |
| 
 | |
| <template>
 | |
|   <ModalDialog v-model="cropperDialog" :use-v-if="false" max-w-500px flex>
 | |
|     <div flex-1 w-0>
 | |
|       <div text-lg text-center my2 px3>
 | |
|         <h1>
 | |
|           {{ $t('action.edit') }}
 | |
|         </h1>
 | |
|       </div>
 | |
|       <div aspect-ratio-1>
 | |
|         <Cropper
 | |
|           ref="cropper"
 | |
|           class="overflow-hidden w-full h-full"
 | |
|           :src="cropperImage.src"
 | |
|           :resize-image="{
 | |
|             adjustStencil: false,
 | |
|           }"
 | |
|           :stencil-size="stencilSize"
 | |
|           :stencil-props="{
 | |
|             aspectRatio: stencilAspectRatio,
 | |
|             movable: false,
 | |
|             resizable: false,
 | |
|             handlers: {},
 | |
|           }"
 | |
|           image-restriction="stencil"
 | |
|         />
 | |
|       </div>
 | |
|       <div m-4>
 | |
|         <button
 | |
|           btn-solid w-full rounded text-sm
 | |
|           @click="cropImage()"
 | |
|         >
 | |
|           {{ $t('action.confirm') }}
 | |
|         </button>
 | |
|       </div>
 | |
|     </div>
 | |
|   </ModalDialog>
 | |
| </template>
 |