Skip to content

Commit

Permalink
Improve handling of view updates and detection of invisible documents.
Browse files Browse the repository at this point in the history
Switch to the new HTML5 Page Visibility API.
  • Loading branch information
lehni committed Feb 9, 2016
1 parent 80e6246 commit da216aa
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 54 deletions.
4 changes: 4 additions & 0 deletions src/core/PaperScript.js
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,10 @@ Base.exports.PaperScript = (function() {
});
if (res.onFrame)
view.setOnFrame(res.onFrame);
// Automatically request an update at the end. This is only needed
// if the script does not actually produce anything yet, and the
// used canvas contains previous content.
view.requestUpdate();
}
return compiled;
}
Expand Down
53 changes: 16 additions & 37 deletions src/dom/DomEvent.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,49 +72,28 @@ DomEvent.requestAnimationFrame = new function() {
var nativeRequest = DomElement.getPrefixed(window, 'requestAnimationFrame'),
requested = false,
callbacks = [],
focused = true,
timer;

DomEvent.add(window, {
focus: function() {
focused = true;
},
blur: function() {
focused = false;
}
});

function handleCallbacks() {
// Checks all installed callbacks for element visibility and
// execute if needed.
for (var i = callbacks.length - 1; i >= 0; i--) {
var entry = callbacks[i],
func = entry[0],
el = entry[1];
if (!el || (PaperScope.getAttribute(el, 'keepalive') == 'true'
|| focused) && DomElement.isInView(el)) {
// Only remove from the list once the callback was called. This
// could take a long time based on visibility. But this way we
// are sure to keep the animation loop running.
callbacks.splice(i, 1);
func();
}
}
if (nativeRequest) {
if (callbacks.length) {
// If we haven't processed all callbacks yet, we need to keep
// the loop running, as otherwise it would die off.
nativeRequest(handleCallbacks);
} else {
requested = false;
}
}
// Make a local references to the current callbacks array and set
// callbacks to a new empty array, so it can collect the functions for
// the new requests.
var functions = callbacks;
callbacks = [];
// Call the collected callback functions.
for (var i = 0, l = functions.length; i < l; i++)
functions[i]();
// Now see if the above calls have collected new callbacks. Keep
// requesting new frames as long as we have callbacks.
requested = nativeRequest && callbacks.length;
if (requested)
nativeRequest(handleCallbacks);
}

return function(callback, element) {
return function(callback) {
// Add to the list of callbacks to be called in the next animation
// frame.
callbacks.push([callback, element]);
callbacks.push(callback);
if (nativeRequest) {
// Handle animation natively. We only need to request the frame
// once for all collected callbacks.
Expand All @@ -125,7 +104,7 @@ DomEvent.requestAnimationFrame = new function() {
} else if (!timer) {
// Install interval timer that checks all callbacks. This
// results in faster animations than repeatedly installing
// timout timers.
// timeout timers.
timer = setInterval(handleCallbacks, 1000 / 60);
}
};
Expand Down
13 changes: 7 additions & 6 deletions src/item/Project.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,14 @@ var Project = PaperScopeItem.extend(/** @lends Project# */{
*/
_changed: function(flags, item) {
if (flags & /*#=*/ChangeFlag.APPEARANCE) {
// Never draw changes right away. Simply mark the project as "dirty"
// and request a view update through window.requestAnimationFrame()
// (via view.requestUpdate()), which handles the smooth updates.
this._needsUpdate = true;
var view = this._view;
if (view && !view._requested && view._autoUpdate)
view.requestUpdate();
if (view) {
// Never draw changes right away. Simply mark view as "dirty"
// and request an update through view.requestUpdate().
view._needsUpdate = true;
if (!view._requested && view._autoUpdate)
view.requestUpdate();
}
}
// Have project keep track of changed items so they can be iterated.
// This can be used for example to update the SVG tree. Needs to be
Expand Down
10 changes: 6 additions & 4 deletions src/view/CanvasView.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ var CanvasView = View.extend(/** @lends CanvasView# */{
this._pixelRatio = deviceRatio / backingStoreRatio;
}
View.call(this, project, canvas);
// We can't be sure the canvas is clear
this._needsUpdate = true;
},

remove: function remove() {
Expand Down Expand Up @@ -128,14 +130,14 @@ var CanvasView = View.extend(/** @lends CanvasView# */{
* @return {Boolean} {@true if the view was updated}
*/
update: function() {
var project = this._project;
if (!project || !project._needsUpdate)
if (!this._needsUpdate)
return false;
var ctx = this._context,
var project = this._project,
ctx = this._context,
size = this._viewSize;
ctx.clearRect(0, 0, size.width + 1, size.height + 1);
project.draw(ctx, this._matrix, this._pixelRatio);
project._needsUpdate = false;
this._needsUpdate = false;
return true;
}
});
22 changes: 15 additions & 7 deletions src/view/View.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ var View = Base.extend(Emitter, /** @lends View# */{
this._itemEvents = { native: {}, virtual: {} };
// Do not set _autoUpdate on Node.js by default:
this._autoUpdate = !paper.agent.node;
this._needsUpdate = false;
},

/**
Expand Down Expand Up @@ -198,8 +199,6 @@ var View = Base.extend(Emitter, /** @lends View# */{
* event hanlders for interaction, animation and load events, this method is
* invoked for you automatically at the end.
*
* @name View#update
* @function
* @return {Boolean} {@true if the view was updated}
*/
update: function() {
Expand All @@ -220,8 +219,6 @@ var View = Base.extend(Emitter, /** @lends View# */{
* requestAnimationFrame() mechanism for smooth animation. Note that when
* using built-in event handlers for interaction, animation and load events,
* updates are automatically invoked for you automatically at the end.
*
* @function
*/
requestUpdate: function() {
if (!this._requested) {
Expand All @@ -234,13 +231,24 @@ var View = Base.extend(Emitter, /** @lends View# */{
if (that._animate) {
// Request next update before handling the current frame
that.requestUpdate();
that._handleFrame();
var element = that._element;
// Only keep animating if we're allowed to, based on whether
// the document is visible and the setting of keepalive. We
// keep requesting frame regardless though, so the animation
// picks up again as soon as the view is visible.
if ((!DomElement.getPrefixed(document, 'hidden')
|| PaperScope.getAttribute(element, 'keepalive')
=== 'true') && DomElement.isInView(element)) {
that._handleFrame();
}
}
// Even if we're not animating, update the view now since this
// might have been a request for a single redraw after a change
// might have been a request for a single redraw after a change.
// NOTE: If nothing has changed (e.g. _handleFrame() wasn't
// called above), then this does not actually do anything.
if (that._autoUpdate)
that.update();
}, this._element);
});
this._requested = true;
}
},
Expand Down

0 comments on commit da216aa

Please sign in to comment.