-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Media Editor: Make zoom floor coverage-aware instead of fixed at 1x #78222
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
958312b
17b1793
b5d6b20
8f1b7ba
c765ed8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
…low 1 when containment math confirms the crop remains covered. This keeps 1x as the default/resting zoom, but removes the hard floor that prevented thin, tall crops on fine-rotated portrait images from filling the available space. Add coverage for: - sub-1 containment on fine-rotated portrait crops - settle behavior for tall rotated crops - export/server modifier parity at sub-1 zoom
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,7 @@ import { mat2d, vec2 } from 'gl-matrix'; | |
| * Internal dependencies | ||
| */ | ||
| import type { CropperState, NormalizedRect, Size } from './types'; | ||
| import { ABSOLUTE_MIN_ZOOM, MIN_ZOOM } from './constants'; | ||
| import { degreesToRadians } from './math/rotation'; | ||
| import { | ||
| isValidSize, | ||
|
|
@@ -44,18 +45,19 @@ function getVisualDimensions( | |
| } | ||
|
|
||
| /** | ||
| * Calculates the minimum zoom factor needed for the rotated image to fully cover | ||
| * the crop rectangle, using normalized coordinates and imageAspectRatio. | ||
| * | ||
| * Works in pixel-proportional space where the unrotated image is a×1. | ||
| * The crop rect is in visual-normalized space and must be scaled by the | ||
| * The geometric minimum zoom required for the rotated image to fully cover the | ||
| * crop rectangle. Works in pixel-proportional space where the unrotated image | ||
| * is a×1; the crop rect lives in visual-normalized space and is scaled by the | ||
| * rotation-dependent visual dimensions before projecting into the image-local | ||
| * frame. | ||
| * | ||
| * Returns 0 for a degenerate (zero-area) crop — callers compose the result | ||
| * with their own floor (typically the current zoom or `MIN_ZOOM`). | ||
| * | ||
| * @param rotation Rotation angle in degrees. | ||
| * @param imageAspectRatio Image width / height ratio. | ||
| * @param cropRect The crop rectangle in normalized coordinates. | ||
| * @return The minimum zoom factor (always >= 1). | ||
| * @return The minimum zoom factor needed for coverage. | ||
| */ | ||
| function getMinZoomForCover( | ||
| rotation: number, | ||
|
|
@@ -88,7 +90,27 @@ function getMinZoomForCover( | |
| const zoomFromAlpha = ( 2 * spanAlpha ) / aspectRatio; | ||
| const zoomFromBeta = 2 * spanBeta; | ||
|
|
||
| return Math.max( 1, zoomFromAlpha, zoomFromBeta ); | ||
| return Math.max( zoomFromAlpha, zoomFromBeta ); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the most important line, where the old hard 1x floor is removed from the geometric minimum. That enables the new behavior: zoom can go below 1 when coverage math says the crop is still fully covered.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for flagging the key part of this! Helps with the reading 👀 😄 |
||
| } | ||
|
|
||
| /** | ||
| * Resolves the effective minimum zoom for the current state — the coverage- | ||
| * aware floor when an image is loaded, falling back to `MIN_ZOOM` otherwise. | ||
| * Used by both the cropper's interaction layer and the zoom slider so the | ||
| * floor stays consistent across surfaces. | ||
| * | ||
| * @param state The current cropper state. | ||
| * @return The minimum zoom factor. | ||
| */ | ||
| export function getMinZoom( state: CropperState ): number { | ||
| if ( ! state.image ) { | ||
| return MIN_ZOOM; | ||
| } | ||
| const aspectRatio = state.image.naturalWidth / state.image.naturalHeight; | ||
| return Math.max( | ||
| ABSOLUTE_MIN_ZOOM, | ||
| getMinZoomForCover( state.rotation, aspectRatio, state.cropRect ) | ||
|
ramonjd marked this conversation as resolved.
Outdated
|
||
| ); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -300,10 +322,9 @@ export function restrictPanZoom( | |
| const aspectRatio = isValidSize( imageSize ) | ||
| ? imageSize.width / imageSize.height | ||
| : 1; | ||
| const minZoom = getMinZoomForCover( | ||
| safeState.rotation, | ||
| aspectRatio, | ||
| safeCropRect | ||
| const minZoom = Math.max( | ||
| ABSOLUTE_MIN_ZOOM, | ||
| getMinZoomForCover( safeState.rotation, aspectRatio, safeCropRect ) | ||
| ); | ||
| const zoom = Math.max( safeState.zoom, minZoom ); | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.