Skip to content

Commit bdd2aaf

Browse files
add zoom change to animateToValidBounds
- Update function name to animateTransformationToOverlayBounds - Add zoom change if overlay size has changed when pointer is up
1 parent 69b236c commit bdd2aaf

File tree

2 files changed

+74
-29
lines changed

2 files changed

+74
-29
lines changed

cropper/src/main/java/com/smarttoolfactory/cropper/state/CropStateImpl.kt

Lines changed: 71 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import androidx.compose.ui.input.pointer.PointerInputChange
99
import androidx.compose.ui.unit.IntOffset
1010
import androidx.compose.ui.unit.IntRect
1111
import androidx.compose.ui.unit.IntSize
12-
import com.smarttoolfactory.cropper.CropProperties
1312
import com.smarttoolfactory.cropper.model.AspectRatio
1413
import com.smarttoolfactory.cropper.model.CropData
14+
import com.smarttoolfactory.cropper.settings.CropProperties
1515

1616
val CropState.cropData: CropData
1717
get() = CropData(
@@ -96,25 +96,24 @@ abstract class CropState internal constructor(
9696
pannable = cropProperties.pannable
9797
zoomable = cropProperties.zoomable
9898
rotatable = cropProperties.rotatable
99-
zoomMax = cropProperties.maxZoom
10099

101-
val newZoom = zoom.coerceAtMost(zoomMax)
102-
animatableZoom.updateBounds(zoomMin, newZoom)
103-
104-
resetWithAnimation(zoom = newZoom)
100+
// TODO Fix zoom reset
101+
// zoomMax = cropProperties.maxZoom
105102

106103
// Update overlay rectangle
107104
val aspectRatio = cropProperties.aspectRatio
108-
animateOverlayRectTo( getOverlayFromAspectRatio(
109-
containerSize.width.toFloat(),
110-
containerSize.height.toFloat(),
111-
drawAreaRect.size.width,
112-
drawAreaRect.size.height,
113-
aspectRatio
114-
))
105+
animateOverlayRectTo(
106+
getOverlayFromAspectRatio(
107+
containerSize.width.toFloat(),
108+
containerSize.height.toFloat(),
109+
drawAreaRect.size.width,
110+
drawAreaRect.size.height,
111+
aspectRatio
112+
)
113+
)
115114

116115
// Update image draw area
117-
updateImageDrawAreaRectFromTransformation()
116+
updateImageDrawRectFromTransformation()
118117
}
119118

120119
/**
@@ -166,9 +165,21 @@ abstract class CropState internal constructor(
166165

167166
/**
168167
* Update rectangle for area that image is drawn. This rect changes when zoom and
169-
* pan changes and position of image changes on screen as result of transformation
168+
* pan changes and position of image changes on screen as result of transformation.
169+
*
170+
* This function is called
171+
*
172+
* * when [onGesture] is called to update rect when zoom or pan changes
173+
* and if [fling] is true just after **fling** gesture starts with target
174+
* value in [StaticCropState].
175+
*
176+
* * when [updateProperties] is called in [CropState]
177+
*
178+
* * when [onUp] is called in [DynamicCropState] to match [overlayRect] that could be
179+
* changed and animated if it's out of [containerSize] bounds or its grow
180+
* bigger than previous size
170181
*/
171-
internal fun updateImageDrawAreaRectFromTransformation() {
182+
internal fun updateImageDrawRectFromTransformation() {
172183
val containerWidth = containerSize.width
173184
val containerHeight = containerSize.height
174185

@@ -200,26 +211,49 @@ abstract class CropState internal constructor(
200211
* Resets to bounds with animation and resets tracking for fling animation.
201212
* Changes pan, zoom and rotation to valid bounds based on [drawAreaRect] and [overlayRect]
202213
*/
203-
internal suspend fun animateToValidBounds() {
214+
internal suspend fun animateTransformationToOverlayBounds() {
204215

205216
val zoom = zoom.coerceAtLeast(1f)
206217

207218
// Calculate new pan based on overlay
208-
val newRect = calculateValidImageDrawRect(overlayRect, drawAreaRect)
219+
val newDrawAreaRect = calculateValidImageDrawRect(overlayRect, drawAreaRect)
209220

210-
val leftChange = newRect.left - drawAreaRect.left
211-
val topChange = newRect.top - drawAreaRect.top
221+
val newZoom =
222+
calculateNewZoom(oldRect = drawAreaRect, newRect = newDrawAreaRect, zoom = zoom)
212223

213-
// Update draw area based on new pan and zoom values
214-
drawAreaRect = newRect
224+
val leftChange = newDrawAreaRect.left - drawAreaRect.left
225+
val topChange = newDrawAreaRect.top - drawAreaRect.top
226+
227+
val widthChange = newDrawAreaRect.width - drawAreaRect.width
228+
val heightChange = newDrawAreaRect.height - drawAreaRect.height
215229

216-
val newPanX = pan.x + leftChange
217-
val newPanY = pan.y + topChange
230+
val panXChange = leftChange + widthChange / 2
231+
val panYChange = topChange + heightChange / 2
218232

219-
resetWithAnimation(pan = Offset(newPanX, newPanY), zoom = zoom)
233+
val newPanX = pan.x + panXChange
234+
val newPanY = pan.y + panYChange
235+
236+
// Update draw area based on new pan and zoom values
237+
drawAreaRect = newDrawAreaRect
238+
239+
resetWithAnimation(pan = Offset(newPanX, newPanY), zoom = newZoom)
220240
resetTracking()
221241
}
222242

243+
/**
244+
* If new overlay is bigger, when crop type is dynamic, we need to increase zoom at least
245+
* size of bigger dimension for image draw area([drawAreaRect]) to cover overlay([overlayRect])
246+
*/
247+
private fun calculateNewZoom(oldRect: Rect, newRect: Rect, zoom: Float): Float {
248+
249+
val widthChange = (newRect.width / oldRect.width)
250+
.coerceAtLeast(1f)
251+
val heightChange = (newRect.height / oldRect.height)
252+
.coerceAtLeast(1f)
253+
254+
return widthChange.coerceAtLeast(heightChange) * zoom
255+
}
256+
223257
/**
224258
* Calculate valid position for image draw rectangle when pointer is up. Overlay rectangle
225259
* should fit inside draw image rectangle to have valid bounds when calculation is completed.
@@ -229,7 +263,18 @@ abstract class CropState internal constructor(
229263
*/
230264
private fun calculateValidImageDrawRect(rectOverlay: Rect, rectImage: Rect): Rect {
231265

232-
var rectImageArea = rectImage.copy()
266+
var width = rectImage.width
267+
var height = rectImage.height
268+
269+
if (width < rectOverlay.width) {
270+
width = rectOverlay.width
271+
}
272+
273+
if (height < rectOverlay.height) {
274+
height = rectOverlay.height
275+
}
276+
277+
var rectImageArea = Rect(offset = rectImage.topLeft, size = Size(width, height))
233278

234279
if (rectImageArea.left > rectOverlay.left) {
235280
rectImageArea = rectImageArea.translate(rectOverlay.left - rectImageArea.left, 0f)

cropper/src/main/java/com/smarttoolfactory/cropper/state/StaticCropState.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ class StaticCropState internal constructor(
7373
)
7474

7575
// Update image draw rectangle based on pan, zoom or rotation change
76-
updateImageDrawAreaRectFromTransformation()
76+
updateImageDrawRectFromTransformation()
7777

7878
// Fling Gesture
7979
if (fling) {
@@ -95,14 +95,14 @@ class StaticCropState internal constructor(
9595
fling {
9696
// We get target value on start instead of updating bounds after
9797
// gesture has finished
98-
updateImageDrawAreaRectFromTransformation()
98+
updateImageDrawRectFromTransformation()
9999
onBoundsCalculated()
100100
}
101101
} else {
102102
onBoundsCalculated()
103103
}
104104

105-
animateToValidBounds()
105+
animateTransformationToOverlayBounds()
106106
}
107107
}
108108

0 commit comments

Comments
 (0)