}
public async showDialog(): Promise<File> {
- 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<File> {
- 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");
});
}
+ 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}`;
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();
+ }
+ });
}
}
#size?: { width: number; height: number };
public async showDialog(): Promise<File> {
- await this.loadImage();
-
// The image already has the correct size, cropping is not necessary
if (
this.image!.width == this.#size!.width &&
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 `<cropper-canvas background>
- <cropper-image></cropper-image>
- <cropper-shade hidden></cropper-shade>
- <cropper-selection movable outlined keyboard>
- <cropper-grid role="grid" bordered covered></cropper-grid>
- <cropper-crosshair centered></cropper-crosshair>
- <cropper-handle action="move" theme-color="rgba(255, 255, 255, 0.35)"></cropper-handle>
- </cropper-selection>
-</cropper-canvas>`;
- }
-
- protected async loadImage(): Promise<void> {
+ public async loadImage(): Promise<void> {
await super.loadImage();
const timeout = new Promise<File>((resolve) => {
);
}
+ protected getCropperTemplate(): string {
+ return `<div class="cropperContainer">
+ <cropper-canvas background>
+ <cropper-image></cropper-image>
+ <cropper-shade hidden></cropper-shade>
+ <cropper-selection movable outlined keyboard>
+ <cropper-grid role="grid" bordered covered></cropper-grid>
+ <cropper-crosshair centered></cropper-crosshair>
+ <cropper-handle action="move" theme-color="rgba(255, 255, 255, 0.35)"></cropper-handle>
+ </cropper-selection>
+ </cropper-canvas>
+</div>`;
+ }
+
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 `<cropper-canvas background>
- <cropper-image skewable scalable translatable></cropper-image>
- <cropper-shade hidden></cropper-shade>
- <cropper-handle action="move" plain></cropper-handle>
- <cropper-selection initial-coverage="0.5" movable resizable outlined>
- <cropper-grid role="grid" bordered 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>`;
+ return `<div class="cropperContainer">
+ <cropper-canvas background>
+ <cropper-image skewable scalable translatable></cropper-image>
+ <cropper-shade hidden></cropper-shade>
+ <cropper-handle action="move" plain></cropper-handle>
+ <cropper-selection initial-coverage="0.5" movable resizable outlined>
+ <cropper-grid role="grid" bordered 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>`;
}
protected getDialogExtra(): string {
throw new Error("Invalid configuration type");
}
+ await imageCropper.loadImage();
return imageCropper.showDialog();
}