From 8bd9c70032f3dc555189c0edc296f860b05f385f Mon Sep 17 00:00:00 2001 From: Cyperghost Date: Wed, 6 Nov 2024 09:26:32 +0100 Subject: [PATCH] Add `cropperContainer` around `cropper-canvas` --- .../Core/Component/Image/Cropper.ts | 168 +++++++++--------- 1 file changed, 84 insertions(+), 84 deletions(-) diff --git a/ts/WoltLabSuite/Core/Component/Image/Cropper.ts b/ts/WoltLabSuite/Core/Component/Image/Cropper.ts index 37843398e5..c0a5fc35a7 100644 --- a/ts/WoltLabSuite/Core/Component/Image/Cropper.ts +++ b/ts/WoltLabSuite/Core/Component/Image/Cropper.ts @@ -36,52 +36,12 @@ abstract class ImageCropper { } public async showDialog(): Promise { - await this.loadImage(); - this.dialog = dialogFactory().fromElement(this.image!).asPrompt({ extra: this.getDialogExtra(), }); this.dialog.show(getPhrase("wcf.upload.crop.image")); - return this.createCropper(); - } - - protected async createCropper(): Promise { - this.#cropper = new Cropper(this.image!, { - template: this.getCropperTemplate(), - }); - - this.cropperCanvas = this.#cropper.getCropperCanvas(); - this.cropperImage = this.#cropper.getCropperImage(); - this.cropperSelection = this.#cropper.getCropperSelection(); - - this.setCropperStyle(); - - this.cropperImage!.$center("contain"); - this.cropperSelection!.$center(); - - // Limit the selection to the canvas boundaries - this.cropperSelection!.addEventListener("change", (event: CustomEvent) => { - // see https://fengyuanchen.github.io/cropperjs/v2/api/cropper-selection.html#limit-boundaries - const cropperCanvasRect = this.cropperCanvas!.getBoundingClientRect(); - const selection = event.detail as Selection; - - const maxSelection: Selection = { - x: 0, - y: 0, - width: cropperCanvasRect.width, - height: cropperCanvasRect.height, - }; - - if ( - selection.x < maxSelection.x || - selection.y < maxSelection.y || - selection.x + selection.width > maxSelection.x + maxSelection.width || - selection.y + selection.height > maxSelection.y + maxSelection.height - ) { - event.preventDefault(); - } - }); + this.createCropper(); this.dialog.addEventListener("extra", () => { this.cropperImage!.$center("contain"); @@ -108,6 +68,12 @@ abstract class ImageCropper { }); } + public async loadImage() { + const { image, exif } = await this.resizer.loadFile(this.file); + this.image = image; + this.exif = exif; + } + protected setCropperStyle() { this.cropperCanvas!.style.aspectRatio = `${this.image!.width}/${this.image!.height}`; @@ -128,10 +94,42 @@ abstract class ImageCropper { return undefined; } - protected async loadImage() { - const { image, exif } = await this.resizer.loadFile(this.file); - this.image = image; - this.exif = exif; + protected createCropper() { + this.#cropper = new Cropper(this.image!, { + template: this.getCropperTemplate(), + }); + + this.cropperCanvas = this.#cropper.getCropperCanvas(); + this.cropperImage = this.#cropper.getCropperImage(); + this.cropperSelection = this.#cropper.getCropperSelection(); + + this.setCropperStyle(); + + this.cropperImage!.$center("contain"); + this.cropperSelection!.$center(); + + // Limit the selection to the canvas boundaries + this.cropperSelection!.addEventListener("change", (event: CustomEvent) => { + // see https://fengyuanchen.github.io/cropperjs/v2/api/cropper-selection.html#limit-boundaries + const cropperCanvasRect = this.cropperCanvas!.getBoundingClientRect(); + const selection = event.detail as Selection; + + const maxSelection: Selection = { + x: 0, + y: 0, + width: cropperCanvasRect.width, + height: cropperCanvasRect.height, + }; + + if ( + selection.x < maxSelection.x || + selection.y < maxSelection.y || + selection.x + selection.width > maxSelection.x + maxSelection.width || + selection.y + selection.height > maxSelection.y + maxSelection.height + ) { + event.preventDefault(); + } + }); } } @@ -139,8 +137,6 @@ class ExactImageCropper extends ImageCropper { #size?: { width: number; height: number }; public async showDialog(): Promise { - await this.loadImage(); - // The image already has the correct size, cropping is not necessary if ( this.image!.width == this.#size!.width && @@ -150,27 +146,10 @@ class ExactImageCropper extends ImageCropper { return this.resizer.saveFile({ exif: this.exif, image: this.image }, this.file.name, this.file.type); } - this.dialog = dialogFactory().fromElement(this.image!).asPrompt({ - extra: this.getDialogExtra(), - }); - this.dialog.show(getPhrase("wcf.upload.crop.image")); - - return this.createCropper(); + return super.showDialog(); } - protected getCropperTemplate(): string { - return ` - - - - - - - -`; - } - - protected async loadImage(): Promise { + public async loadImage(): Promise { await super.loadImage(); const timeout = new Promise((resolve) => { @@ -212,34 +191,54 @@ class ExactImageCropper extends ImageCropper { ); } + protected getCropperTemplate(): string { + return `
+ + + + + + + + + +
`; + } + protected setCropperStyle() { super.setCropperStyle(); this.cropperSelection!.width = this.#size!.width; this.cropperSelection!.height = this.#size!.height; + + this.cropperCanvas!.style.width = `${this.image!.width}px`; + this.cropperCanvas!.style.height = `${this.image!.height}px`; + this.cropperSelection!.style.removeProperty("aspectRatio"); } } class MinMaxImageCropper extends ImageCropper { protected getCropperTemplate(): string { - return ` - - - - - - - - - - - - - - - - -`; + return `
+ + + + + + + + + + + + + + + + + + +
`; } protected getDialogExtra(): string { @@ -266,5 +265,6 @@ export async function cropImage( throw new Error("Invalid configuration type"); } + await imageCropper.loadImage(); return imageCropper.showDialog(); } -- 2.20.1