Boris Kachscovsky
06/29/2021, 11:31 AMmonotonicFrameClock
, but see some stuttering at the beginning of the animation. Any idea why this stuttering might be happening?
var progress by remember { mutableStateOf(0f) }
LaunchedEffect(Unit) {
while (true) {
val frameTime = withInfiniteAnimationFrameMillis { it }
progress = (frameTime % Duration).toFloat() / Duration
}
My guess is that it’s because the initial progress is set to 0? How can we sync that (from the start) with the frame time? Another possibility is that because LaunchedEffect
is launched after the first frame, we start one frame behind. Don’t think I can use DisposableEffect
(which doesn’t have this issue) as withInfiniteAnimationFrameMillis
is a suspend
function.Timo Drick
06/29/2021, 2:36 PMval start = withInfiniteAnimationFrameMillis { it }
while (true) {
val frameTime = withInfiniteAnimationFrameMillis { it } - start
progress = (frameTime % Duration).toFloat() / Duration
}
Boris Kachscovsky
06/29/2021, 3:04 PMTimo Drick
06/29/2021, 3:09 PMTimo Drick
06/29/2021, 3:14 PMBoris Kachscovsky
06/29/2021, 4:07 PMmonotonicFrameClock
which is a great resource for syncing these kinds of animations. I just need to know how to get access to it!Doris Liu
06/29/2021, 10:33 PMBoris Kachscovsky
06/30/2021, 8:24 AMDoris Liu
06/30/2021, 5:53 PMBoris Kachscovsky
07/01/2021, 5:32 AMJelle Fresen [G]
07/01/2021, 10:22 AMwithInfiniteAnimationFrameMillis
.
So, why can't you know the "current time" before that invocation to withInfiniteAnimationFrameMillis
? Well, the default implementation of Android's MonotonicFrameClock doesn't actually keep track of time. It just passes on the time it gets from the Choreographer to anyone who is suspended in withFrameNanos
. So it's kinda like a voice triggered clock without a face: it will tell you the time when you ask for it, but you can't see it.
(Note that a MonotonicFrameClock doesn't always run on the Choreographer's time. In particular, the clock used in tests uses the time from a TestCoroutineDispatcher.)
Hopefully this will give some insight into why things work the way they do.philglass
07/01/2021, 12:43 PMMonotonicFrameClock
holding on to & exposing the last frame time would be wrong/broken, or is it more a “this is just how it currently works” kinda thing?
There are quite a few uses for these globally synchronized types of animation (shimmer animations are another example, or Slack’s animated reactions!). At least in my mind the frame time is the natural way of implementing something like that, and there are already great test hooks in Compose for controlling it.Jelle Fresen [G]
07/01/2021, 1:25 PMwithFrameNanos
, but I would advise against that from a performance / battery life viewpoint.philglass
07/01/2021, 1:59 PMThe last known frame time would only be accurate (up to frame duration ms) if you’d keep posting frame callbacks to the Choreographer,A frame callback gives you the “time in nanoseconds when the frame started being rendered”. My (probably incorrect) mental model is that a layout/draw pass would always be preceded by a frame callback, so you’d always have a valid frame time during layout/draw (but not necessarily during regular composition?). What am I missing?
which wouldn’t be great for performance. If you really want to keep track of the time, you could start a coroutine that just keeps looping over withFrameNanos , but I would advise against that from a performance / battery life viewpoint.Oh, so Choreographer callbacks aren’t just passively observing frames, they have side effects? Would constantly posting them prevent the device skipping frames where there’s no work to do (e.g. no animations running, no invalidations)?
Jelle Fresen [G]
07/01/2021, 2:02 PMWould constantly posting them prevent the device skipping frames where there’s no work to doCorrect
Jelle Fresen [G]
07/01/2021, 2:04 PMso you’d always have a valid frame time during layout/draw (but not necessarily during regular composition?)Not necessarily for layout, as layout can be happen during other stages as well For draw I'm not quite sure how RenderThread interacts with that assumption