首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用setTimout循环创建自定义requestAnimationFrame函数

使用setTimout循环创建自定义requestAnimationFrame函数
EN

Code Review用户
提问于 2022-10-14 06:47:43
回答 1查看 59关注 0票数 0

我使用requestAnimationFrame循环创建了一个函数,它的工作原理与普通的JS setTimeout函数非常相似(除了可以选择在持续时间内提供自定义的启动时间,并且返回完成时间)。我想知道这是否是一种理想的写作方式。特别是,在定义主对象时,我遇到了一些自我引用的问题,所以我不得不分别定义它们。(也许创建类更有意义,但我希望尽可能地保持它的简单性。)

代码语言:javascript
复制
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回调中调用的,因此这个时间应该与重新绘制同步。)

EN

回答 1

Code Review用户

回答已采纳

发布于 2022-10-15 14:17:23

我必须指出,在这段代码中,我看不出有什么用处。它的作用就像一个带有随机16.66...ms错误的计时器,根本不提供帧同步。

requestAnimationFrame不与显示帧同步,而是告诉排序者保存在rAF回调(存储在buf中)中所做的可视更改,直到安全地移动到显示缓冲区(在VSync期间)。

现在大多数浏览器也使用setTimeoutsetInterval来实现这一功能。

回到评论上。

通用点

  • 最好是通过工厂来实现DT,而不是使用平面代码(参见重写)
  • 不知道为什么在对象的(DT)声明之外定义setRTORTOLoop
  • 很高兴看到Object.seal,不过我更喜欢使用Object.freeze。这迫使公开的对象更像接口,只允许getter和setter修改状态并确保对象完整性。
  • 避免间接调用,因为它们只是浪费源代码字符和CPU周期。requestAnimationFrame((RAFtimestamp) => DT.RTOloop(RAFtimestamp));可以是requestAnimationFrame(DT.RTOloop);
  • 你不是在保护你的对象的状态。callback不作为函数进行审查,因此可以提交您的代码。startduration不被检查为数字,并且在有效范围内。始终确保您的对象具有有效的状态。

未定义的不需要的行为

在运行现有循环时,没有什么可以阻止对setRTO的新调用启动新的rAF循环。随着越来越多的循环等待终止,这可能会导致严重的资源耗尽。

您需要确保现有循环在预期时终止,或者防止在当前循环结束之前开始新的循环。重写(下面)在DT的闭包中使用信号量running来防止一次运行多个循环。

使用现代JS.

一开始我以为您的目标是支持遗留浏览器,但是您有现代语法( DT.RTOLoop中的箭头函数和DT.setRTO中的默认参数),它否定了所有旧的样式代码,我怀疑是否需要定义performance.now,如果浏览器支持箭头函数,那么performance.now就是给定的。

现代的JS点。

  • 更多的箭头功能。
  • 对象函数简写声明。{ now(){} }而不是{ now: function(){} }
  • ??而不是||。Eg performance = window.performance ?? {}

重写

  • 重写使用工厂模式通过IIFE创建DT的单个实例。
  • DT被简化为一个属性setRTO
  • 该行为是针对无效参数进行检查的,如果没有提供好的数据,则不会做任何事情。
  • 在现有计时器(如果有的话)完成之前,不会启动新计时器。
代码语言:javascript
复制
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 start
票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/280431

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档