Remove all direct calls to view.update() and favor of the new view.requestUpdate()

Pure window.requestAnimationFrame() smoothness, automatic updates even when working directly from JavaScript, and no more slow-downs from onLoad events!

Closes #830, #925
This commit is contained in:
Jürg Lehni 2016-01-26 21:37:27 +01:00
parent 9ad63a7231
commit b71ffdbe71
9 changed files with 76 additions and 96 deletions

View file

@ -122,7 +122,6 @@
var image = document.createElement('img'); var image = document.createElement('img');
image.onload = function () { image.onload = function () {
handleImage(image); handleImage(image);
view.update();
}; };
image.src = event.target.result; image.src = event.target.result;
}; };

View file

@ -273,8 +273,6 @@
// var nup = unite(pathA, pathB); // var nup = unite(pathA, pathB);
// console.timeEnd('unite'); // console.timeEnd('unite');
// // nup.style = booleanStyle; // // nup.style = booleanStyle;
// view.update();
} }
var booleanStyle = { var booleanStyle = {
@ -307,7 +305,6 @@
var boolPathU = _p1U.unite(_p2U); var boolPathU = _p1U.unite(_p2U);
console.timeEnd('Union'); console.timeEnd('Union');
boolPathU.style = booleanStyle; boolPathU.style = booleanStyle;
view.update();
} }
if (operations.intersection) { if (operations.intersection) {
@ -318,7 +315,6 @@
var boolPathI = _p1I.intersect(_p2I); var boolPathI = _p1I.intersect(_p2I);
console.timeEnd('Intersection'); console.timeEnd('Intersection');
boolPathI.style = booleanStyle; boolPathI.style = booleanStyle;
view.update();
} }
if (operations.subtraction) { if (operations.subtraction) {
@ -329,7 +325,6 @@
var boolPathS = _p1S.subtract(_p2S); var boolPathS = _p1S.subtract(_p2S);
console.timeEnd('Subtraction'); console.timeEnd('Subtraction');
boolPathS.style = booleanStyle; boolPathS.style = booleanStyle;
view.update();
} }
if (operations.exclusion) { if (operations.exclusion) {
@ -340,7 +335,6 @@
var boolPathE = _p1E.exclude(_p2E); var boolPathE = _p1E.exclude(_p2E);
console.timeEnd('Exclusion'); console.timeEnd('Exclusion');
boolPathE.style = booleanStyle; boolPathE.style = booleanStyle;
view.update();
} }
if (operations.division) { if (operations.division) {
@ -352,7 +346,6 @@
console.timeEnd('Division'); console.timeEnd('Division');
disperse(boolPathD); disperse(boolPathD);
boolPathD.style = booleanStyle; boolPathD.style = booleanStyle;
view.update();
} }
} }

View file

@ -482,8 +482,6 @@ Base.exports.PaperScript = (function() {
}); });
if (res.onFrame) if (res.onFrame)
view.setOnFrame(res.onFrame); view.setOnFrame(res.onFrame);
// Automatically update view at the end.
view.update();
} }
return compiled; return compiled;
} }

View file

@ -134,8 +134,6 @@ var Key = new function() {
paper = scope; paper = scope;
// Call the onKeyDown or onKeyUp handler if present // Call the onKeyDown or onKeyUp handler if present
tool.emit(type, new KeyEvent(down, key, character, event)); tool.emit(type, new KeyEvent(down, key, character, event));
if (view)
view.update();
} }
} }

View file

@ -230,9 +230,6 @@ var Raster = Item.extend(/** @lends Raster# */{
if (view && that.responds(type)) { if (view && that.responds(type)) {
paper = view._scope; paper = view._scope;
that.emit(type, new Event(event)); that.emit(type, new Event(event));
// TODO: Request a redraw in the next animation frame from
// update() instead!
view.update();
} }
} }

View file

@ -88,7 +88,13 @@ 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; this._needsUpdate = true;
var view = this._view;
if (view && !view._requested)
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

View file

