Do not use `precise` anymore so that we only calculate with integers
authorCyperghost <olaf_schmitz_1@t-online.de>
Fri, 20 Dec 2024 11:35:50 +0000 (12:35 +0100)
committerCyperghost <olaf_schmitz_1@t-online.de>
Fri, 20 Dec 2024 11:35:50 +0000 (12:35 +0100)
ts/WoltLabSuite/Core/Component/Image/Cropper.ts
wcfsetup/install/files/js/WoltLabSuite/Core/Component/Image/Cropper.js

index 4c1048249d893f32a5840e7c3a8cb5e892ef8dc6..4504e558d85c7a3aec139e7f8ac6fda2a6ad77ff 100644 (file)
@@ -178,8 +178,8 @@ abstract class ImageCropper {
     const height = width / this.configuration.aspectRatio;
 
     return this.cropperSelection!.$toCanvas({
-      width: Math.max(Math.min(Math.floor(width), this.maxSize.width), this.minSize.width),
-      height: Math.max(Math.min(Math.ceil(height), this.maxSize.height), this.minSize.height),
+      width: Math.max(Math.min(Math.round(width), this.maxSize.width), this.minSize.width),
+      height: Math.max(Math.min(Math.round(height), this.maxSize.height), this.minSize.height),
     });
   }
 
