mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-14 18:12:09 +08:00
96 lines
2.8 KiB
TypeScript
96 lines
2.8 KiB
TypeScript
import { queueMacrotask } from '../utils/macrotask-scheduler';
|
|
import { FPSCallback } from '../fps-meter/fps-native';
|
|
import { getTimeInFrameBase } from './animation-native';
|
|
import { Trace } from 'trace';
|
|
|
|
export interface FrameRequestCallback {
|
|
(time: number): void;
|
|
}
|
|
|
|
type AnimationFrameCallbacks = { [key: string]: FrameRequestCallback };
|
|
|
|
let animationId = 0;
|
|
let currentFrameAnimationCallbacks: AnimationFrameCallbacks = {}; // requests that were scheduled in this frame and must be called ASAP
|
|
let currentFrameScheduled = false;
|
|
let nextFrameAnimationCallbacks: AnimationFrameCallbacks = {}; // requests there were scheduled in another request and must be called in the next frame
|
|
let shouldStop = true;
|
|
let inAnimationFrame = false;
|
|
let fpsCallback: FPSCallback;
|
|
let lastFrameTime = 0;
|
|
|
|
function getNewId() {
|
|
return animationId++;
|
|
}
|
|
|
|
function ensureNative() {
|
|
if (fpsCallback) {
|
|
return;
|
|
}
|
|
fpsCallback = new FPSCallback(doFrame);
|
|
}
|
|
|
|
function callAnimationCallbacks(thisFrameCbs: AnimationFrameCallbacks, frameTime: number): void {
|
|
inAnimationFrame = true;
|
|
for (const animationId in thisFrameCbs) {
|
|
if (thisFrameCbs[animationId]) {
|
|
try {
|
|
thisFrameCbs[animationId](frameTime);
|
|
} catch (err) {
|
|
const msg = err ? err.stack || err : err;
|
|
Trace.write(`Error in requestAnimationFrame: ${msg}`, Trace.categories.Error, Trace.messageType.error);
|
|
}
|
|
}
|
|
}
|
|
inAnimationFrame = false;
|
|
}
|
|
|
|
function doCurrentFrame() {
|
|
// if we're not getting accurate frame times
|
|
// set last frame time as the current time
|
|
if (!fpsCallback || !fpsCallback.running) {
|
|
lastFrameTime = getTimeInFrameBase();
|
|
}
|
|
currentFrameScheduled = false;
|
|
const thisFrameCbs = currentFrameAnimationCallbacks;
|
|
currentFrameAnimationCallbacks = {};
|
|
callAnimationCallbacks(thisFrameCbs, lastFrameTime);
|
|
}
|
|
|
|
function doFrame(currentTimeMillis: number) {
|
|
lastFrameTime = currentTimeMillis;
|
|
shouldStop = true;
|
|
const thisFrameCbs = nextFrameAnimationCallbacks;
|
|
nextFrameAnimationCallbacks = {};
|
|
callAnimationCallbacks(thisFrameCbs, lastFrameTime);
|
|
if (shouldStop) {
|
|
fpsCallback.stop(); // TODO: check performance without stopping to allow consistent frame times
|
|
}
|
|
}
|
|
|
|
function ensureCurrentFrameScheduled() {
|
|
if (!currentFrameScheduled) {
|
|
currentFrameScheduled = true;
|
|
queueMacrotask(doCurrentFrame);
|
|
}
|
|
}
|
|
|
|
export function requestAnimationFrame(cb: FrameRequestCallback): number {
|
|
const animId = getNewId();
|
|
if (!inAnimationFrame) {
|
|
ensureCurrentFrameScheduled();
|
|
currentFrameAnimationCallbacks[animId] = zonedCallback(cb) as FrameRequestCallback;
|
|
return animId;
|
|
}
|
|
ensureNative();
|
|
nextFrameAnimationCallbacks[animId] = zonedCallback(cb) as FrameRequestCallback;
|
|
shouldStop = false;
|
|
fpsCallback.start();
|
|
|
|
return animId;
|
|
}
|
|
|
|
export function cancelAnimationFrame(id: number): void {
|
|
delete currentFrameAnimationCallbacks[id];
|
|
delete nextFrameAnimationCallbacks[id];
|
|
}
|