@ -558,7 +558,6 @@ new function() {
view = scope.project && scope.getView(); view = scope.project && scope.getView();
if (onLoad) if (onLoad)
onLoad.call(this, item); onLoad.call(this, item);
view.update();
} }
if (isRoot) { if (isRoot) {

View file

@ -119,13 +119,11 @@ var CanvasView = View.extend(/** @lends CanvasView# */{
* 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.
* *
* @param {Boolean} [force=false] {@true if the view should be updated even
* if no change has happened}
* @return {Boolean} {@true if the view was updated} * @return {Boolean} {@true if the view was updated}
*/ */
update: function(force) { update: function() {
var project = this._project; var project = this._project;
if (!project || !force && !project._needsUpdate) if (!project || !project._needsUpdate)
return false; return false;
var ctx = this._context, var ctx = this._context,
size = this._viewSize; size = this._viewSize;

View file

@ -160,18 +160,73 @@ var View = Base.extend(Emitter, /** @lends View# */{
_time: 0, _time: 0,
_count: 0, _count: 0,
_requestFrame: function() { /**
var that = this; * Updates the view if there are changes. Note that when using built-in
DomEvent.requestAnimationFrame(function() { * event hanlders for interaction, animation and load events, this method is
that._requested = false; * invoked for you automatically at the end.
// Do we need to stop due to a call to the frame event's uninstall() *
if (!that._animate) * @name View#update
return; * @function
// Request next frame already before handling the current frame * @param {Boolean} [force=false] {@true if the view should be updated even
that._requestFrame(); * if no change has happened}
that._handleFrame(); * @return {Boolean} {@true if the view was updated}
}, this._element); */
this._requested = true; // update: function(force) {
// },
/**
* Updates the view if there are changes.
*
* @deprecated use {@link #update()} instead.
*/
draw: '#update',
/**
* Requests an update of the view if there are changes through the browser's
* 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) {
var that = this;
DomEvent.requestAnimationFrame(function() {
that._requested = false;
// Only handle frame and request next one if we don't need to
// stop, e.g. due to a call to pause(), or a request for a
// single redraw.
if (that._animate) {
// Request next frame before handling the current frame
that.requestUpdate();
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
that.update();
}, this._element);
this._requested = true;
}
},
/**
* Makes all animation play by adding the view to the request animation
* loop.
*/
play: function() {
this._animate = true;
// Request a frame handler straight away to initialize the
// sequence of onFrame calls.
this.requestUpdate();
},
/**
* Makes all animation pause by removing the view to the request animation
* loop.
*/
pause: function() {
this._animate = false;
}, },
_handleFrame: function() { _handleFrame: function() {
@ -193,8 +248,6 @@ var View = Base.extend(Emitter, /** @lends View# */{
if (this._stats) if (this._stats)
this._stats.update(); this._stats.update();
this._handlingFrame = false; this._handlingFrame = false;
// Automatically update view on each frame.
this.update();
}, },
_animateItem: function(item, animate) { _animateItem: function(item, animate) {
@ -229,20 +282,6 @@ var View = Base.extend(Emitter, /** @lends View# */{
} }
}, },
_update: function() {
this._project._needsUpdate = true;
if (this._handlingFrame)
return;
if (this._animate) {
// If we're animating, call _handleFrame staight away, but without
// requesting another animation frame.
this._handleFrame();
} else {
// Otherwise simply update the view now
this.update();
}
},
/** /**
* Private notifier that is called whenever a change occurs in this view. * Private notifier that is called whenever a change occurs in this view.
* Used only by Matrix for now. * Used only by Matrix for now.
@ -317,7 +356,7 @@ var View = Base.extend(Emitter, /** @lends View# */{
delta: delta delta: delta
}); });
this._changed(); this._changed();
this.update(); this.requestUpdate();
}, },
/** /**
@ -539,49 +578,6 @@ var View = Base.extend(Emitter, /** @lends View# */{
this.translate(Point.read(arguments).negate()); this.translate(Point.read(arguments).negate());
} }
}), /** @lends View# */{ }), /** @lends View# */{
/**
* Makes all animation play by adding the view to the request animation
* loop.
*/
play: function() {
this._animate = true;
// Request a frame handler straight away to initialize the
// sequence of onFrame calls.
if (!this._requested)
this._requestFrame();
},
/**
* Makes all animation pause by removing the view to the request animation
* loop.
*/
pause: function() {
this._animate = false;
},
/**
* Updates the view if there are changes. Note that when using built-in
* event hanlders for interaction, animation and load events, this method is
* invoked for you automatically at the end.
*
* @name View#update
* @function
* @param {Boolean} [force=false] {@true if the view should be updated even
* if no change has happened}
* @return {Boolean} {@true if the view was updated}
*/
// update: function(force) {
// },
/**
* Updates the view if there are changes.
*
* @deprecated use {@link #update()} instead.
*/
draw: function() {
this.update();
},
// TODO: getInvalidBounds // TODO: getInvalidBounds
// TODO: invalidate(rect) // TODO: invalidate(rect)
// TODO: style: artwork / preview / raster / opaque / ink // TODO: style: artwork / preview / raster / opaque / ink
@ -1186,10 +1182,6 @@ new function() { // Injection scope for mouse events on the browser
if (called && (!nativeMove || responds('mousedrag')) if (called && (!nativeMove || responds('mousedrag'))
|| mouse.down && responds('mouseup')) || mouse.down && responds('mouseup'))
event.preventDefault(); event.preventDefault();
// In the end we always call update(), which only updates the view
// if anything has changed in the above calls.
this.update();
}, },
_countItemEvent: function(type, sign) { _countItemEvent: function(type, sign) {