From ee61e6084a5b9d999bcd0c124ce5b7c08c749ae9 Mon Sep 17 00:00:00 2001 From: Aaron Jorbin Date: Tue, 8 Oct 2024 22:27:44 +0000 Subject: [PATCH] Customizer: Do not force users to go through the cropping flow if the image is the correct ratio. If a user uploads an 800x800 image and a 512x512 image is required, then they should be allowed to skip cropping. This still creates the correct crop behind the scenes but simplifies the flow a bit for users. Props nirajgirixd, celloexpressions, westonruter, azaozz, jorbin. Fixes #36441. git-svn-id: https://develop.svn.wordpress.org/trunk@59197 602fd350-edb4-49c9-b593-d223f7449a82 --- src/js/_enqueues/wp/customize/controls.js | 62 +++++++++++++++-------- src/js/media/controllers/cropper.js | 32 +++++++++--- 2 files changed, 67 insertions(+), 27 deletions(-) diff --git a/src/js/_enqueues/wp/customize/controls.js b/src/js/_enqueues/wp/customize/controls.js index 5ed89afcade46..d367c6b7c1269 100644 --- a/src/js/_enqueues/wp/customize/controls.js +++ b/src/js/_enqueues/wp/customize/controls.js @@ -4602,26 +4602,29 @@ * @return {Object} Options */ calculateImageSelectOptions: function( attachment, controller ) { - var control = controller.get( 'control' ), - flexWidth = !! parseInt( control.params.flex_width, 10 ), - flexHeight = !! parseInt( control.params.flex_height, 10 ), - realWidth = attachment.get( 'width' ), - realHeight = attachment.get( 'height' ), - xInit = parseInt( control.params.width, 10 ), - yInit = parseInt( control.params.height, 10 ), - ratio = xInit / yInit, - xImg = xInit, - yImg = yInit, + var control = controller.get( 'control' ), + flexWidth = !! parseInt( control.params.flex_width, 10 ), + flexHeight = !! parseInt( control.params.flex_height, 10 ), + realWidth = attachment.get( 'width' ), + realHeight = attachment.get( 'height' ), + xInit = parseInt( control.params.width, 10 ), + yInit = parseInt( control.params.height, 10 ), + requiredRatio = xInit / yInit, + realRatio = realWidth / realHeight, + xImg = xInit, + yImg = yInit, x1, y1, imgSelectOptions; + controller.set( 'hasRequiredAspectRatio', control.hasRequiredAspectRatio( requiredRatio, realRatio ) ); + controller.set( 'suggestedCropSize', { width: realWidth, height: realHeight, x1: 0, y1: 0, x2: xInit, y2: yInit } ); controller.set( 'canSkipCrop', ! control.mustBeCropped( flexWidth, flexHeight, xInit, yInit, realWidth, realHeight ) ); - if ( realWidth / realHeight > ratio ) { + if ( realRatio > requiredRatio ) { yInit = realHeight; - xInit = yInit * ratio; + xInit = yInit * requiredRatio; } else { xInit = realWidth; - yInit = xInit / ratio; + yInit = xInit / requiredRatio; } x1 = ( realWidth - xInit ) / 2; @@ -4662,13 +4665,13 @@ /** * Return whether the image must be cropped, based on required dimensions. * - * @param {boolean} flexW - * @param {boolean} flexH - * @param {number} dstW - * @param {number} dstH - * @param {number} imgW - * @param {number} imgH - * @return {boolean} + * @param {boolean} flexW Width is flexible. + * @param {boolean} flexH Height is flexible. + * @param {number} dstW Required width. + * @param {number} dstH Required height. + * @param {number} imgW Provided image's width. + * @param {number} imgH Provided image's height. + * @return {boolean} Whether cropping is required. */ mustBeCropped: function( flexW, flexH, dstW, dstH, imgW, imgH ) { if ( true === flexW && true === flexH ) { @@ -4694,6 +4697,25 @@ return true; }, + /** + * Check if the image's aspect ratio essentially matches the required aspect ratio. + * + * Floating point precision is low, so this allows a small tolerance. This + * tolerance allows for images over 100,000 px on either side to still trigger + * the cropping flow. + * + * @param {number} requiredRatio Required image ratio. + * @param {number} realRatio Provided image ratio. + * @return {boolean} Whether the image has the required aspect ratio. + */ + hasRequiredAspectRatio: function ( requiredRatio, realRatio ) { + if ( Math.abs( requiredRatio - realRatio ) < 0.000001 ) { + return true; + } + + return false; + }, + /** * If cropping was skipped, apply the image data directly to the setting. */ diff --git a/src/js/media/controllers/cropper.js b/src/js/media/controllers/cropper.js index ef16752db4d2c..b0a7a394400e5 100644 --- a/src/js/media/controllers/cropper.js +++ b/src/js/media/controllers/cropper.js @@ -93,9 +93,11 @@ Cropper = wp.media.controller.State.extend(/** @lends wp.media.controller.Croppe * @return {void} */ createCropToolbar: function() { - var canSkipCrop, toolbarOptions; + var canSkipCrop, hasRequiredAspectRatio, suggestedCropSize, toolbarOptions; - canSkipCrop = this.get('canSkipCrop') || false; + suggestedCropSize = this.get( 'suggestedCropSize' ); + hasRequiredAspectRatio = this.get( 'hasRequiredAspectRatio' ); + canSkipCrop = this.get( 'canSkipCrop' ) || false; toolbarOptions = { controller: this.frame, @@ -127,7 +129,7 @@ Cropper = wp.media.controller.State.extend(/** @lends wp.media.controller.Croppe } }; - if ( canSkipCrop ) { + if ( canSkipCrop || hasRequiredAspectRatio ) { _.extend( toolbarOptions.items, { skip: { style: 'secondary', @@ -135,10 +137,26 @@ Cropper = wp.media.controller.State.extend(/** @lends wp.media.controller.Croppe priority: 70, requires: { library: false, selection: false }, click: function() { - var selection = this.controller.state().get('selection').first(); - this.controller.state().cropperView.remove(); - this.controller.trigger('skippedcrop', selection); - this.controller.close(); + var controller = this.controller, + selection = controller.state().get( 'selection' ).first(); + + controller.state().cropperView.remove(); + + // Apply the suggested crop size. + if ( hasRequiredAspectRatio && !canSkipCrop ) { + selection.set({cropDetails: suggestedCropSize}); + controller.state().doCrop( selection ).done( function( croppedImage ) { + controller.trigger( 'cropped', croppedImage ); + controller.close(); + }).fail( function() { + controller.trigger( 'content:error:crop' ); + }); + return; + } + + // Skip the cropping process. + controller.trigger( 'skippedcrop', selection ); + controller.close(); } } });