mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-01-22 07:19:57 -05:00
Improve handling of view updates and detection of invisible documents.
Switch to the new HTML5 Page Visibility API.
This commit is contained in:
parent
80e6246016
commit
da216aa581
5 changed files with 48 additions and 54 deletions
|
@ -482,6 +482,10 @@ Base.exports.PaperScript = (function() {
|
||||||
});
|
});
|
||||||
if (res.onFrame)
|
if (res.onFrame)
|
||||||
view.setOnFrame(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;
|
return compiled;
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,49 +72,28 @@ DomEvent.requestAnimationFrame = new function() {
|
||||||
var nativeRequest = DomElement.getPrefixed(window, 'requestAnimationFrame'),
|
var nativeRequest = DomElement.getPrefixed(window, 'requestAnimationFrame'),
|
||||||
requested = false,
|
requested = false,
|
||||||
callbacks = [],
|
callbacks = [],
|
||||||
focused = true,
|
|
||||||
timer;
|
timer;
|
||||||
|
|
||||||
DomEvent.add(window, {
|
|
||||||
focus: function() {
|
|
||||||
focused = true;
|
|
||||||
},
|
|
||||||
blur: function() {
|
|
||||||
focused = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function handleCallbacks() {
|
function handleCallbacks() {
|
||||||
// Checks all installed callbacks for element visibility and
|
// Make a local references to the current callbacks array and set
|
||||||
// execute if needed.
|
// callbacks to a new empty array, so it can collect the functions for
|
||||||
for (var i = callbacks.length - 1; i >= 0; i--) {
|
// the new requests.
|
||||||
var entry = callbacks[i],
|
var functions = callbacks;
|
||||||
func = entry[0],
|
callbacks = [];
|
||||||
el = entry[1];
|
// Call the collected callback functions.
|
||||||
if (!el || (PaperScope.getAttribute(el, 'keepalive') == 'true'
|
for (var i = 0, l = functions.length; i < l; i++)
|
||||||
|| focused) && DomElement.isInView(el)) {
|
functions[i]();
|
||||||
// Only remove from the list once the callback was called. This
|
// Now see if the above calls have collected new callbacks. Keep
|
||||||
// could take a long time based on visibility. But this way we
|
// requesting new frames as long as we have callbacks.
|
||||||
// are sure to keep the animation loop running.
|
requested = nativeRequest && callbacks.length;
|
||||||
callbacks.splice(i, 1);
|
if (requested)
|
||||||
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);
|
nativeRequest(handleCallbacks);
|
||||||
} else {
|
|
||||||
requested = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return function(callback, element) {
|
return function(callback) {
|
||||||
// Add to the list of callbacks to be called in the next animation
|
// Add to the list of callbacks to be called in the next animation
|
||||||
// frame.
|
// frame.
|
||||||
callbacks.push([callback, element]);
|
callbacks.push(callback);
|
||||||
if (nativeRequest) {
|
if (nativeRequest) {
|
||||||
// Handle animation natively. We only need to request the frame
|
// Handle animation natively. We only need to request the frame
|
||||||
// once for all collected callbacks.
|
// once for all collected callbacks.
|
||||||
|
@ -125,7 +104,7 @@ DomEvent.requestAnimationFrame = new function() {
|
||||||
} else if (!timer) {
|
} else if (!timer) {
|
||||||
// Install interval timer that checks all callbacks. This
|
// Install interval timer that checks all callbacks. This
|
||||||
// results in faster animations than repeatedly installing
|
// results in faster animations than repeatedly installing
|
||||||
// timout timers.
|
// timeout timers.
|
||||||
timer = setInterval(handleCallbacks, 1000 / 60);
|
timer = setInterval(handleCallbacks, 1000 / 60);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -87,14 +87,15 @@ var Project = PaperScopeItem.extend(/** @lends Project# */{
|
||||||
*/
|
*/
|
||||||
_changed: function(flags, item) {
|
_changed: function(flags, item) {
|
||||||
if (flags & /*#=*/ChangeFlag.APPEARANCE) {
|
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;
|
var view = this._view;
|
||||||
if (view && !view._requested && view._autoUpdate)
|
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();
|
view.requestUpdate();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Have project keep track of changed items so they can be iterated.
|
// 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
|
// This can be used for example to update the SVG tree. Needs to be
|
||||||
// activated in Project
|
// activated in Project
|
||||||
|
|
|
@ -56,6 +56,8 @@ var CanvasView = View.extend(/** @lends CanvasView# */{
|
||||||
this._pixelRatio = deviceRatio / backingStoreRatio;
|
this._pixelRatio = deviceRatio / backingStoreRatio;
|
||||||
}
|
}
|
||||||
View.call(this, project, canvas);
|
View.call(this, project, canvas);
|
||||||
|
// We can't be sure the canvas is clear
|
||||||
|
this._needsUpdate = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
remove: function remove() {
|
remove: function remove() {
|
||||||
|
@ -128,14 +130,14 @@ var CanvasView = View.extend(/** @lends CanvasView# */{
|
||||||
* @return {Boolean} {@true if the view was updated}
|
* @return {Boolean} {@true if the view was updated}
|
||||||
*/
|
*/
|
||||||
update: function() {
|
update: function() {
|
||||||
var project = this._project;
|
if (!this._needsUpdate)
|
||||||
if (!project || !project._needsUpdate)
|
|
||||||
return false;
|
return false;
|
||||||
var ctx = this._context,
|
var project = this._project,
|
||||||
|
ctx = this._context,
|
||||||
size = this._viewSize;
|
size = this._viewSize;
|
||||||
ctx.clearRect(0, 0, size.width + 1, size.height + 1);
|
ctx.clearRect(0, 0, size.width + 1, size.height + 1);
|
||||||
project.draw(ctx, this._matrix, this._pixelRatio);
|
project.draw(ctx, this._matrix, this._pixelRatio);
|
||||||
project._needsUpdate = false;
|
this._needsUpdate = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -119,6 +119,7 @@ var View = Base.extend(Emitter, /** @lends View# */{
|
||||||
this._itemEvents = { native: {}, virtual: {} };
|
this._itemEvents = { native: {}, virtual: {} };
|
||||||
// Do not set _autoUpdate on Node.js by default:
|
// Do not set _autoUpdate on Node.js by default:
|
||||||
this._autoUpdate = !paper.agent.node;
|
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
|
* event hanlders for interaction, animation and load events, this method is
|
||||||
* invoked for you automatically at the end.
|
* invoked for you automatically at the end.
|
||||||
*
|
*
|
||||||
* @name View#update
|
|
||||||
* @function
|
|
||||||
* @return {Boolean} {@true if the view was updated}
|
* @return {Boolean} {@true if the view was updated}
|
||||||
*/
|
*/
|
||||||
update: function() {
|
update: function() {
|
||||||
|
@ -220,8 +219,6 @@ var View = Base.extend(Emitter, /** @lends View# */{
|
||||||
* requestAnimationFrame() mechanism for smooth animation. Note that when
|
* requestAnimationFrame() mechanism for smooth animation. Note that when
|
||||||
* using built-in event handlers for interaction, animation and load events,
|
* using built-in event handlers for interaction, animation and load events,
|
||||||
* updates are automatically invoked for you automatically at the end.
|
* updates are automatically invoked for you automatically at the end.
|
||||||
*
|
|
||||||
* @function
|
|
||||||
*/
|
*/
|
||||||
requestUpdate: function() {
|
requestUpdate: function() {
|
||||||
if (!this._requested) {
|
if (!this._requested) {
|
||||||
|
@ -234,13 +231,24 @@ var View = Base.extend(Emitter, /** @lends View# */{
|
||||||
if (that._animate) {
|
if (that._animate) {
|
||||||
// Request next update before handling the current frame
|
// Request next update before handling the current frame
|
||||||
that.requestUpdate();
|
that.requestUpdate();
|
||||||
|
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();
|
that._handleFrame();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Even if we're not animating, update the view now since this
|
// 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)
|
if (that._autoUpdate)
|
||||||
that.update();
|
that.update();
|
||||||
}, this._element);
|
});
|
||||||
this._requested = true;
|
this._requested = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue