简体   繁体   中英

How to animate Rect position with Animatable?

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM