消除列表元素旋转动画中的闪烁效果

问题描述 投票:0回答:1

我正在使用三个元素的列表,例如

[A, B, C]
,并且我正在尝试用它们创建旋转动画。该动画的顺序为 A B、B C、C A、A B 等等,无限重复。该列表还可以有 n 个对象,我希望它无限旋转。

但是,我在实现元素之间的无缝过渡方面遇到了挑战。似乎有明显的“闪烁”效果破坏了动画的流畅性。我正在寻求有关如何消除此问题的建议,或寻求实现相同效果的替代方法的建议。

我想要的效果如下图所示。但经过观察,您可能会注意到动画不连续,并且出现某种“闪烁”效果。

@Composable
fun FollowEventCardHome() {
    val data = "{...}" 
    val eventData = Gson().fromJson(data, EventData::class.java)
    var currentEvent by remember { mutableStateOf(eventData.events?.getOrNull(0)) }
    var nextEvent by remember { mutableStateOf(eventData.events?.getOrNull(1)) }
    var currentIndex by remember { mutableIntStateOf(0) }
    var isAnimate by remember { mutableStateOf(true) }

    val configuration = LocalConfiguration.current
    val cardWidth = configuration.screenWidthDp.dp - 55.dp

    if (isAnimate) {
        StartEndInfo(
            nextEvent = nextEvent,
            currentEvent = currentEvent,
            isAnimate = true,
            isAnimationFinished = {
                isAnimate = false
                if (eventData.events?.size!! > 1) {
                    if (currentIndex == eventData.events?.size!! - 1) {
                        currentIndex = 0
                    } else {
                        currentIndex++
                    }
                }
                currentEvent = eventData.events?.getOrNull(currentIndex)
                nextEvent = if (currentIndex < eventData.events?.size!!) {
                    eventData.events?.getOrNull(0)
                } else {
                    eventData.events?.getOrNull(currentIndex + 1)
                }
            }
        )
    }

    if(!isAnimate){
        StartEndInfo(
            nextEvent = nextEvent,
            currentEvent = currentEvent,
            isAnimate = true,
            isAnimationFinished = {
                isAnimate = true
                if (eventData.events?.size!! > 1) {
                    if (currentIndex == eventData.events?.size!! - 1) {
                        currentIndex = 0
                    } else {
                        currentIndex++
                    }
                }
                currentEvent = eventData.events?.getOrNull(currentIndex)
                nextEvent = if (currentIndex < eventData.events?.size!!) {
                    eventData.events?.getOrNull(0)
                } else {
                    eventData.events?.getOrNull(currentIndex + 1)
                }
            }
        )
    }
}

@Composable
fun StartEndInfo(
    nextEvent: Event? = null,
    currentEvent: Event? = null,
    isAnimate: Boolean,
    isAnimationFinished: () -> Unit
) {
    Row(
        modifier = Modifier
            .padding(
                start = 16.dp,
                bottom = 16.dp
            )
            .fillMaxWidth()
    ) {
        Column(modifier = Modifier.fillMaxSize()) {
            StartEndWithProgress(
                nextEvent = nextEvent,
                currentEvent = currentEvent,
                isAnimate = isAnimate,
                isAnimationFinished = isAnimationFinished
            )
        }
    }
}

