I'm building an image cropper. I'm using rectangle to draw dynamic overlay. When overlay is out of image bounds i move it back to image bounds when pointer is up.
What i build
open var overlayRect: Rect =
Rect(offset = Offset.Zero, size = Size(size.width.toFloat(), size.height.toFloat()))
and i get final position using this function to move back to valid bounds
internal fun moveIntoBounds(rectBounds: Rect, rectCurrent: Rect): Rect {
var width = rectCurrent.width
var height = rectCurrent.height
if (width > rectBounds.width) {
width = rectBounds.width
}
if (height > rectBounds.height) {
height = rectBounds.height
}
var rect = Rect(offset = rectCurrent.topLeft, size = Size(width, height))
if (rect.left < rectBounds.left) {
rect = rect.translate(rectBounds.left - rect.left, 0f)
}
if (rect.top < rectBounds.top) {
rect = rect.translate(0f, rectBounds.top - rect.top)
}
if (rect.right > rectBounds.right) {
rect = rect.translate(rectBounds.right - rect.right, 0f)
}
if (rect.bottom > rectBounds.bottom) {
rect = rect.translate(0f, rectBounds.bottom - rect.bottom)
}
return rect
}
And set it on pointer up as
override fun onUp(change: PointerInputChange) {
touchRegion = TouchRegion.None
overlayRect = moveIntoBounds(rectBounds, overlayRect)
// Calculate crop rectangle
cropRect = calculateRectBounds()
rectTemp = overlayRect.copy()
}
How can i animate this rect to valid bounds? Is there way to use Animatable to animate a rect?
I checked official document for animation and suggestion is using transition
and transition.animateRect from one state to another but i don't have states i want to animate to a dynamic target from current dynamic value and this is a non-Composable class called DynamicCropState
that extends a class like zoom state here. Need to animate using Animatable or non-Composable apis.
I solved this creating an AnimationVector4D
that converts between Float
and Rect
by
val RectToVector = TwoWayConverter(
convertToVector = { rect: Rect ->
AnimationVector4D(rect.left, rect.top, rect.width, rect.height)
},
convertFromVector = { vector: AnimationVector4D ->
Rect(
offset = Offset(vector.v1, vector.v2),
size = Size(vector.v3, vector.v4)
)
}
)
For demonstration, created a class to animate internally and return current value of Rect
class RectWrapper {
private val animatableRect = Animatable(
Rect(
offset = Offset.Zero,
size = Size(300f, 300f)
),
RectToVector
)
val rect: Rect
get() = animatableRect.value
suspend fun animateRectTo(rect: Rect) {
animatableRect.animateTo(rect)
}
}
And a demonstration to show how to use it
@Composable
private fun AnimateRectWithAnimatable() {
val coroutineScope = rememberCoroutineScope()
val rectWrapper = remember {
RectWrapper()
}
Column(modifier = Modifier.fillMaxSize()) {
Button(
modifier = Modifier
.padding(10.dp)
.fillMaxWidth(),
onClick = {
coroutineScope.launch {
rectWrapper.animateRectTo(
Rect(
topLeft = Offset(200f, 200f),
bottomRight = Offset(800f, 800f)
)
)
}
}
) {
Text("Animate")
}
Canvas(
modifier = Modifier
.fillMaxSize()
) {
drawRect(
color = Color.Red,
topLeft = rectWrapper.rect.topLeft,
size = rectWrapper.rect.size
)
}
}
}
If you wish to animate a Rect
from a class you can implement it as above. I generally pass these classes to modifiers as State
and observe and trigger changes inside Modifier.composed
and return result to any class that uses that modifier.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.