Skip to content

Commit 3b4cdda

Browse files
committed
perf($animate): listen for document visibility changes
Accessing the document for the hidden state is costly for platforms like Electron. Closes angular#14066
1 parent 489835d commit 3b4cdda

File tree

6 files changed

+34
-21
lines changed

6 files changed

+34
-21
lines changed

src/AngularPublic.js

+2
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
$ControllerProvider,
6666
$DateProvider,
6767
$DocumentProvider,
68+
$$IsDocumentHiddenProvider,
6869
$ExceptionHandlerProvider,
6970
$FilterProvider,
7071
$$ForceReflowProvider,
@@ -227,6 +228,7 @@ function publishExternalAPI(angular) {
227228
$cacheFactory: $CacheFactoryProvider,
228229
$controller: $ControllerProvider,
229230
$document: $DocumentProvider,
231+
$$isDocumentHidden: $$IsDocumentHiddenProvider,
230232
$exceptionHandler: $ExceptionHandlerProvider,
231233
$filter: $FilterProvider,
232234
$$forceReflow: $$ForceReflowProvider,

src/ng/animateRunner.js

+3-7
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ var $$AnimateAsyncRunFactoryProvider = function() {
2828
};
2929

3030
var $$AnimateRunnerFactoryProvider = function() {
31-
this.$get = ['$q', '$sniffer', '$$animateAsyncRun', '$document', '$timeout',
32-
function($q, $sniffer, $$animateAsyncRun, $document, $timeout) {
31+
this.$get = ['$q', '$sniffer', '$$animateAsyncRun', '$$isDocumentHidden', '$timeout',
32+
function($q, $sniffer, $$animateAsyncRun, $$isDocumentHidden, $timeout) {
3333

3434
var INITIAL_STATE = 0;
3535
var DONE_PENDING_STATE = 1;
@@ -81,11 +81,7 @@ var $$AnimateRunnerFactoryProvider = function() {
8181

8282
this._doneCallbacks = [];
8383
this._tick = function(fn) {
84-
var doc = $document[0];
85-
86-
// the document may not be ready or attached
87-
// to the module for some internal tests
88-
if (doc && doc.hidden) {
84+
if ($$isDocumentHidden()) {
8985
timeoutTick(fn);
9086
} else {
9187
rafTick(fn);

src/ng/document.js

+16
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,19 @@ function $DocumentProvider() {
3030
return jqLite(window.document);
3131
}];
3232
}
33+
34+
35+
function $$IsDocumentHiddenProvider() {
36+
this.$get = ['$document', function($document) {
37+
var doc = $document[0];
38+
var hidden = doc && doc.hidden;
39+
40+
$document.on('visibilitychange', function() {
41+
hidden = doc.hidden;
42+
});
43+
44+
return function() {
45+
return hidden;
46+
};
47+
}];
48+
}

src/ngAnimate/animateQueue.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,10 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
9797

9898
this.$get = ['$$rAF', '$rootScope', '$rootElement', '$document', '$$HashMap',
9999
'$$animation', '$$AnimateRunner', '$templateRequest', '$$jqLite', '$$forceReflow',
100+
'$$isDocumentHidden',
100101
function($$rAF, $rootScope, $rootElement, $document, $$HashMap,
101-
$$animation, $$AnimateRunner, $templateRequest, $$jqLite, $$forceReflow) {
102+
$$animation, $$AnimateRunner, $templateRequest, $$jqLite, $$forceReflow,
103+
$$isDocumentHidden) {
102104

103105
var activeAnimationsLookup = new $$HashMap();
104106
var disabledElementsLookup = new $$HashMap();
@@ -336,7 +338,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
336338
// past this point if not enabled
337339
// Animations are also disabled if the document is currently hidden (page is not visible
338340
// to the user), because browsers slow down or do not flush calls to requestAnimationFrame
339-
var skipAnimations = !animationsEnabled || $document[0].hidden || disabledElementsLookup.get(node);
341+
var skipAnimations = !animationsEnabled || $$isDocumentHidden() || disabledElementsLookup.get(node);
340342
var existingAnimation = (!skipAnimations && activeAnimationsLookup.get(node)) || {};
341343
var hasExistingAnimation = !!existingAnimation.state;
342344

test/ng/animateRunnerSpec.js

+5-6
Original file line numberDiff line numberDiff line change
@@ -163,14 +163,13 @@ describe("$$AnimateRunner", function() {
163163
}));
164164

165165
it("should use timeouts to trigger async operations when the document is hidden", function() {
166-
var doc;
166+
var hidden = true;
167167

168168
module(function($provide) {
169-
doc = jqLite({
170-
body: document.body,
171-
hidden: true
169+
170+
$provide.value('$$isDocumentHidden', function() {
171+
return hidden;
172172
});
173-
$provide.value('$document', doc);
174173
});
175174

176175
inject(function($$AnimateRunner, $rootScope, $$rAF, $timeout) {
@@ -184,7 +183,7 @@ describe("$$AnimateRunner", function() {
184183
$timeout.flush();
185184
expect(spy).toHaveBeenCalled();
186185

187-
doc[0].hidden = false;
186+
hidden = false;
188187

189188
spy = jasmine.createSpy();
190189
runner = new $$AnimateRunner();

test/ngAnimate/animateSpec.js

+4-6
Original file line numberDiff line numberDiff line change
@@ -156,14 +156,12 @@ describe("animations", function() {
156156
}));
157157

158158
it("should skip animations entirely if the document is hidden", function() {
159-
var doc;
159+
var hidden = true;
160160

161161
module(function($provide) {
162-
doc = jqLite({
163-
body: document.body,
164-
hidden: true
162+
$provide.value('$$isDocumentHidden', function() {
163+
return hidden;
165164
});
166-
$provide.value('$document', doc);
167165
});
168166

169167
inject(function($animate, $rootScope) {
@@ -172,7 +170,7 @@ describe("animations", function() {
172170
expect(capturedAnimation).toBeFalsy();
173171
expect(element[0].parentNode).toEqual(parent[0]);
174172

175-
doc[0].hidden = false;
173+
hidden = false;
176174

177175
$animate.leave(element);
178176
$rootScope.$digest();

0 commit comments

Comments
 (0)