mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-03-13 16:33:28 -04:00
Improve handling of item based onFrame handlers, by moving functionality to View and removing handlers properly when the view is destroyed.
This commit is contained in:
parent
5b56bd7fbf
commit
cf5853c8cc
3 changed files with 109 additions and 88 deletions
|
@ -75,19 +75,6 @@ var Item = this.Item = Base.extend(Callback, /** @lends Item# */{
|
|||
}
|
||||
};
|
||||
|
||||
var onFrameItems = [];
|
||||
function onFrame(event) {
|
||||
// Note: Do not optimaize onFrameItems.length since it may change!
|
||||
for (var i = 0; i < onFrameItems.length; i++) {
|
||||
var item = onFrameItems[i];
|
||||
if (item)
|
||||
item.fire('frame', event);
|
||||
else
|
||||
// item was marked for delition. Remove it, and reduce index
|
||||
onFrameItems.splice(i--, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return Base.each(['onMouseDown', 'onMouseUp', 'onMouseDrag', 'onClick',
|
||||
'onDoubleClick', 'onMouseMove', 'onMouseEnter', 'onMouseLeave'],
|
||||
function(name) {
|
||||
|
@ -95,21 +82,10 @@ var Item = this.Item = Base.extend(Callback, /** @lends Item# */{
|
|||
}, {
|
||||
onFrame: {
|
||||
install: function() {
|
||||
if (!onFrameItems.length)
|
||||
this._project.view.attach('frame', onFrame);
|
||||
onFrameItems.push(this);
|
||||
this._project.view._animateItem(this, true);
|
||||
},
|
||||
uninstall: function() {
|
||||
// Mark for deletion, but do not remove it yet, since
|
||||
// removing handlers from inside handlers would mess up
|
||||
// onFrame loop above otherwise.
|
||||
onFrameItems[onFrameItems.indexOf(this)] = null;
|
||||
if (onFrameItems.length == 1) {
|
||||
// If this is the last one, just stop animating
|
||||
// straight away.
|
||||
this._project.view.detach('frame', onFrame);
|
||||
onFrameItems = [];
|
||||
}
|
||||
this._project.view._animateItem(this, false);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -52,14 +52,14 @@ var Project = this.Project = PaperScopeItem.extend(/** @lends Project# */{
|
|||
// Activate straight away by passing true to base(), so paper.project is
|
||||
// set, as required by Layer and DoumentView constructors.
|
||||
this.base(true);
|
||||
this._currentStyle = new PathStyle();
|
||||
this._selectedItems = {};
|
||||
this._selectedItemCount = 0;
|
||||
this.layers = [];
|
||||
this.symbols = [];
|
||||
this.activeLayer = new Layer();
|
||||
if (view)
|
||||
this.view = view instanceof View ? view : View.create(view);
|
||||
this._currentStyle = new PathStyle();
|
||||
this._selectedItems = {};
|
||||
this._selectedItemCount = 0;
|
||||
// Change tracking, not in use for now. Activate once required:
|
||||
// this._changes = [];
|
||||
// this._changesById = {};
|
||||
|
|
163
src/ui/View.js
163
src/ui/View.js
|
@ -24,60 +24,6 @@
|
|||
* screen.
|
||||
*/
|
||||
var View = this.View = Base.extend(Callback, /** @lends View# */{
|
||||
_events: {
|
||||
onFrame: {
|
||||
install: function() {
|
||||
/*#*/ if (options.browser) {
|
||||
var that = this,
|
||||
requested = false,
|
||||
before,
|
||||
time = 0,
|
||||
count = 0;
|
||||
this._onFrameCallback = function(param, dontRequest) {
|
||||
requested = false;
|
||||
// See if we need to stop due to a call to uninstall()
|
||||
if (!that._onFrameCallback)
|
||||
return;
|
||||
// Set the global paper object to the current scope
|
||||
paper = that._scope;
|
||||
if (!dontRequest) {
|
||||
// Request next frame already
|
||||
requested = true;
|
||||
DomEvent.requestAnimationFrame(that._onFrameCallback,
|
||||
that._element);
|
||||
}
|
||||
var now = Date.now() / 1000,
|
||||
delta = before ? now - before : 0;
|
||||
// delta: Time elapsed since last redraw in seconds
|
||||
// time: Time since first call of frame() in seconds
|
||||
// Use Base.merge to convert into a Base object,
|
||||
// for #toString()
|
||||
that.fire('frame', Base.merge({
|
||||
delta: delta,
|
||||
time: time += delta,
|
||||
count: count++
|
||||
}));
|
||||
before = now;
|
||||
// Update framerate stats
|
||||
if (that._stats)
|
||||
that._stats.update();
|
||||
// Automatically draw view on each frame.
|
||||
that.draw(true);
|
||||
};
|
||||
// Call the onFrame handler straight away, initializing the
|
||||
// sequence of onFrame calls.
|
||||
if (!requested)
|
||||
this._onFrameCallback();
|
||||
/*#*/ } // options.browser
|
||||
},
|
||||
|
||||
uninstall: function() {
|
||||
delete this._onFrameCallback;
|
||||
}
|
||||
},
|
||||
|
||||
onResize: {}
|
||||
},
|
||||
|
||||
initialize: function(element) {
|
||||
// Store reference to the currently active global paper scope, and the
|
||||
|
@ -157,6 +103,8 @@ var View = this.View = Base.extend(Callback, /** @lends View# */{
|
|||
// Make sure the first view is focused for keyboard input straight away
|
||||
if (!View._focused)
|
||||
View._focused = this;
|
||||
// Items that need the onFrame handler called on them
|
||||
this._frameItems = [];
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -178,18 +126,115 @@ var View = this.View = Base.extend(Callback, /** @lends View# */{
|
|||
DomEvent.remove(this._element, this._viewHandlers);
|
||||
DomEvent.remove(window, this._windowHandlers);
|
||||
this._element = this._project = null;
|
||||
// Removing all onFrame handlers makes the _onFrameCallback handler stop
|
||||
// Removing all onFrame handlers makes the onFrame handler stop
|
||||
// automatically through its uninstall method.
|
||||
this.detach('frame');
|
||||
this._frameItems = [];
|
||||
return true;
|
||||
},
|
||||
|
||||
_events: {
|
||||
onFrame: {
|
||||
install: function() {
|
||||
/*#*/ if (options.browser) {
|
||||
// Call the onFrame handler straight away and initialize the
|
||||
// sequence of onFrame calls.
|
||||
if (!this._requested) {
|
||||
this._animate = true;
|
||||
this._handleFrame(true);
|
||||
}
|
||||
/*#*/ } // options.browser
|
||||
},
|
||||
|
||||
uninstall: function() {
|
||||
this._animate = false;
|
||||
}
|
||||
},
|
||||
|
||||
onResize: {}
|
||||
},
|
||||
|
||||
// These are default values for event related properties on the prototype.
|
||||
// Writing item._count++ does not change the defaults, it creates / updates
|
||||
// the property on the instance. Useful!
|
||||
_animate: false,
|
||||
_time: 0,
|
||||
_count: 0,
|
||||
|
||||
_handleFrame: function(request) {
|
||||
this._requested = false;
|
||||
// See if we need to stop due to a call to uninstall()
|
||||
if (!this._animate)
|
||||
return;
|
||||
// Set the global paper object to the current scope
|
||||
paper = this._scope;
|
||||
if (request) {
|
||||
// Request next frame already
|
||||
this._requested = true;
|
||||
var that = this;
|
||||
DomEvent.requestAnimationFrame(function() {
|
||||
that._handleFrame(true);
|
||||
}, this._element);
|
||||
}
|
||||
var now = Date.now() / 1000,
|
||||
delta = this._before ? now - this._before : 0;
|
||||
this._before = now;
|
||||
// Use Base.merge to convert into a Base object, for #toString()
|
||||
this.fire('frame', Base.merge({
|
||||
// Time elapsed since last redraw in seconds:
|
||||
delta: delta,
|
||||
// Time since first call of frame() in seconds:
|
||||
time: this._time += delta,
|
||||
count: this._count++
|
||||
}));
|
||||
// Update framerate stats
|
||||
if (this._stats)
|
||||
this._stats.update();
|
||||
// Automatically draw view on each frame.
|
||||
this.draw(true);
|
||||
},
|
||||
|
||||
_animateItem: function(item, animate) {
|
||||
var items = this._frameItems;
|
||||
if (animate) {
|
||||
if (!items.length)
|
||||
this.attach('frame', this._handleFrameItems);
|
||||
items.push(item);
|
||||
} else {
|
||||
// Mark for deletion, but do not remove it yet, since
|
||||
// removing handlers from inside handlers would mess up
|
||||
// onFrame loop in the view otherwise.
|
||||
items[items.indexOf(this)] = null;
|
||||
if (items.length == 1) {
|
||||
// If this is the last one, just stop animating straight away.
|
||||
this.detach('frame', this._handleFrameItems);
|
||||
this._frameItems = [];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// An empty callback that's only there so _frameItems can be handled
|
||||
// through the onFrame callback framework that automatically starts and
|
||||
// stops the animation for us whenever there's one or more frame handlers
|
||||
_handleFrameItems: function(event) {
|
||||
var items = this._frameItems;
|
||||
// Note: Do not optimaize onFrameItems.length since it may change!
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
var item = items[i];
|
||||
if (item)
|
||||
item.fire('frame', event);
|
||||
else
|
||||
// item was marked for delition. Remove it, and reduce index
|
||||
items.splice(i--, 1);
|
||||
}
|
||||
},
|
||||
|
||||
_redraw: function() {
|
||||
this._redrawNeeded = true;
|
||||
if (this._onFrameCallback) {
|
||||
// If there's a _onFrameCallback, call it staight away,
|
||||
// but without requesting another animation frame.
|
||||
this._onFrameCallback(0, true);
|
||||
if (this._animate) {
|
||||
// If we're animating, call _handleFrame staight away, but without
|
||||
// requesting another animation frame.
|
||||
this._handleFrame();
|
||||
} else {
|
||||
// Otherwise simply redraw the view now
|
||||
this.draw();
|
||||
|
|
Loading…
Reference in a new issue