我使用requestAnimationFrame循环创建了一个函数,它的工作原理与普通的JS setTimeout函数非常相似(除了可以选择在持续时间内提供自定义的启动时间,并且返回完成时间)。我想知道这是否是一种理想的写作方式。特别是,在定义主对象时,我遇到了一些自我引用的问题,所以我不得不分别定义它们。(也许创建类更有意义,但我希望尽可能地保持它的简单性。)
const DT = {
// initiate RTOloop ("setTimeout" via RAF loop)
// actually defined below, because of self-reference
setRTO: undefined,
// RTOloop actually defined below, because of self-reference
RTOloop: undefined,
// start time for setRTOLoop
RTOstart: undefined,
// duration for setRTOLoop
RTOduration: undefined,
// callback function for setRTOLoop
RTOcallback: undefined,
// now: gets the appropriate version of "performance.now()"
// normally available in all modern browsers, but it can resort to the still precise Date()
// see https://developer.mozilla.org/en-US/docs/Web/API/Performance/now
now: function() {
let performance = window.performance || {};
performance.now = (function() {
return (
performance.now ||
performance.webkitNow ||
performance.msNow ||
performance.oNow ||
performance.mozNow ||
function() {
return new Date().getTime();
}
);
})();
return performance.now();
}
};
// add the RAF Time-Out (RTO) initiation function definition
DT.setRTO = function(callback, duration, start = DT.now()) {
DT.RTOstart = start;
DT.RTOduration = duration;
DT.RTOcallback = callback;
DT.RTOloop(start);
};
// add the RAF setRTO function definition
DT.RTOloop = function(timestamp) {
if (timestamp - DT.RTOstart < DT.RTOduration) {
requestAnimationFrame((RAFtimestamp) => DT.RTOloop(RAFtimestamp));
} else {
DT.RTOcallback(timestamp, DT.RTOstart);
DT.RTOcallback = undefined;
DT.RTOstart = undefined;
DT.RTOduration = undefined;
}
};
Object.seal(DT);
// example use
DT.setRTO((stamp, start) => console.log(stamp - start), 500);(编辑:这个函数是从一个初始的RAF回调中调用的,因此这个时间应该与重新绘制同步。)
发布于 2022-10-15 14:17:23
我必须指出,在这段代码中,我看不出有什么用处。它的作用就像一个带有随机16.66...ms错误的计时器,根本不提供帧同步。
requestAnimationFrame不与显示帧同步,而是告诉排序者保存在rAF回调(存储在buf中)中所做的可视更改,直到安全地移动到显示缓冲区(在VSync期间)。
现在大多数浏览器也使用setTimeout和setInterval来实现这一功能。
回到评论上。
DT,而不是使用平面代码(参见重写)setRTO和RTOLoop。Object.seal,不过我更喜欢使用Object.freeze。这迫使公开的对象更像接口,只允许getter和setter修改状态并确保对象完整性。requestAnimationFrame((RAFtimestamp) => DT.RTOloop(RAFtimestamp));可以是requestAnimationFrame(DT.RTOloop);callback不作为函数进行审查,因此可以提交您的代码。start和duration不被检查为数字,并且在有效范围内。始终确保您的对象具有有效的状态。在运行现有循环时,没有什么可以阻止对setRTO的新调用启动新的rAF循环。随着越来越多的循环等待终止,这可能会导致严重的资源耗尽。
您需要确保现有循环在预期时终止,或者防止在当前循环结束之前开始新的循环。重写(下面)在DT的闭包中使用信号量running来防止一次运行多个循环。
一开始我以为您的目标是支持遗留浏览器,但是您有现代语法( DT.RTOLoop中的箭头函数和DT.setRTO中的默认参数),它否定了所有旧的样式代码,我怀疑是否需要定义performance.now,如果浏览器支持箭头函数,那么performance.now就是给定的。
现代的JS点。
{ now(){} }而不是{ now: function(){} }??而不是||。Eg performance = window.performance ?? {}DT的单个实例。setRTO。const DT = (() => {
const isNum = val => !isNaN(val) && val < Infinity; // Special case Infinity is not a number
var start, duration, callback, running;
function onFrame(time) {
time - start < duration ?
requestAnimationFrame(onFrame) :
(running = false, callback(time, start));
}
return Object.freeze({
setRTO(cb, duration_, start_ = performance.now()) {
if (cb instanceof Function && isNum(duration_) && isNum(start_) && !running) {
callback = cb;
start = start_;
duration = duration_;
running = true;
onFrame(start);
} else {
// todo Fail code here
}
}
});
})();
DT.setRTO((stamp, start) => console.log(stamp - start), 500);
DT.setRTO((stamp, start) => console.log(stamp - start), 100); // this timer will not starthttps://codereview.stackexchange.com/questions/280431
复制相似问题