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 kotlinx.coroutines.internal.* 8 import org.w3c.dom.Window 9 10 /** 11 * Converts an instance of [Window] to an implementation of [CoroutineDispatcher]. 12 */ 13 public fun Window.asCoroutineDispatcher(): CoroutineDispatcher = 14 @Suppress("UnsafeCastFromDynamic") 15 asDynamic().coroutineDispatcher ?: WindowDispatcher(this).also { 16 asDynamic().coroutineDispatcher = it 17 } 18 19 /** 20 * Suspends coroutine until next JS animation frame and returns frame time on resumption. 21 * The time is consistent with [window.performance.now()][org.w3c.performance.Performance.now]. 22 * This function is cancellable. If the [Job] of the current coroutine is completed while this suspending 23 * function is waiting, this function immediately resumes with [CancellationException]. 24 */ contnull25public suspend fun Window.awaitAnimationFrame(): Double = suspendCancellableCoroutine { cont -> 26 asWindowAnimationQueue().enqueue(cont) 27 } 28 Windownull29private fun Window.asWindowAnimationQueue(): WindowAnimationQueue = 30 @Suppress("UnsafeCastFromDynamic") 31 asDynamic().coroutineAnimationQueue ?: WindowAnimationQueue(this).also { 32 asDynamic().coroutineAnimationQueue = it 33 } 34 35 private class WindowAnimationQueue(private val window: Window) { 36 private val dispatcher = window.asCoroutineDispatcher() 37 private var scheduled = false 38 private var current = ArrayQueue<CancellableContinuation<Double>>() 39 private var next = ArrayQueue<CancellableContinuation<Double>>() 40 private var timestamp = 0.0 41 enqueuenull42 fun enqueue(cont: CancellableContinuation<Double>) { 43 next.addLast(cont) 44 if (!scheduled) { 45 scheduled = true 46 window.requestAnimationFrame { ts -> 47 timestamp = ts 48 val prev = current 49 current = next 50 next = prev 51 scheduled = false 52 process() 53 } 54 } 55 } 56 processnull57 fun process() { 58 while(true) { 59 val element = current.removeFirstOrNull() ?: return 60 with(element) { dispatcher.resumeUndispatched(timestamp) } 61 } 62 } 63 } 64