Upgrade cropperjs to v2

This commit is contained in:
silverwind 2025-03-09 05:17:29 +01:00
parent 6f13331754
commit 4ccc4983e0
No known key found for this signature in database
GPG Key ID: 2E62B41C93869443
4 changed files with 174 additions and 26 deletions

134
package-lock.json generated
View File

@ -22,7 +22,7 @@
"chartjs-adapter-dayjs-4": "1.0.4",
"chartjs-plugin-zoom": "2.2.0",
"clippie": "4.1.5",
"cropperjs": "1.6.2",
"cropperjs": "2.0.0",
"css-loader": "7.1.2",
"dayjs": "1.11.13",
"dropzone": "6.0.0-beta.2",
@ -432,6 +432,126 @@
"node": ">=14.0.0"
}
},
"node_modules/@cropper/element": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/element/-/element-2.0.0.tgz",
"integrity": "sha512-lsthn0nQq73GExUE7Mg/ss6Q3RXADGDv055hxoLFwvl/wGHgy6ZkYlfLZ/VmgBHC6jDK5IgPBFnqrPqlXWSGBA==",
"license": "MIT",
"dependencies": {
"@cropper/utils": "^2.0.0"
}
},
"node_modules/@cropper/element-canvas": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/element-canvas/-/element-canvas-2.0.0.tgz",
"integrity": "sha512-GPtGJgSm92crJhhhwUsaMw3rz2KfJWWSz7kRAlufFEV/EHTP5+6r6/Z1BCGRna830i+Avqbm435XLOtA7PVJwA==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.0.0",
"@cropper/utils": "^2.0.0"
}
},
"node_modules/@cropper/element-crosshair": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/element-crosshair/-/element-crosshair-2.0.0.tgz",
"integrity": "sha512-KfPfyrdeFvUC31Ws7ATtcalWWSaMtrC6bMoCipZhqbUOE7wZoL4ecDSL6BUOZxPa74awZUqfzirCDjHvheBfyw==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.0.0",
"@cropper/utils": "^2.0.0"
}
},
"node_modules/@cropper/element-grid": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/element-grid/-/element-grid-2.0.0.tgz",
"integrity": "sha512-i78SQ0IJTLFveKX6P7svkfMYVdgHrQ8ZmmEw8keFy9n1ZVbK+SK0UHK5FNMRNI/gtVhKJOGEnK/zeyjUdj4Iyw==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.0.0",
"@cropper/utils": "^2.0.0"
}
},
"node_modules/@cropper/element-handle": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/element-handle/-/element-handle-2.0.0.tgz",
"integrity": "sha512-ZJvW+0MkK9E8xYymGdoruaQn2kwjSHFpNSWinjyq6csuVQiCPxlX5ovAEDldmZ9MWePPtWEi3vLKQOo2Yb0T8g==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.0.0",
"@cropper/utils": "^2.0.0"
}
},
"node_modules/@cropper/element-image": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/element-image/-/element-image-2.0.0.tgz",
"integrity": "sha512-9BxiTS/aHRmrjopaFQb9mQQXmx4ruhYHGkDZMVz24AXpMFjUY6OpqrWse/WjzD9tfhMFvEdu17b3VAekcAgpeg==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.0.0",
"@cropper/element-canvas": "^2.0.0",
"@cropper/utils": "^2.0.0"
}
},
"node_modules/@cropper/element-selection": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/element-selection/-/element-selection-2.0.0.tgz",
"integrity": "sha512-ensNnbIfJsJ8bhbJTH/RXtk2URFvTOO4TvfRk461n2FPEC588D7rwBmUJxQg74IiTi4y1JbCI+6j+4LyzYBLCQ==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.0.0",
"@cropper/element-canvas": "^2.0.0",
"@cropper/element-image": "^2.0.0",
"@cropper/utils": "^2.0.0"
}
},
"node_modules/@cropper/element-shade": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/element-shade/-/element-shade-2.0.0.tgz",
"integrity": "sha512-jv/2bbNZnhU4W+T4G0c8ADocLIZvQFTXgCf2RFDNhI5UVxurzWBnDdb8Mx8LnVplnkTqO+xUmHZYve0CwgWo+Q==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.0.0",
"@cropper/element-canvas": "^2.0.0",
"@cropper/element-selection": "^2.0.0",
"@cropper/utils": "^2.0.0"
}
},
"node_modules/@cropper/element-viewer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/element-viewer/-/element-viewer-2.0.0.tgz",
"integrity": "sha512-zY+3VRN5TvpM8twlphYtXw0tzJL2VgzeK7ufhL1BixVqOdRxwP13TprYIhqwGt9EW/SyJZUiaIu396T89kRX8A==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.0.0",
"@cropper/element-canvas": "^2.0.0",
"@cropper/element-image": "^2.0.0",
"@cropper/element-selection": "^2.0.0",
"@cropper/utils": "^2.0.0"
}
},
"node_modules/@cropper/elements": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/elements/-/elements-2.0.0.tgz",
"integrity": "sha512-PQkPo1nUjxLFUQuHYu+6atfHxpX9B41Xribao6wpvmvmNIFML6LQdNqqWYb6LyM7ujsu71CZdBiMT5oetjJVoQ==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.0.0",
"@cropper/element-canvas": "^2.0.0",
"@cropper/element-crosshair": "^2.0.0",
"@cropper/element-grid": "^2.0.0",
"@cropper/element-handle": "^2.0.0",
"@cropper/element-image": "^2.0.0",
"@cropper/element-selection": "^2.0.0",
"@cropper/element-shade": "^2.0.0",
"@cropper/element-viewer": "^2.0.0"
}
},
"node_modules/@cropper/utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/utils/-/utils-2.0.0.tgz",
"integrity": "sha512-cprLYr+7kK3faGgoOsTW9gIn5sefDr2KwOmgyjzIXk+8PLpW8FgFKEg5FoWfRD5zMAmkCBuX6rGKDK3VdUEGrg==",
"license": "MIT"
},
"node_modules/@csstools/css-parser-algorithms": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz",
@ -4925,10 +5045,14 @@
}
},
"node_modules/cropperjs": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/cropperjs/-/cropperjs-1.6.2.tgz",
"integrity": "sha512-nhymn9GdnV3CqiEHJVai54TULFAE3VshJTXSqSJKa8yXAKyBKDWdhHarnlIPrshJ0WMFTGuFvG02YjLXfPiuOA==",
"license": "MIT"
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/cropperjs/-/cropperjs-2.0.0.tgz",
"integrity": "sha512-TO2j0Qre01kPHbow4FuTrbdEB4jTmGRySxW49jyEIqlJZuEBfrvCTT0vC3eRB2WBXudDfKi1Onako6DKWKxeAQ==",
"license": "MIT",
"dependencies": {
"@cropper/elements": "^2.0.0",
"@cropper/utils": "^2.0.0"
}
},
"node_modules/cross-spawn": {
"version": "7.0.6",

View File

@ -21,7 +21,7 @@
"chartjs-adapter-dayjs-4": "1.0.4",
"chartjs-plugin-zoom": "2.2.0",
"clippie": "4.1.5",
"cropperjs": "1.6.2",
"cropperjs": "2.0.0",
"css-loader": "7.1.2",
"dayjs": "1.11.13",
"dropzone": "6.0.0-beta.2",

View File

@ -1,6 +1,8 @@
@import "cropperjs/dist/cropper.css";
.avatar-file-with-cropper + .cropper-panel .cropper-wrapper {
max-width: 400px;
width: 400px;
max-height: 400px;
}
cropper-canvas {
height: 400px;
}

View File

@ -1,4 +1,5 @@
import {showElem, type DOMEvent} from '../../utils/dom.ts';
import {CropperCanvas, CropperImage} from 'cropperjs';
import {createElementFromHTML, showElem, type DOMEvent} from '../../utils/dom.ts';
type CropperOpts = {
container: HTMLElement,
@ -7,25 +8,46 @@ type CropperOpts = {
}
async function initCompCropper({container, fileInput, imageSource}: CropperOpts) {
const {default: Cropper} = await import(/* webpackChunkName: "cropperjs" */'cropperjs');
await import(/* webpackChunkName: "cropperjs" */'cropperjs');
let currentFileName = '';
let currentFileLastModified = 0;
const cropper = new Cropper(imageSource, {
aspectRatio: 1,
viewMode: 2,
autoCrop: false,
crop() {
const canvas = cropper.getCroppedCanvas();
canvas.toBlob((blob) => {
const croppedFileName = currentFileName.replace(/\.[^.]{3,4}$/, '.png');
const croppedFile = new File([blob], croppedFileName, {type: 'image/png', lastModified: currentFileLastModified});
const dataTransfer = new DataTransfer();
dataTransfer.items.add(croppedFile);
fileInput.files = dataTransfer.files;
});
},
const canvasEl = createElementFromHTML<CropperCanvas>(`
<cropper-canvas background>
<cropper-image src="${imageSource.src}" rotatable scalable skewable translatable></cropper-image>
<cropper-shade hidden></cropper-shade>
<cropper-handle action="select" plain></cropper-handle>
<cropper-selection initial-coverage="0.5" initial-aspect-ratio="1" movable resizable>
<cropper-grid role="grid" covered></cropper-grid>
<cropper-crosshair centered></cropper-crosshair>
<cropper-handle action="move" theme-color="#ffffff23"></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>
`);
const imgEl = canvasEl.querySelector<CropperImage>('cropper-image');
canvasEl.addEventListener('action', async (e) => {
const canvas = await (e.target as CropperCanvas).$toCanvas();
canvas.toBlob((blob) => {
const croppedFileName = currentFileName.replace(/\.[^.]{3,4}$/, '.png');
const croppedFile = new File([blob], croppedFileName, {type: 'image/png', lastModified: currentFileLastModified});
const dataTransfer = new DataTransfer();
dataTransfer.items.add(croppedFile);
fileInput.files = dataTransfer.files;
});
});
imageSource.replaceWith(canvasEl);
fileInput.addEventListener('input', (e: DOMEvent<Event, HTMLInputElement>) => {
const files = e.target.files;
if (files?.length > 0) {
@ -33,7 +55,7 @@ async function initCompCropper({container, fileInput, imageSource}: CropperOpts)
currentFileLastModified = files[0].lastModified;
const fileURL = URL.createObjectURL(files[0]);
imageSource.src = fileURL;
cropper.replace(fileURL);
imgEl.src = fileURL;
showElem(container);
}
});