这一节主要了解一下Compose中使用withFrameNanos,withFrameNanos是一个用于精确控制动画帧回调的核心函数,属于Compose动画系统的API。它允许使用者在每一帧动画渲染时获取当前时间戳,并基于时间计算动画进度,从而手动实现高性能的自定义动画逻辑。
API:
withFrameNanos:是一个挂起函数,用于在协程中以帧级精度执行动画逻辑,并返回当前帧的时间戳

栗子:
Kotlin
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.translate
@Composable
fun WithFrameNanosDemo() {
var xPosition by remember { mutableStateOf(0f) }
LaunchedEffect(Unit) {
var lastFrameTime = 0L
while (true) {
withFrameNanos { frameTime ->
if (lastFrameTime != 0L) {
val deltaTime = (frameTime - lastFrameTime) / 1_000_000_000f
xPosition += 300 * deltaTime
if (xPosition > 1000f) xPosition = 0f
}
lastFrameTime = frameTime
}
}
}
Canvas(modifier = Modifier.fillMaxSize()) {
translate(left = xPosition) {
drawCircle(Color.Cyan, radius = 60f, center = Offset(200f, size.height / 2))
}
}
}
Kotlin
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.translate
import androidx.compose.ui.unit.dp
@Composable
fun PhysicsWithFrameNanosDemo() {
data class BallState(
val x: Float = 100f,
val y: Float = 200f,
val vx: Float = 12f,
val vy: Float = 0f,
)
val ball = remember { mutableStateOf(BallState()) }
val radius = 40.dp.value
LaunchedEffect(Unit) {
var lastTime = 0L
val gravity = 9.8f * 50
val bounce = 0.8f
while (true) {
withFrameNanos { frameTime ->
if (lastTime == 0L) {
lastTime = frameTime
return@withFrameNanos
}
val dt = (frameTime - lastTime) / 1_000_000_000f
lastTime = frameTime
var (x, y, vx, vy) = ball.value
vy += gravity * dt
x += vx
y += vy
val maxX = 1000f - radius * 2
val maxY = 2000f - radius * 2
if (x < 0 || x > maxX) vx *= -bounce
if (y > maxY) {
vy *= -bounce
y = maxY
}
ball.value = BallState(x, y, vx, vy)
}
}
}
Canvas(modifier = Modifier.fillMaxSize()) {
translate(ball.value.x, ball.value.y) {
drawCircle(Color.Magenta, radius)
}
}
}
注意
1 必须在协程中调用
2 避免阻塞主线程
3 时间单位转换 frameTimeNanos是纳秒(1e-9 秒),通常需要转换为毫秒或秒