Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 39b7cc1f authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "PagerDots animation fix" into main

parents 6d2c21ee db468ddf
Loading
Loading
Loading
Loading
+50 −52
Original line number Diff line number Diff line
@@ -31,9 +31,11 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.drawscope.scale
import androidx.compose.ui.semantics.pageLeft
import androidx.compose.ui.semantics.pageRight
import androidx.compose.ui.semantics.semantics
@@ -60,68 +62,64 @@ fun PagerDots(
    val inPageTransition by
        remember(pagerState) {
            derivedStateOf {
                pagerState.currentPageOffsetFraction.absoluteValue > 0.01 &&
                pagerState.currentPageOffsetFraction.absoluteValue > 0.05 &&
                    !pagerState.isOverscrolling()
            }
        }
    val coroutineScope = rememberCoroutineScope()
    Row(
        modifier =
            modifier
                .wrapContentWidth()
                .pagerDotsSemantics(
                    pagerState,
                    coroutineScope,
                ),
        horizontalArrangement = spacedBy(spaceSize),
        verticalAlignment = Alignment.CenterVertically
    ) {
        if (!inPageTransition) {
            repeat(pagerState.pageCount) { i ->
                // We use canvas directly to only invalidate the draw phase when the page is
                // changing.
                Canvas(Modifier.size(dotSize)) {
                    if (pagerState.currentPage == i) {
                        drawCircle(activeColor)
                    } else {
                        drawCircle(nonActiveColor)
                    }
                }
            }
        } else {
    val doubleDotWidth = dotSize * 2 + spaceSize
    val activeMarkerWidth by
        animateDpAsState(
            targetValue = if (inPageTransition) doubleDotWidth else dotSize,
            label = "PagerDotsTransitionAnimation",
        )
    val cornerRadius = dotSize / 2
            val width by
                animateDpAsState(targetValue = if (inPageTransition) doubleDotWidth else dotSize)

            fun DrawScope.drawDoubleRect() {
    fun DrawScope.drawDoubleRect(withPrevious: Boolean, width: Dp) {
        drawRoundRect(
            topLeft =
                Offset(
                    if (withPrevious) {
                        dotSize.toPx() - width.toPx()
                    } else {
                        -(dotSize.toPx() + spaceSize.toPx())
                    },
                    0f,
                ),
            color = activeColor,
            size = Size(width.toPx(), dotSize.toPx()),
                    cornerRadius = CornerRadius(cornerRadius.toPx(), cornerRadius.toPx())
            cornerRadius = CornerRadius(cornerRadius.toPx()),
        )
    }

    Row(
        modifier = modifier.wrapContentWidth().pagerDotsSemantics(pagerState, coroutineScope),
        horizontalArrangement = spacedBy(spaceSize),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        // This means that the active rounded rect has to be drawn between the current page
        // and the previous one (as we are animating back), or the current one if not transitioning
        val withPrevious = pagerState.currentPageOffsetFraction <= 0 || pagerState.isOverscrolling()
        repeat(pagerState.pageCount) { page ->
            Canvas(Modifier.size(dotSize)) {
                    val withPrevious = pagerState.currentPageOffsetFraction < 0
                    val ltr = layoutDirection == LayoutDirection.Ltr
                val rtl = layoutDirection == LayoutDirection.Rtl
                scale(if (rtl) -1f else 1f, 1f, Offset(0f, center.y)) {
                    drawCircle(nonActiveColor)
                    // We always want to draw the rounded rect on the rightmost dot iteration, so
                    // the inactive dot is always drawn behind.
                    // This means that:
                    // * if we are scrolling back, we draw it when we are in the current page (so it
                    //   extends between this page and the previous one).
                    // * if we are scrolling forward, we draw it when we are in the next page (so it
                    //   extends between the next page and the current one).
                    // * if we are not scrolling, withPrevious is true (pageOffset 0) and we
                    //   draw in the current page.
                    // drawDoubleRect calculates the offset based on the above.
                    if (
                        withPrevious && page == (pagerState.currentPage - 1) ||
                            !withPrevious && page == pagerState.currentPage
                    ) {
                        if (ltr) {
                            drawDoubleRect()
                        }
                    } else if (
                        withPrevious && page == pagerState.currentPage ||
                            !withPrevious && page == (pagerState.currentPage + 1)
                            (!withPrevious && page == pagerState.currentPage + 1)
                    ) {
                        if (!ltr) {
                            drawDoubleRect()
                        }
                    } else {
                        drawCircle(nonActiveColor)
                        drawDoubleRect(withPrevious, activeMarkerWidth)
                    }
                }
            }