@Composable
fun StartEndWithProgress(
    nextEvent: Event?,
    currentEvent: Event?,
    isAnimate: Boolean,
    isAnimationFinished: () -> Unit
) {
    val scalePreviousEvent = remember { Animatable(1f) }
    val scaleCurrentEvent = remember { Animatable(0f ) }

    LaunchedEffect(isAnimate) {
        scalePreviousEvent.animateTo(
            targetValue = 0f,
            animationSpec = tween(durationMillis = 4000)
        )
    }

    LaunchedEffect(key1 = isAnimate) {
        scaleCurrentEvent.animateTo(
            targetValue = 1f,
            animationSpec = tween(durationMillis = 4000)
        )
    }

    if(isAnimate && scalePreviousEvent.value == 0f ||  !isAnimate && scalePreviousEvent.value == 1f){
        isAnimationFinished()
    }

    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(horizontal = 30.dp)
            .padding(top = 150.dp),
        horizontalArrangement = Arrangement.SpaceBetween,
        verticalAlignment = Alignment.CenterVertically
    ) {
        Box {
            currentEvent?.start?.locationCode.let {
                Text(
                    text = currentEvent?.start?.locationCode ?: "",
                    fontSize = 20.sp,
                    modifier = Modifier.graphicsLayer {
                        scaleX = 1f
                        scaleY = if (isAnimate) {
                            scaleCurrentEvent.value
                        } else 1f
                        transformOrigin = TransformOrigin(0.5f, 1f)
                    }
                )
                Text(
                    text = nextEvent?.start?.locationCode ?: "",
                    fontSize = 20.sp,
                    modifier = Modifier.graphicsLayer {
                        scaleX = 1f
                        scaleY = if (isAnimate) {
                            scalePreviousEvent.value
                        } else 0f
                        transformOrigin = TransformOrigin(0.5f, 0f)
                    }
                )
            }
        }
        Box {
            currentEvent?.end?.locationCode.let {
                Text(
                    text = currentEvent?.end?.locationCode ?: "",
                    fontSize = 20.sp,
                    modifier = Modifier.graphicsLayer {
                        scaleX = 1f
                        scaleY = if (isAnimate) { scaleCurrentEvent.value } else 1f
                        transformOrigin = TransformOrigin(0.5f, 1f)
                    }
                )
            }
            Text(
                text = nextEvent?.end?.locationCode ?: "",
                fontSize = 20.sp,
                modifier = Modifier.graphicsLayer {
                    scaleX = 1f
                    scaleY = if (isAnimate) {
                        scalePreviousEvent.value
                    } else 0f
                    transformOrigin = TransformOrigin(0.5f, 0f)
                }
            )
        }
    }
}
android kotlin android-jetpack-compose android-animation
1个回答
0
投票

在不改变当前使用

Animatable
graphicsLayer
的方法的情况下,我只是将相关部分提取到专用的可组合项中并对其进行了一些简化:

@Composable
fun <T> AnimateRotatingList(
    list: List<T>,
    modifier: Modifier = Modifier,
    animationSpec: AnimationSpec<Float> = tween(durationMillis = 4000),
    itemContent: @Composable (item: T) -> Unit,
) {
    if (list.isEmpty()) return

    Box(modifier = modifier) {
        if (list.size == 1) {
            itemContent(list.first())
            return
        }

        var currentIndex by remember { mutableIntStateOf(0) }
        val current = remember(currentIndex) { list[currentIndex] }
        val next = remember(currentIndex) { list[(currentIndex + 1) % list.size] }

        val scale = remember { Animatable(1f) }

        LaunchedEffect(scale, animationSpec) {
            while (true) {
                scale.animateTo(
                    targetValue = 0f,
                    animationSpec = animationSpec,
                )
                scale.snapTo(1f)
                currentIndex = (currentIndex + 1) % list.size
            }
        }

        Box(modifier = Modifier.graphicsLayer {
            scaleX = 1f
            scaleY = scale.value
            transformOrigin = TransformOrigin(1f, 0f)
        }) {
            itemContent(current)
        }

        Box(modifier = Modifier.graphicsLayer {
            scaleX = 1f
            scaleY = 1 - scale.value
            transformOrigin = TransformOrigin(1f, 1f)
        }) {
            itemContent(next)
        }
    }
}

我不知道你的错误到底在哪里,但这个清理后的版本现在可以按预期工作:

@Composable
fun FollowEventCardHome() {
    val eventData = TODO()

    AnimateRotatingList(
        list = eventData.events.orEmpty(),
        modifier = Modifier
            .padding(top = 150.dp)
    ) {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .padding(horizontal = 30.dp),
            horizontalArrangement = Arrangement.SpaceBetween,
            verticalAlignment = Alignment.CenterVertically,
        ) {
            Text(
                text = it.start.locationCode,
                fontSize = 20.sp,
            )
            Text(
                text = it.end.locationCode,
                fontSize = 20.sp,
            )
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.