1 /* <lambda>null2 * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. 3 */ 4 5 package kotlinx.coroutines 6 7 import org.w3c.dom.Window 8 9 /** 10 * Converts an instance of [Window] to an implementation of [CoroutineDispatcher]. 11 */ 12 public fun Window.asCoroutineDispatcher(): CoroutineDispatcher = 13 @Suppress("UnsafeCastFromDynamic") 14 asDynamic().coroutineDispatcher ?: WindowDispatcher(this).also { 15 asDynamic().coroutineDispatcher = it 16 } 17 18 /** 19 * Suspends coroutine until next JS animation frame and returns frame time on resumption. 20 * The time is consistent with [window.performance.now()][org.w3c.performance.Performance.now]. 21 * This function is cancellable. If the [Job] of the current coroutine is completed while this suspending 22 * function is waiting, this function immediately resumes with [CancellationException]. 23 */ contnull24public suspend fun Window.awaitAnimationFrame(): Double = suspendCancellableCoroutine { cont -> 25 asWindowAnimationQueue().enqueue(cont) 26 } 27 Windownull28private fun Window.asWindowAnimationQueue(): WindowAnimationQueue = 29 @Suppress("UnsafeCastFromDynamic") 30 asDynamic().coroutineAnimationQueue ?: WindowAnimationQueue(this).also { 31 asDynamic().coroutineAnimationQueue = it 32 } 33 34 private class WindowAnimationQueue(private val window: Window) { 35 private val dispatcher = window.asCoroutineDispatcher() 36 private var scheduled = false 37 private var current = ArrayDeque<CancellableContinuation<Double>>() 38 private var next = ArrayDeque<CancellableContinuation<Double>>() 39 private var timestamp = 0.0 40 enqueuenull41 fun enqueue(cont: CancellableContinuation<Double>) { 42 next.addLast(cont) 43 if (!scheduled) { 44 scheduled = true 45 window.requestAnimationFrame { ts -> 46 timestamp = ts 47 val prev = current 48 current = next 49 next = prev 50 scheduled = false 51 process() 52 } 53 } 54 } 55 processnull56 fun process() { 57 while(true) { 58 val element = current.removeFirstOrNull() ?: return 59 with(element) { dispatcher.resumeUndispatched(timestamp) } 60 } 61 } 62 } 63