diff options
Diffstat (limited to 'ext/js/core/promise-animation-frame.js')
-rw-r--r-- | ext/js/core/promise-animation-frame.js | 62 |
1 files changed, 62 insertions, 0 deletions
diff --git a/ext/js/core/promise-animation-frame.js b/ext/js/core/promise-animation-frame.js new file mode 100644 index 00000000..0bcd6970 --- /dev/null +++ b/ext/js/core/promise-animation-frame.js @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2023-2024 Yomitan Authors + * Copyright (C) 2019-2022 Yomichan Authors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +/** + * Creates a promise that will resolve after the next animation frame, using `requestAnimationFrame`. + * @param {number} [timeout] A maximum duration (in milliseconds) to wait until the promise resolves. If null or omitted, no timeout is used. + * @returns {Promise<{time: number, timeout: boolean}>} A promise that is resolved with `{time, timeout}`, where `time` is the timestamp from `requestAnimationFrame`, + * and `timeout` is a boolean indicating whether the cause was a timeout or not. + * @throws The promise throws an error if animation is not supported in this context, such as in a service worker. + */ +export function promiseAnimationFrame(timeout) { + return new Promise((resolve, reject) => { + if (typeof cancelAnimationFrame !== 'function' || typeof requestAnimationFrame !== 'function') { + reject(new Error('Animation not supported in this context')); + return; + } + + /** @type {?import('core').Timeout} */ + let timer = null; + /** @type {?number} */ + let frameRequest = null; + /** + * @param {number} time + */ + const onFrame = (time) => { + frameRequest = null; + if (timer !== null) { + clearTimeout(timer); + timer = null; + } + resolve({time, timeout: false}); + }; + const onTimeout = () => { + timer = null; + if (frameRequest !== null) { + cancelAnimationFrame(frameRequest); + frameRequest = null; + } + resolve({time: performance.now(), timeout: true}); + }; + + frameRequest = requestAnimationFrame(onFrame); + if (typeof timeout === 'number') { + timer = setTimeout(onTimeout, timeout); + } + }); +} |