From 0cf509f674ea3899293450b45108728c52a25d2e Mon Sep 17 00:00:00 2001 From: Pat Cavit Date: Wed, 18 May 2016 23:08:35 -0700 Subject: [PATCH] Add forcing support and improve tests Also verify that we're using tabs, whee. --- api/limiter.js | 15 ++++---- api/tests/async.js | 42 +++++++++++++++++++--- api/tests/test-limiter.js | 76 +++++++++++++++++++++++++++++---------- 3 files changed, 104 insertions(+), 29 deletions(-) diff --git a/api/limiter.js b/api/limiter.js index ec35a80e..477c715e 100644 --- a/api/limiter.js +++ b/api/limiter.js @@ -5,22 +5,23 @@ module.exports = function($window, render) { var cAF = $window.cancelAnimationFrame || $window.clearTimeout var last = 0 - var pending + var pending = null - return function() { + return function(force) { var now = new Date() - // First render, OR if the time since the last render is greater - // than the frame budget - // just immediately render - if(!last || now - last > FRAME_BUDGET) { + // Immediately render if: + // Forced + // Haven't rendered yet + // Time since the last render is greater than the frame budget + if(force || !last || now - last > FRAME_BUDGET) { last = now; return render() } // Redraw already pending, abort - if(pending) { + if(pending !== null) { return } diff --git a/api/tests/async.js b/api/tests/async.js index e599b0bc..f3206fd7 100644 --- a/api/tests/async.js +++ b/api/tests/async.js @@ -1,12 +1,46 @@ +var _fns = [] +var _last = 0 +var _frame = 1000 / 60 + module.exports = { setTimeout : function($window) { - $window.setTimeout = window.setTimeout; - $window.clearTimeout = window.clearTimeout; + $window.setTimeout = typeof window === "object" ? window.setTimeout : global.setTimeout; + $window.clearTimeout = typeof window === "object" ? window.clearTimeout : global.setTimeout; }, requestAnimationFrame : function($window) { - $window.requestAnimationFrame = window.requestAnimationFrame; - $window.cancelAnimationFrame = window.cancelAnimationFrame; + // Modified version of https://github.com/chrisdickinson/raf + // Copyright chrisdickinson I guess? + $window.requestAnimationFrame = typeof window === "object" ? window.requestAnimationFrame : function(fn) { + if(!_fns.length) { + var now = Date.now() + var next = Math.max(0, _frame - (now - _last)) + + _last = next + now + + setTimeout(function() { + var fns = _fns.slice() + + _fns = [] + + for(var i = 0; i < fns.length; i++) { + if(typeof fns[i] !== "function") { + continue + } + + fns[i](_last) + } + }, Math.round(next)) + } + + _fns.push(fn) + + return _fns.length - 1; + } + + $window.cancelAnimationFrame = typeof window === "object" ? window.cancelAnimationFrame : function(handle) { + _fns[handle] = null + }; } } diff --git a/api/tests/test-limiter.js b/api/tests/test-limiter.js index e5cfb679..ece28c69 100644 --- a/api/tests/test-limiter.js +++ b/api/tests/test-limiter.js @@ -7,23 +7,63 @@ var async = require("./async") var limiter = require("../limiter") o.spec("fps limiter", function() { - var $window, root + var $window, root - [ "setTimeout", "requestAnimationFrame" ].forEach(function(type) { - o.spec(type, function() { - o.beforeEach(function() { - $window = domMock() - - async[type]($window) - }) - - o("is a function", function() { - o(typeof limiter).equals("function") - }) - - o("it returns a function", function() { - o(typeof limiter(false)).equals("function") - }) - }) - }) + [ + "setTimeout", + "requestAnimationFrame", + ].forEach(function(type) { + o.spec(type, function() { + o.beforeEach(function() { + $window = domMock() + + async[type]($window) + }) + + o("is a function", function() { + o(typeof limiter).equals("function") + }) + + o("it returns a function", function() { + o(typeof limiter($window, false)).equals("function") + }) + + o("it runs synchronously the first time", function() { + var spy = o.spy() + var run = limiter($window, spy) + + run() + + o(spy.callCount).equals(1) + }) + + o("it only runs once per tick", function(done) { + var spy = o.spy() + var run = limiter($window, spy) + + run() + run() + run() + + o(spy.callCount).equals(1) + + setTimeout(function() { + o(spy.callCount).equals(2) + + done() + }, 17) + }) + + o("it supports forcing a synchronous redraw", function() { + var spy = o.spy() + var run = limiter($window, spy) + + run() + run() + run(true) + + o(spy.callCount).equals(2) + }) + }) + }) })