You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetpack Compose坐标系工作原理及启动页Logo动画定位问题

解决方案

1. 移除全局坐标依赖,改用父容器相对定位

放弃positionInWindow()获取全局坐标,直接基于BoxWithConstraints的内部尺寸计算位置,避免跨平台下系统状态栏、窗口装饰对坐标的干扰。

2. 统一使用graphicsLayer处理缩放与偏移

Modifier.offset和Modifier.scale是独立变换,易出现位置偏差。改用Modifier.graphicsLayer并设置transformOrigin = TransformOrigin.TopLeft,让缩放以Logo左上角为原点,简化位置计算逻辑。

3. 同步动画参数,避免异步错位

将偏移和缩放参数封装到一个数据类中,用单个Animatable控制,确保三个动画属性同步执行。

4. 基于容器尺寸动态计算起始/目标值

  • 初始状态:Logo居中显示,计算公式为(容器宽度 - 原始Logo宽度×初始缩放)/2、(容器高度 - 原始Logo高度×初始缩放)/2。
  • 目标状态:Logo位于父容器左上角10dp处,缩放为0.2f,偏移值直接设为10dp对应的像素值。

修改后的代码

import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.graphicsLayer
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.IntSize

// 封装动画状态参数,统一控制偏移与缩放
private data class SplashAnimationState(
    val translationX: Float,
    val translationY: Float,
    val scale: Float
)

@Composable
fun SplashScreen(sizeProvider: WindowSizeProvider) {
    BoxWithConstraints(modifier = Modifier.fillMaxSize()) {
        val containerWidth = maxWidth.value
        val containerHeight = maxHeight.value
        val imgSize = IntSize(1460, 320) // 原始Logo尺寸(像素)
        val initialScale = 1f
        val finalScale = 0.2f
        val padding = 10.dp.toPx() // 目标位置距左上角的间距

        // 初始状态:Logo在容器中心
        val initialState = SplashAnimationState(
            translationX = (containerWidth - imgSize.width * initialScale) / 2,
            translationY = (containerHeight - imgSize.height * initialScale) / 2,
            scale = initialScale
        )

        // 目标状态:左上角偏移padding,缩放0.2f
        val targetState = SplashAnimationState(
            translationX = padding,
            translationY = padding,
            scale = finalScale
        )

        val animatableState = remember { Animatable(initialState) }
        val animationSpec = tween<SplashAnimationState>(
            durationMillis = 2000,
            easing = FastOutSlowInEasing
        )

        LaunchedEffect(Unit) {
            delay(2000)
            animatableState.animateTo(targetState, animationSpec)
        }

        Box(modifier = Modifier.fillMaxSize()) {
            Image(
                painter = painterResource(Res.drawable.logo_light),
                contentDescription = null,
                modifier = Modifier
                    .graphicsLayer(
                        translationX = animatableState.value.translationX,
                        translationY = animatableState.value.translationY,
                        scaleX = animatableState.value.scale,
                        scaleY = animatableState.value.scale,
                        transformOrigin = TransformOrigin.TopLeft // 以左上角为缩放原点,避免位置偏移
                    )
            )
        }
    }
}

关键改动说明

  • transformOrigin = TransformOrigin.TopLeft:设置缩放原点为Logo左上角,缩放时不会改变Logo左上角的相对位置,偏移计算更直观。
  • 封装动画状态:用单个Animatable控制所有动画参数,保证偏移和缩放同步执行,避免分开动画导致的错位。
  • 动态容器尺寸:直接使用BoxWithConstraints提供的maxWidth和maxHeight,无需额外存储状态,实时响应容器尺寸变化。
  • 移除全局坐标:不再依赖positionInWindow(),彻底解决跨平台下系统UI元素对坐标的干扰问题。

内容的提问来源于stack exchange,提问作者siliconeagle

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 18:40:22