mirror of
https://github.com/videojs/video.js.git
synced 2025-07-07 01:07:13 +02:00
feat: Add named requestAnimationFrame to prevent performance issues (#6627)
Make sure we don't create multiple rAFs particularly when in a background tab. Fixes #5937
This commit is contained in:
committed by
Gary Katsevman
parent
4d9e1bcccc
commit
6e7cc75aaa
@ -952,7 +952,7 @@ QUnit.test('should provide interval methods that automatically get cleared on co
|
||||
assert.ok(intervalsFired === 5, 'Interval was cleared when component was disposed');
|
||||
});
|
||||
|
||||
QUnit.test('should provide *AnimationFrame methods that automatically get cleared on component disposal', function(assert) {
|
||||
QUnit.test('should provide a requestAnimationFrame method that is cleared on disposal', function(assert) {
|
||||
const comp = new Component(getFakePlayer());
|
||||
const oldRAF = window.requestAnimationFrame;
|
||||
const oldCAF = window.cancelAnimationFrame;
|
||||
@ -967,7 +967,7 @@ QUnit.test('should provide *AnimationFrame methods that automatically get cleare
|
||||
|
||||
const spyRAF = sinon.spy();
|
||||
|
||||
comp.requestAnimationFrame(spyRAF);
|
||||
comp.requestNamedAnimationFrame('testing', spyRAF);
|
||||
|
||||
assert.strictEqual(spyRAF.callCount, 0, 'rAF callback was not called immediately');
|
||||
this.clock.tick(1);
|
||||
@ -975,11 +975,11 @@ QUnit.test('should provide *AnimationFrame methods that automatically get cleare
|
||||
this.clock.tick(1);
|
||||
assert.strictEqual(spyRAF.callCount, 1, 'rAF callback was not called after a second "repaint"');
|
||||
|
||||
comp.cancelAnimationFrame(comp.requestAnimationFrame(spyRAF));
|
||||
comp.cancelNamedAnimationFrame(comp.requestNamedAnimationFrame('testing', spyRAF));
|
||||
this.clock.tick(1);
|
||||
assert.strictEqual(spyRAF.callCount, 1, 'second rAF callback was not called because it was cancelled');
|
||||
|
||||
comp.requestAnimationFrame(spyRAF);
|
||||
comp.requestNamedAnimationFrame('testing', spyRAF);
|
||||
comp.dispose();
|
||||
this.clock.tick(1);
|
||||
assert.strictEqual(spyRAF.callCount, 1, 'third rAF callback was not called because the component was disposed');
|
||||
@ -988,7 +988,43 @@ QUnit.test('should provide *AnimationFrame methods that automatically get cleare
|
||||
window.cancelAnimationFrame = oldCAF;
|
||||
});
|
||||
|
||||
QUnit.test('*AnimationFrame methods fall back to timers if rAF not supported', function(assert) {
|
||||
QUnit.test('should provide a requestNamedAnimationFrame method that is cleared on disposal', function(assert) {
|
||||
const comp = new Component(getFakePlayer());
|
||||
const oldRAF = window.requestAnimationFrame;
|
||||
const oldCAF = window.cancelAnimationFrame;
|
||||
|
||||
// Stub the window.*AnimationFrame methods with window.setTimeout methods
|
||||
// so we can control when the callbacks are called via sinon's timer stubs.
|
||||
window.requestAnimationFrame = (fn) => window.setTimeout(fn, 1);
|
||||
window.cancelAnimationFrame = (id) => window.clearTimeout(id);
|
||||
|
||||
// Make sure the component thinks it supports rAF.
|
||||
comp.supportsRaf_ = true;
|
||||
|
||||
const spyRAF = sinon.spy();
|
||||
|
||||
comp.requestNamedAnimationFrame('testing', spyRAF);
|
||||
|
||||
assert.strictEqual(spyRAF.callCount, 0, 'rAF callback was not called immediately');
|
||||
this.clock.tick(1);
|
||||
assert.strictEqual(spyRAF.callCount, 1, 'rAF callback was called after a "repaint"');
|
||||
this.clock.tick(1);
|
||||
assert.strictEqual(spyRAF.callCount, 1, 'rAF callback was not called after a second "repaint"');
|
||||
|
||||
comp.cancelNamedAnimationFrame(comp.requestNamedAnimationFrame('testing', spyRAF));
|
||||
this.clock.tick(1);
|
||||
assert.strictEqual(spyRAF.callCount, 1, 'second rAF callback was not called because it was cancelled');
|
||||
|
||||
comp.requestNamedAnimationFrame('testing', spyRAF);
|
||||
comp.dispose();
|
||||
this.clock.tick(1);
|
||||
assert.strictEqual(spyRAF.callCount, 1, 'third rAF callback was not called because the component was disposed');
|
||||
|
||||
window.requestAnimationFrame = oldRAF;
|
||||
window.cancelAnimationFrame = oldCAF;
|
||||
});
|
||||
|
||||
QUnit.test('requestAnimationFrame falls back to timers if rAF not supported', function(assert) {
|
||||
const comp = new Component(getFakePlayer());
|
||||
const oldRAF = window.requestAnimationFrame;
|
||||
const oldCAF = window.cancelAnimationFrame;
|
||||
@ -1030,7 +1066,7 @@ QUnit.test('setTimeout should remove dispose handler on trigger', function(asser
|
||||
comp.dispose();
|
||||
});
|
||||
|
||||
QUnit.test('requestAnimationFrame should remove dispose handler on trigger', function(assert) {
|
||||
QUnit.test('requestNamedAnimationFrame should remove dispose handler on trigger', function(assert) {
|
||||
const comp = new Component(getFakePlayer());
|
||||
const oldRAF = window.requestAnimationFrame;
|
||||
const oldCAF = window.cancelAnimationFrame;
|
||||
@ -1045,13 +1081,15 @@ QUnit.test('requestAnimationFrame should remove dispose handler on trigger', fun
|
||||
|
||||
const spyRAF = sinon.spy();
|
||||
|
||||
comp.requestAnimationFrame(spyRAF);
|
||||
comp.requestNamedAnimationFrame('testFrame', spyRAF);
|
||||
|
||||
assert.equal(comp.rafIds_.size, 1, 'we got a new dispose handler');
|
||||
assert.equal(comp.rafIds_.size, 1, 'we got a new raf dispose handler');
|
||||
assert.equal(comp.namedRafs_.size, 1, 'we got a new named raf dispose handler');
|
||||
|
||||
this.clock.tick(1);
|
||||
|
||||
assert.equal(comp.rafIds_.size, 0, 'we removed our dispose handle');
|
||||
assert.equal(comp.rafIds_.size, 0, 'we removed our raf dispose handle');
|
||||
assert.equal(comp.namedRafs_.size, 0, 'we removed our named raf dispose handle');
|
||||
|
||||
comp.dispose();
|
||||
|
||||
@ -1169,6 +1207,122 @@ QUnit.test('setInterval should be canceled on dispose', function(assert) {
|
||||
assert.equal(called, false, 'setInterval was never called');
|
||||
});
|
||||
|
||||
QUnit.test('requestNamedAnimationFrame should be canceled on dispose', function(assert) {
|
||||
const comp = new Component(getFakePlayer());
|
||||
let called = false;
|
||||
let clearName;
|
||||
const setName = comp.requestNamedAnimationFrame('testing', () => {
|
||||
called = true;
|
||||
});
|
||||
|
||||
const cancelNamedAnimationFrame = comp.cancelNamedAnimationFrame;
|
||||
|
||||
comp.cancelNamedAnimationFrame = (name) => {
|
||||
clearName = name;
|
||||
return cancelNamedAnimationFrame.call(comp, name);
|
||||
};
|
||||
|
||||
assert.equal(comp.namedRafs_.size, 1, 'we added a named raf');
|
||||
assert.equal(comp.rafIds_.size, 1, 'we added a raf id');
|
||||
|
||||
comp.dispose();
|
||||
|
||||
assert.equal(comp.namedRafs_.size, 0, 'we removed a named raf');
|
||||
assert.equal(comp.rafIds_.size, 0, 'we removed a raf id');
|
||||
assert.equal(clearName, setName, 'cancelNamedAnimationFrame was called');
|
||||
|
||||
this.clock.tick(1);
|
||||
|
||||
assert.equal(called, false, 'requestNamedAnimationFrame was never called');
|
||||
});
|
||||
|
||||
QUnit.test('requestNamedAnimationFrame should only allow one raf of a specific name at a time', function(assert) {
|
||||
const comp = new Component(getFakePlayer());
|
||||
const calls = {
|
||||
one: 0,
|
||||
two: 0,
|
||||
three: 0
|
||||
};
|
||||
const cancelNames = [];
|
||||
const name = 'testing';
|
||||
const handlerOne = () => {
|
||||
assert.equal(comp.namedRafs_.size, 1, 'named raf still exists while function runs');
|
||||
assert.equal(comp.rafIds_.size, 0, 'raf id does not exist during run');
|
||||
|
||||
calls.one++;
|
||||
};
|
||||
const handlerTwo = () => {
|
||||
assert.equal(comp.namedRafs_.size, 1, 'named raf still exists while function runs');
|
||||
assert.equal(comp.rafIds_.size, 0, 'raf id does not exist during run');
|
||||
calls.two++;
|
||||
};
|
||||
const handlerThree = () => {
|
||||
assert.equal(comp.namedRafs_.size, 1, 'named raf still exists while function runs');
|
||||
assert.equal(comp.rafIds_.size, 0, 'raf id does not exist during run');
|
||||
calls.three++;
|
||||
};
|
||||
|
||||
const oldRAF = window.requestAnimationFrame;
|
||||
const oldCAF = window.cancelAnimationFrame;
|
||||
|
||||
// Make sure the component thinks it supports rAF.
|
||||
comp.supportsRaf_ = true;
|
||||
|
||||
// Stub the window.*AnimationFrame methods with window.setTimeout methods
|
||||
// so we can control when the callbacks are called via sinon's timer stubs.
|
||||
window.requestAnimationFrame = (fn) => window.setTimeout(fn, 1);
|
||||
window.cancelAnimationFrame = (id) => window.clearTimeout(id);
|
||||
|
||||
const cancelNamedAnimationFrame = comp.cancelNamedAnimationFrame;
|
||||
|
||||
comp.cancelNamedAnimationFrame = (_name) => {
|
||||
cancelNames.push(_name);
|
||||
return cancelNamedAnimationFrame.call(comp, _name);
|
||||
};
|
||||
|
||||
comp.requestNamedAnimationFrame(name, handlerOne);
|
||||
|
||||
assert.equal(comp.namedRafs_.size, 1, 'we added a named raf');
|
||||
assert.equal(comp.rafIds_.size, 1, 'we added a raf id');
|
||||
|
||||
comp.requestNamedAnimationFrame(name, handlerTwo);
|
||||
|
||||
assert.deepEqual(cancelNames, [], 'no named cancels');
|
||||
assert.equal(comp.namedRafs_.size, 1, 'still only one named raf');
|
||||
assert.equal(comp.rafIds_.size, 1, 'still only one raf id');
|
||||
|
||||
this.clock.tick(1);
|
||||
|
||||
assert.equal(comp.namedRafs_.size, 0, 'we removed a named raf');
|
||||
assert.equal(comp.rafIds_.size, 0, 'we removed a raf id');
|
||||
assert.deepEqual(calls, {
|
||||
one: 1,
|
||||
two: 0,
|
||||
three: 0
|
||||
}, 'only handlerOne was called');
|
||||
|
||||
comp.requestNamedAnimationFrame(name, handlerOne);
|
||||
comp.requestNamedAnimationFrame(name, handlerTwo);
|
||||
comp.requestNamedAnimationFrame(name, handlerThree);
|
||||
|
||||
assert.deepEqual(cancelNames, [], 'no named cancels for testing');
|
||||
assert.equal(comp.namedRafs_.size, 1, 'only added one named raf');
|
||||
assert.equal(comp.rafIds_.size, 1, 'only added one named raf');
|
||||
|
||||
this.clock.tick(1);
|
||||
|
||||
assert.equal(comp.namedRafs_.size, 0, 'we removed a named raf');
|
||||
assert.equal(comp.rafIds_.size, 0, 'we removed a raf id');
|
||||
assert.deepEqual(calls, {
|
||||
one: 2,
|
||||
two: 0,
|
||||
three: 0
|
||||
}, 'only the handlerOne called');
|
||||
|
||||
window.requestAnimationFrame = oldRAF;
|
||||
window.cancelAnimationFrame = oldCAF;
|
||||
});
|
||||
|
||||
QUnit.test('$ and $$ functions', function(assert) {
|
||||
const comp = new Component(getFakePlayer());
|
||||
const contentEl = document.createElement('div');
|
||||
|
Reference in New Issue
Block a user