mirror of
https://github.com/videojs/video.js.git
synced 2024-12-04 10:34:51 +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:
parent
4d9e1bcccc
commit
6e7cc75aaa
@ -13,6 +13,8 @@ import * as Guid from './utils/guid.js';
|
|||||||
import {toTitleCase, toLowerCase} from './utils/string-cases.js';
|
import {toTitleCase, toLowerCase} from './utils/string-cases.js';
|
||||||
import mergeOptions from './utils/merge-options.js';
|
import mergeOptions from './utils/merge-options.js';
|
||||||
import computedStyle from './utils/computed-style';
|
import computedStyle from './utils/computed-style';
|
||||||
|
import Map from './utils/map.js';
|
||||||
|
import Set from './utils/set.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for all UI Components.
|
* Base class for all UI Components.
|
||||||
@ -100,38 +102,10 @@ class Component {
|
|||||||
this.childIndex_ = {};
|
this.childIndex_ = {};
|
||||||
this.childNameIndex_ = {};
|
this.childNameIndex_ = {};
|
||||||
|
|
||||||
let SetSham;
|
this.setTimeoutIds_ = new Set();
|
||||||
|
this.setIntervalIds_ = new Set();
|
||||||
if (!window.Set) {
|
this.rafIds_ = new Set();
|
||||||
SetSham = class {
|
this.namedRafs_ = new Map();
|
||||||
constructor() {
|
|
||||||
this.set_ = {};
|
|
||||||
}
|
|
||||||
has(key) {
|
|
||||||
return key in this.set_;
|
|
||||||
}
|
|
||||||
delete(key) {
|
|
||||||
const has = this.has(key);
|
|
||||||
|
|
||||||
delete this.set_[key];
|
|
||||||
|
|
||||||
return has;
|
|
||||||
}
|
|
||||||
add(key) {
|
|
||||||
this.set_[key] = 1;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
forEach(callback, thisArg) {
|
|
||||||
for (const key in this.set_) {
|
|
||||||
callback.call(thisArg, key, key, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setTimeoutIds_ = window.Set ? new Set() : new SetSham();
|
|
||||||
this.setIntervalIds_ = window.Set ? new Set() : new SetSham();
|
|
||||||
this.rafIds_ = window.Set ? new Set() : new SetSham();
|
|
||||||
this.clearingTimersOnDispose_ = false;
|
this.clearingTimersOnDispose_ = false;
|
||||||
|
|
||||||
// Add any child components in options
|
// Add any child components in options
|
||||||
@ -1529,6 +1503,53 @@ class Component {
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request an animation frame, but only one named animation
|
||||||
|
* frame will be queued. Another will never be added until
|
||||||
|
* the previous one finishes.
|
||||||
|
*
|
||||||
|
* @param {string} name
|
||||||
|
* The name to give this requestAnimationFrame
|
||||||
|
*
|
||||||
|
* @param {Component~GenericCallback} fn
|
||||||
|
* A function that will be bound to this component and executed just
|
||||||
|
* before the browser's next repaint.
|
||||||
|
*/
|
||||||
|
requestNamedAnimationFrame(name, fn) {
|
||||||
|
if (this.namedRafs_.has(name)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.clearTimersOnDispose_();
|
||||||
|
|
||||||
|
fn = Fn.bind(this, fn);
|
||||||
|
|
||||||
|
const id = this.requestAnimationFrame(() => {
|
||||||
|
fn();
|
||||||
|
if (this.namedRafs_.has(name)) {
|
||||||
|
this.namedRafs_.delete(name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.namedRafs_.set(name, id);
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels a current named animation frame if it exists.
|
||||||
|
*
|
||||||
|
* @param {string} name
|
||||||
|
* The name of the requestAnimationFrame to cancel.
|
||||||
|
*/
|
||||||
|
cancelNamedAnimationFrame(name) {
|
||||||
|
if (!this.namedRafs_.has(name)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cancelAnimationFrame(this.namedRafs_.get(name));
|
||||||
|
this.namedRafs_.delete(name);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancels a queued callback passed to {@link Component#requestAnimationFrame}
|
* Cancels a queued callback passed to {@link Component#requestAnimationFrame}
|
||||||
* (rAF).
|
* (rAF).
|
||||||
@ -1578,11 +1599,15 @@ class Component {
|
|||||||
this.clearingTimersOnDispose_ = true;
|
this.clearingTimersOnDispose_ = true;
|
||||||
this.one('dispose', () => {
|
this.one('dispose', () => {
|
||||||
[
|
[
|
||||||
|
['namedRafs_', 'cancelNamedAnimationFrame'],
|
||||||
['rafIds_', 'cancelAnimationFrame'],
|
['rafIds_', 'cancelAnimationFrame'],
|
||||||
['setTimeoutIds_', 'clearTimeout'],
|
['setTimeoutIds_', 'clearTimeout'],
|
||||||
['setIntervalIds_', 'clearInterval']
|
['setIntervalIds_', 'clearInterval']
|
||||||
].forEach(([idName, cancelName]) => {
|
].forEach(([idName, cancelName]) => {
|
||||||
this[idName].forEach(this[cancelName], this);
|
// for a `Set` key will actually be the value again
|
||||||
|
// so forEach((val, val) =>` but for maps we want to use
|
||||||
|
// the key.
|
||||||
|
this[idName].forEach((val, key) => this[cancelName](key));
|
||||||
});
|
});
|
||||||
|
|
||||||
this.clearingTimersOnDispose_ = false;
|
this.clearingTimersOnDispose_ = false;
|
||||||
|
@ -72,7 +72,7 @@ class LoadProgressBar extends Component {
|
|||||||
* @listens Player#progress
|
* @listens Player#progress
|
||||||
*/
|
*/
|
||||||
update(event) {
|
update(event) {
|
||||||
this.requestAnimationFrame(() => {
|
this.requestNamedAnimationFrame('LoadProgressBar#update', () => {
|
||||||
const liveTracker = this.player_.liveTracker;
|
const liveTracker = this.player_.liveTracker;
|
||||||
const buffered = this.player_.buffered();
|
const buffered = this.player_.buffered();
|
||||||
const duration = (liveTracker && liveTracker.isLive()) ? liveTracker.seekableEnd() : this.player_.duration();
|
const duration = (liveTracker && liveTracker.isLive()) ? liveTracker.seekableEnd() : this.player_.duration();
|
||||||
|
@ -133,7 +133,7 @@ class SeekBar extends Slider {
|
|||||||
update(event) {
|
update(event) {
|
||||||
const percent = super.update();
|
const percent = super.update();
|
||||||
|
|
||||||
this.requestAnimationFrame(() => {
|
this.requestNamedAnimationFrame('SeekBar#update', () => {
|
||||||
const currentTime = this.player_.ended() ?
|
const currentTime = this.player_.ended() ?
|
||||||
this.player_.duration() : this.getCurrentTime_();
|
this.player_.duration() : this.getCurrentTime_();
|
||||||
const liveTracker = this.player_.liveTracker;
|
const liveTracker = this.player_.liveTracker;
|
||||||
|
@ -128,12 +128,7 @@ class TimeTooltip extends Component {
|
|||||||
* for tooltips that need to do additional animations from the default
|
* for tooltips that need to do additional animations from the default
|
||||||
*/
|
*/
|
||||||
updateTime(seekBarRect, seekBarPoint, time, cb) {
|
updateTime(seekBarRect, seekBarPoint, time, cb) {
|
||||||
// If there is an existing rAF ID, cancel it so we don't over-queue.
|
this.requestNamedAnimationFrame('TimeTooltip#updateTime', () => {
|
||||||
if (this.rafId_) {
|
|
||||||
this.cancelAnimationFrame(this.rafId_);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.rafId_ = this.requestAnimationFrame(() => {
|
|
||||||
let content;
|
let content;
|
||||||
const duration = this.player_.duration();
|
const duration = this.player_.duration();
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ class TimeDisplay extends Component {
|
|||||||
|
|
||||||
this.formattedTime_ = time;
|
this.formattedTime_ = time;
|
||||||
|
|
||||||
this.requestAnimationFrame(() => {
|
this.requestNamedAnimationFrame('TimeDisplay#updateTextNode_', () => {
|
||||||
if (!this.contentEl_) {
|
if (!this.contentEl_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -249,7 +249,7 @@ class Slider extends Component {
|
|||||||
|
|
||||||
this.progress_ = progress;
|
this.progress_ = progress;
|
||||||
|
|
||||||
this.requestAnimationFrame(() => {
|
this.requestNamedAnimationFrame('Slider#update', () => {
|
||||||
// Set the new bar width or height
|
// Set the new bar width or height
|
||||||
const sizeKey = this.vertical() ? 'height' : 'width';
|
const sizeKey = this.vertical() ? 'height' : 'width';
|
||||||
|
|
||||||
|
28
src/js/utils/map.js
Normal file
28
src/js/utils/map.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import window from 'global/window';
|
||||||
|
|
||||||
|
class MapSham {
|
||||||
|
constructor() {
|
||||||
|
this.map_ = {};
|
||||||
|
}
|
||||||
|
has(key) {
|
||||||
|
return key in this.map_;
|
||||||
|
}
|
||||||
|
delete(key) {
|
||||||
|
const has = this.has(key);
|
||||||
|
|
||||||
|
delete this.map_[key];
|
||||||
|
|
||||||
|
return has;
|
||||||
|
}
|
||||||
|
set(key, value) {
|
||||||
|
this.set_[key] = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
forEach(callback, thisArg) {
|
||||||
|
for (const key in this.map_) {
|
||||||
|
callback.call(thisArg, this.map_[key], key, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default window.Map ? window.Map : MapSham;
|
28
src/js/utils/set.js
Normal file
28
src/js/utils/set.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import window from 'global/window';
|
||||||
|
|
||||||
|
class SetSham {
|
||||||
|
constructor() {
|
||||||
|
this.set_ = {};
|
||||||
|
}
|
||||||
|
has(key) {
|
||||||
|
return key in this.set_;
|
||||||
|
}
|
||||||
|
delete(key) {
|
||||||
|
const has = this.has(key);
|
||||||
|
|
||||||
|
delete this.set_[key];
|
||||||
|
|
||||||
|
return has;
|
||||||
|
}
|
||||||
|
add(key) {
|
||||||
|
this.set_[key] = 1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
forEach(callback, thisArg) {
|
||||||
|
for (const key in this.set_) {
|
||||||
|
callback.call(thisArg, key, key, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default window.Set ? window.Set : SetSham;
|
@ -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');
|
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 comp = new Component(getFakePlayer());
|
||||||
const oldRAF = window.requestAnimationFrame;
|
const oldRAF = window.requestAnimationFrame;
|
||||||
const oldCAF = window.cancelAnimationFrame;
|
const oldCAF = window.cancelAnimationFrame;
|
||||||
@ -967,7 +967,7 @@ QUnit.test('should provide *AnimationFrame methods that automatically get cleare
|
|||||||
|
|
||||||
const spyRAF = sinon.spy();
|
const spyRAF = sinon.spy();
|
||||||
|
|
||||||
comp.requestAnimationFrame(spyRAF);
|
comp.requestNamedAnimationFrame('testing', spyRAF);
|
||||||
|
|
||||||
assert.strictEqual(spyRAF.callCount, 0, 'rAF callback was not called immediately');
|
assert.strictEqual(spyRAF.callCount, 0, 'rAF callback was not called immediately');
|
||||||
this.clock.tick(1);
|
this.clock.tick(1);
|
||||||
@ -975,11 +975,11 @@ QUnit.test('should provide *AnimationFrame methods that automatically get cleare
|
|||||||
this.clock.tick(1);
|
this.clock.tick(1);
|
||||||
assert.strictEqual(spyRAF.callCount, 1, 'rAF callback was not called after a second "repaint"');
|
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);
|
this.clock.tick(1);
|
||||||
assert.strictEqual(spyRAF.callCount, 1, 'second rAF callback was not called because it was cancelled');
|
assert.strictEqual(spyRAF.callCount, 1, 'second rAF callback was not called because it was cancelled');
|
||||||
|
|
||||||
comp.requestAnimationFrame(spyRAF);
|
comp.requestNamedAnimationFrame('testing', spyRAF);
|
||||||
comp.dispose();
|
comp.dispose();
|
||||||
this.clock.tick(1);
|
this.clock.tick(1);
|
||||||
assert.strictEqual(spyRAF.callCount, 1, 'third rAF callback was not called because the component was disposed');
|
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;
|
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 comp = new Component(getFakePlayer());
|
||||||
const oldRAF = window.requestAnimationFrame;
|
const oldRAF = window.requestAnimationFrame;
|
||||||
const oldCAF = window.cancelAnimationFrame;
|
const oldCAF = window.cancelAnimationFrame;
|
||||||
@ -1030,7 +1066,7 @@ QUnit.test('setTimeout should remove dispose handler on trigger', function(asser
|
|||||||
comp.dispose();
|
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 comp = new Component(getFakePlayer());
|
||||||
const oldRAF = window.requestAnimationFrame;
|
const oldRAF = window.requestAnimationFrame;
|
||||||
const oldCAF = window.cancelAnimationFrame;
|
const oldCAF = window.cancelAnimationFrame;
|
||||||
@ -1045,13 +1081,15 @@ QUnit.test('requestAnimationFrame should remove dispose handler on trigger', fun
|
|||||||
|
|
||||||
const spyRAF = sinon.spy();
|
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);
|
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();
|
comp.dispose();
|
||||||
|
|
||||||
@ -1169,6 +1207,122 @@ QUnit.test('setInterval should be canceled on dispose', function(assert) {
|
|||||||
assert.equal(called, false, 'setInterval was never called');
|
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) {
|
QUnit.test('$ and $$ functions', function(assert) {
|
||||||
const comp = new Component(getFakePlayer());
|
const comp = new Component(getFakePlayer());
|
||||||
const contentEl = document.createElement('div');
|
const contentEl = document.createElement('div');
|
||||||
|
Loading…
Reference in New Issue
Block a user