Improve handling of view updates and detection of invisible documents.

Switch to the new HTML5 Page Visibility API.
This commit is contained in:
Jürg Lehni 2016-02-09 09:59:19 +01:00
parent 80e6246016
commit da216aa581
5 changed files with 48 additions and 54 deletions

View file

@ -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;
}

View file

@ -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.
@ -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);
}
};

View file

@ -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

View file

@ -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() {
@ -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;
}
});

View file

@ -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;
},
/**
@ -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() {
@ -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) {
@ -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;
}
},