@@ -212,13 +212,15 @@ abstract class ImageCropper {
 
       // Round all values to integers to avoid dealing with the wonderful world
       // of IEEE 754 numbers.
-      const minWidth = Math.round(this.minSize.width * selectionRatio);
+      const minWidth = Math.ceil(this.minSize.width * selectionRatio);
       const maxWidth = Math.round(this.cropperCanvasRect.width);
-      const minHeight = Math.round(minWidth / this.configuration.aspectRatio);
+      const minHeight = Math.ceil(minWidth / this.configuration.aspectRatio);
       const maxHeight = Math.round(maxWidth / this.configuration.aspectRatio);
 
       const width = Math.round(selection.width);
       const height = Math.round(selection.height);
+      const x = Math.round(selection.x);
+      const y = Math.round(selection.y);
 
       if (width < minWidth || height < minHeight || width > maxWidth || height > maxHeight) {
         event.preventDefault();
@@ -234,20 +236,19 @@ abstract class ImageCropper {
       const maxSelection: Selection = {
         x: 0,
         y: 0,
-        width: this.cropperCanvasRect.width,
-        height: this.cropperCanvasRect.height,
+        width: maxWidth,
+        height: Math.round(this.cropperCanvasRect.height),
       };
 
       if (!inSelection(selection, maxSelection)) {
         event.preventDefault();
-
         // Clamp the position to the boundaries of the canvas.
         void this.cropperSelection!.$nextTick().then(() => {
           this.cropperSelection!.$change(
-            clampValue(selection.x, selection.width, maxSelection.width),
-            clampValue(selection.y, selection.height, maxSelection.height),
-            selection.width,
-            selection.height,
+            clampValue(x, width, maxSelection.width),
+            clampValue(y, height, maxSelection.height),
+            width,
+            height,
           );
         });
       }
@@ -255,8 +256,6 @@ abstract class ImageCropper {
   }
 
   protected setCropperStyle() {
-    this.cropperCanvas!.style.aspectRatio = `${this.width}/${this.height}`;
-
     this.cropperSelection!.aspectRatio = this.configuration.aspectRatio;
   }
 
@@ -267,11 +266,15 @@ abstract class ImageCropper {
 
     const dimension = DomUtil.innerDimensions(this.cropperCanvas!.parentElement!);
     const ratio = Math.min(dimension.width / this.width, dimension.height / this.height);
+    const imageRatio = this.width / this.height;
 
-    this.cropperCanvas!.style.height = `${this.height * ratio}px`;
-    this.cropperCanvas!.style.width = `${this.width * ratio}px`;
+    const canvasHeight = Math.round(this.height * ratio);
+    const canvasWidth = Math.round(canvasHeight * imageRatio);
 
-    this.cropperImage!.$center("contain");
+    this.cropperCanvas!.style.height = `${canvasHeight}px`;
+    this.cropperCanvas!.style.width = `${canvasWidth}px`;
+
+    this.cropperImage!.$center("cover");
     this.cropperCanvasRect = this.cropperImage!.getBoundingClientRect();
 
     const selectionRatio = Math.min(
@@ -279,14 +282,15 @@ abstract class ImageCropper {
       this.cropperCanvasRect.height / this.maxSize.height,
     );
 
-    this.cropperSelection!.$change(
-      0,
-      0,
-      Math.min(this.cropperCanvasRect.width, this.maxSize.width * selectionRatio),
-      Math.min(this.cropperCanvasRect.height, this.maxSize.height * selectionRatio),
-      this.configuration.aspectRatio,
-      true,
-    );
+    let selectionHeight = Math.min(this.cropperCanvasRect.height, Math.floor(this.maxSize.height * selectionRatio));
+    let selectionWidth = Math.floor(selectionHeight * this.configuration.aspectRatio);
+
+    if (selectionWidth > this.cropperCanvasRect.width) {
+      selectionWidth = Math.floor(this.cropperCanvasRect.width);
+      selectionHeight = Math.floor(selectionWidth / this.configuration.aspectRatio);
+    }
+
+    this.cropperSelection!.$change(0, 0, selectionWidth, selectionHeight, this.configuration.aspectRatio, true);
 
     this.cropperSelection!.$center();
     this.cropperSelection!.scrollIntoView({ block: "center", inline: "center" });
@@ -297,7 +301,7 @@ abstract class ImageCropper {
   <cropper-image skewable scalable translatable rotatable></cropper-image>
   <cropper-shade hidden></cropper-shade>
   <cropper-handle action="scale" hidden disabled></cropper-handle>
-  <cropper-selection precise movable resizable outlined>
+  <cropper-selection 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>
index 4d8597286f88db084ad3186306e74468ea00408c..dc54f6c8b2db646209e3063d392816b57d58dd58 100644 (file)
@@ -134,8 +134,8 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/Image/Resizer", "WoltL
             const width = this.cropperSelection.width / selectionRatio;
             const height = width / this.configuration.aspectRatio;
             return this.cropperSelection.$toCanvas({
-                width: Math.max(Math.min(Math.floor(width), this.maxSize.width), this.minSize.width),
-                height: Math.max(Math.min(Math.ceil(height), this.maxSize.height), this.minSize.height),
+                width: Math.max(Math.min(Math.round(width), this.maxSize.width), this.minSize.width),
+                height: Math.max(Math.min(Math.round(height), this.maxSize.height), this.minSize.height),
             });
         }
         createCropper() {
@@ -157,12 +157,14 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/Image/Resizer", "WoltL
                 const selectionRatio = Math.min(this.cropperCanvasRect.width / this.width, this.cropperCanvasRect.height / this.height);
                 // Round all values to integers to avoid dealing with the wonderful world
                 // of IEEE 754 numbers.
-                const minWidth = Math.round(this.minSize.width * selectionRatio);
+                const minWidth = Math.ceil(this.minSize.width * selectionRatio);
                 const maxWidth = Math.round(this.cropperCanvasRect.width);
-                const minHeight = Math.round(minWidth / this.configuration.aspectRatio);
+                const minHeight = Math.ceil(minWidth / this.configuration.aspectRatio);
                 const maxHeight = Math.round(maxWidth / this.configuration.aspectRatio);
                 const width = Math.round(selection.width);
                 const height = Math.round(selection.height);
+                const x = Math.round(selection.x);
+                const y = Math.round(selection.y);
                 if (width < minWidth || height < minHeight || width > maxWidth || height > maxHeight) {
                     event.preventDefault();
                     // Stop the event handling here otherwise the following code would try
@@ -175,20 +177,19 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/Image/Resizer", "WoltL
                 const maxSelection = {
                     x: 0,
                     y: 0,
-                    width: this.cropperCanvasRect.width,
-                    height: this.cropperCanvasRect.height,
+                    width: maxWidth,
+                    height: Math.round(this.cropperCanvasRect.height),
                 };
                 if (!inSelection(selection, maxSelection)) {
                     event.preventDefault();
                     // Clamp the position to the boundaries of the canvas.
                     void this.cropperSelection.$nextTick().then(() => {
-                        this.cropperSelection.$change(clampValue(selection.x, selection.width, maxSelection.width), clampValue(selection.y, selection.height, maxSelection.height), selection.width, selection.height);
+                        this.cropperSelection.$change(clampValue(x, width, maxSelection.width), clampValue(y, height, maxSelection.height), width, height);
                     });
                 }
             });
         }
         setCropperStyle() {
-            this.cropperCanvas.style.aspectRatio = `${this.width}/${this.height}`;
             this.cropperSelection.aspectRatio = this.configuration.aspectRatio;
         }
         centerSelection() {
@@ -197,12 +198,21 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/Image/Resizer", "WoltL
             this.cropperCanvas.style.height = `${this.height}px`;
             const dimension = Util_1.default.innerDimensions(this.cropperCanvas.parentElement);
             const ratio = Math.min(dimension.width / this.width, dimension.height / this.height);
-            this.cropperCanvas.style.height = `${this.height * ratio}px`;
-            this.cropperCanvas.style.width = `${this.width * ratio}px`;
-            this.cropperImage.$center("contain");
+            const imageRatio = this.width / this.height;
+            const canvasHeight = Math.round(this.height * ratio);
+            const canvasWidth = Math.round(canvasHeight * imageRatio);
+            this.cropperCanvas.style.height = `${canvasHeight}px`;
+            this.cropperCanvas.style.width = `${canvasWidth}px`;
+            this.cropperImage.$center("cover");
             this.cropperCanvasRect = this.cropperImage.getBoundingClientRect();
             const selectionRatio = Math.min(this.cropperCanvasRect.width / this.maxSize.width, this.cropperCanvasRect.height / this.maxSize.height);
-            this.cropperSelection.$change(0, 0, Math.min(this.cropperCanvasRect.width, this.maxSize.width * selectionRatio), Math.min(this.cropperCanvasRect.height, this.maxSize.height * selectionRatio), this.configuration.aspectRatio, true);
+            let selectionHeight = Math.min(this.cropperCanvasRect.height, Math.floor(this.maxSize.height * selectionRatio));
+            let selectionWidth = Math.floor(selectionHeight * this.configuration.aspectRatio);
+            if (selectionWidth > this.cropperCanvasRect.width) {
+                selectionWidth = Math.floor(this.cropperCanvasRect.width);
+                selectionHeight = Math.floor(selectionWidth / this.configuration.aspectRatio);
+            }
+            this.cropperSelection.$change(0, 0, selectionWidth, selectionHeight, this.configuration.aspectRatio, true);
             this.cropperSelection.$center();
             this.cropperSelection.scrollIntoView({ block: "center", inline: "center" });
         }
@@ -211,7 +221,7 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/Image/Resizer", "WoltL
   <cropper-image skewable scalable translatable rotatable></cropper-image>
   <cropper-shade hidden></cropper-shade>
   <cropper-handle action="scale" hidden disabled></cropper-handle>
-  <cropper-selection precise movable resizable outlined>
+  <cropper-selection 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>