Skip to content

Commit

Permalink
feat(animation.rendering): waitForFrames, throttle
Browse files Browse the repository at this point in the history
  • Loading branch information
Benjamin Strauß committed Apr 23, 2018
1 parent 489046b commit ea7e144
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 2 deletions.
1 change: 1 addition & 0 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ function compile () {
'extraRequire'
],
hide_warnings_for: 'node_modules/google-closure-library/closure/goog',
jscomp_off: [],
output_wrapper: '(function(){%output%}).call(this);',
js_output_file: 'test.min.js'
};
Expand Down
44 changes: 43 additions & 1 deletion src/animation/rendering.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,46 @@ const RenderLoopEventType = {
END: 'end'
};

exports = {RenderLoop, RenderLoopEvent, RenderLoopEventType};
/**
* Returns a Promise that resolves after a specified number of browser repaints.
*
* @param {number=} frames The frames to wait for
* @returns {Promise}
*/
function waitForFrames (frames = 1) {
return new Promise(resolve => {
let frameCount = 0;

function onTick () {
if (++frameCount === frames)
resolve();
else
window.requestAnimationFrame(onTick);
}

window.requestAnimationFrame(onTick);
});
}

/**
* Throttles a function to be only called on repaint.
*
* Arguments are passed down.
*
* @param {Function} func
* @returns {Function}
*/
function throttle (func) {
let isRunning = false;
return function (...args) {
if (isRunning)
return;
isRunning = true;
window.requestAnimationFrame(() => {
func(...args);
isRunning = false;
});
};
}

exports = {RenderLoop, RenderLoopEvent, RenderLoopEventType, waitForFrames, throttle};
68 changes: 67 additions & 1 deletion test/animation/rendering_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ goog.module('test.clulib.animation.rendering');

const {listen} = goog.require('goog.events');

const {RenderLoop, RenderLoopEventType} = goog.require('clulib.animation.rendering');
const {RenderLoop, RenderLoopEventType, waitForFrames, throttle} = goog.require('clulib.animation.rendering');

const {waitFor} = goog.require('testing.async');
const {tick} = goog.require('testing.animation');

exports = function () {
describe('clulib.animation.rendering', () => {
Expand Down Expand Up @@ -40,5 +41,70 @@ exports = function () {
expect(elapsedTime > 0).toBe(true);
});
});

describe('waitForFrames', () => {
it('should wait for a certain number of browser repaints', async () => {
let ticks = 0;
let callId = null;

function tick () {
ticks++;
callId = window.requestAnimationFrame(tick);
}

callId = window.requestAnimationFrame(tick);

await waitForFrames(3);
window.cancelAnimationFrame(/** @type {number} */ (callId));

expect(ticks).toBe(3);
});
});

describe('throttle', () => {
it('should throttle a function to be only called on repaint', async () => {
let calls = 0;
let throttledFunction = throttle(() => {
calls++;
});

expect(calls).toBe(0);

throttledFunction();
throttledFunction();
throttledFunction();

expect(calls).toBe(0);

await tick();

expect(calls).toBe(1);

throttledFunction();
throttledFunction();
throttledFunction();

expect(calls).toBe(1);

await tick();

expect(calls).toBe(2);
});

it('should pass arguments to the inner function', async () => {
let argument = null;
let throttledFunction = throttle(x => {
argument = x;
});

throttledFunction(5);

expect(argument).toBe(null);

await tick();

expect(argument).toBe(5);
});
});
});
};
11 changes: 11 additions & 0 deletions testing/animation/animation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
goog.module('testing.animation');

function tick () {
return new Promise(resolve => {
window.requestAnimationFrame(() => {
resolve();
});
});
}

exports = {tick};

0 comments on commit ea7e144

Please sign in to comment.