upgrade to new cropperjs 2.0 API

This commit is contained in:
Henry Jameson 2025-03-30 17:06:19 +03:00
commit eda51ae486
4 changed files with 86 additions and 45 deletions

View file

@ -1,5 +1,4 @@
import Cropper from 'cropperjs'
import 'cropperjs/dist/cropper.css'
import 'cropperjs' // This adds all of the cropperjs's components into DOM
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faCircleNotch
@ -19,19 +18,6 @@ const ImageCropper = {
type: Function,
required: true
},
cropperOptions: {
type: Object,
default () {
return {
aspectRatio: 1,
autoCropArea: 1,
viewMode: 1,
movable: false,
zoomable: false,
guides: false
}
}
},
mimes: {
type: String,
default: 'image/png, image/gif, image/jpeg, image/bmp, image/x-icon'
@ -48,7 +34,6 @@ const ImageCropper = {
},
data () {
return {
cropper: undefined,
dataUrl: undefined,
filename: undefined,
submitting: false
@ -67,27 +52,30 @@ const ImageCropper = {
},
methods: {
destroy () {
if (this.cropper) {
this.cropper.destroy()
}
this.$refs.input.value = ''
this.dataUrl = undefined
this.$emit('close')
},
submit (cropping = true) {
this.submitting = true
this.submitHandler(cropping && this.cropper, this.file)
.then(() => this.destroy())
.finally(() => {
this.submitting = false
})
let cropperPromise
if (cropping) {
cropperPromise = this.$refs.cropperSelection.$toCanvas()
} else {
cropperPromise = Promise.resolve()
}
cropperPromise.then(canvas => {
this.submitHandler(canvas, this.file)
.then(() => this.destroy())
.finally(() => {
this.submitting = false
})
})
},
pickImage () {
this.$refs.input.click()
},
createCropper () {
this.cropper = new Cropper(this.$refs.img, this.cropperOptions)
},
getTriggerDOM () {
return typeof this.trigger === 'object' ? this.trigger : document.querySelector(this.trigger)
},
@ -103,6 +91,29 @@ const ImageCropper = {
reader.readAsDataURL(this.file)
this.$emit('changed', this.file, reader)
}
},
inSelection(selection, maxSelection) {
return (
selection.x >= maxSelection.x
&& selection.y >= maxSelection.y
&& (selection.x + selection.width) <= (maxSelection.x + maxSelection.width)
&& (selection.y + selection.height) <= (maxSelection.y + maxSelection.height)
)
},
onCropperSelectionChange(event) {
const cropperCanvas = this.$refs.cropperCanvas
const cropperCanvasRect = cropperCanvas.getBoundingClientRect()
const selection = event.detail
const maxSelection = {
x: 0,
y: 0,
width: cropperCanvasRect.width,
height: cropperCanvasRect.height,
}
if (!this.inSelection(selection, maxSelection)) {
event.preventDefault();
}
}
},
mounted () {

View file

@ -1,14 +1,43 @@
<template>
<div class="image-cropper">
<div v-if="dataUrl">
<div class="image-cropper-image-container">
<img
ref="img"
<cropper-canvas
background
class="image-cropper-canvas"
ref="cropperCanvas"
height="25em"
>
<cropper-image
:src="dataUrl"
alt=""
@load.stop="createCropper"
alt="Picture"
ref="cropperImage"
class="image-cropper-image"
translatable
scalable
/>
<cropper-shade hidden />
<cropper-handle action="select" plain />
<cropper-selection
ref="cropperSelection"
initial-coverage="1"
aspect-ratio="1"
movable
resizable
@change="onCropperSelectionChange"
>
</div>
<cropper-grid role="grid" covered></cropper-grid>
<cropper-crosshair centered></cropper-crosshair>
<cropper-handle action="move" theme-color="rgba(255, 255, 255, 0.35)"></cropper-handle>
<cropper-handle action="n-resize"></cropper-handle>
<cropper-handle action="e-resize"></cropper-handle>
<cropper-handle action="s-resize"></cropper-handle>
<cropper-handle action="w-resize"></cropper-handle>
<cropper-handle action="ne-resize"></cropper-handle>
<cropper-handle action="nw-resize"></cropper-handle>
<cropper-handle action="se-resize"></cropper-handle>
<cropper-handle action="sw-resize"></cropper-handle>
</cropper-selection>
</cropper-canvas>
<div class="image-cropper-buttons-wrapper">
<button
class="button-default btn"
@ -55,20 +84,18 @@
display: none;
}
&-image-container {
position: relative;
img {
display: block;
max-width: 100%;
}
&-canvas {
height: 25em;
width: 25em;
}
&-buttons-wrapper {
margin-top: 10px;
display: grid;
grid-gap: 0.5em;
grid-template-columns: 1fr 1fr 1fr;
button {
margin-top: 5px;
margin-top: 1em;
}
}
}

View file

@ -216,7 +216,7 @@ const ProfileTab = {
this.submitBackground('')
}
},
submitAvatar (cropper, file) {
submitAvatar (canvas, file) {
const that = this
return new Promise((resolve, reject) => {
function updateAvatar (avatar, avatarName) {
@ -232,8 +232,8 @@ const ProfileTab = {
})
}
if (cropper) {
cropper.getCroppedCanvas().toBlob((data) => updateAvatar(data, file.name), file.type)
if (canvas) {
canvas.toBlob((data) => updateAvatar(data, file.name), file.type)
} else {
updateAvatar(file, file.name)
}

View file

@ -97,6 +97,9 @@ export default defineConfig(async ({ mode, command }) => {
if (tag === 'pinch-zoom') {
return true
}
if (tag.startsWith('cropper-')) {
return true
}
return false
}
}