Step one of transition to proper separation of view and document, regarding canvas drawing, mouse interaction, resizing, frame handling, etc. Work in progress.

This commit is contained in:
Jürg Lehni 2011-05-15 21:56:43 +01:00
parent 0dc2241a9c
commit f0e8c54008
3 changed files with 140 additions and 121 deletions

View file

@ -17,87 +17,46 @@
var Document = this.Document = Base.extend({ var Document = this.Document = Base.extend({
beans: true, beans: true,
// XXX: Add arguments to define pages, but do not pass canvas here
initialize: function(canvas) { initialize: function(canvas) {
// Store reference to the currently active global paper scope: // Store reference to the currently active global paper scope:
this._scope = paper; this._scope = paper;
if (canvas && canvas instanceof HTMLCanvasElement) {
this.canvas = canvas;
if (canvas.attributes.resize) {
// If the canvas has a fullscreen attribute,
// resize the canvas to fill the window and resize it again
// whenever the user resizes the window.
// TODO: set the following styles on the body tag:
// body {
// background: black;
// margin: 0;
// overflow: hidden;
// }
this._size = DomElement.getWindowSize()
.subtract(DomElement.getOffset(this.canvas));
this.canvas.width = this._size.width;
this.canvas.height = this._size.height;
var that = this;
var offset = DomElement.getOffset(this.canvas);
DomEvent.add(window, {
resize: function(event) {
// Only get canvas offset if it's not invisible (size is
// 0, 0), as otherwise the offset would be wrong.
if (!DomElement.getSize(that.canvas).equals([0, 0]))
offset = DomElement.getOffset(that.canvas);
that.setSize(DomElement.getWindowSize().subtract(offset));
that.redraw();
}
});
} else {
this._size = Size.create(canvas.offsetWidth,
canvas.offsetHeight);
}
} else {
this._size = Size.read(arguments) || new Size(1024, 768);
this.canvas = CanvasProvider.getCanvas(this._size);
}
// TODO: Currently we don't do anything with Document#bounds.
// What does it mean to change the bounds of the document? (JP)
this.bounds = Rectangle.create(0, 0, this._size.width,
this._size.height);
this.context = this.canvas.getContext('2d');
// Push it onto this._scope.documents and set index: // Push it onto this._scope.documents and set index:
this._index = this._scope.documents.push(this) - 1; this._index = this._scope.documents.push(this) - 1;
// Activate straight away so paper.document is set, as required by
// Layer and DoumentView constructors.
this.activate(); this.activate();
this.layers = []; this.layers = [];
this.activeLayer = new Layer(); this.views = [];
this.setCurrentStyle(null);
this.symbols = []; this.symbols = [];
this.activeView = new DocumentView(this); this.activeLayer = new Layer();
this.views = [this.activeView]; this.activeView = canvas ? new DocumentView(canvas) : null;
// XXX: Introduce pages and remove Document#bounds!
var size = this.activeView && this.activeView._size
|| new Size(1024, 768);
this._bounds = Rectangle.create(0, 0, size.width, size.height);
this._selectedItems = {}; this._selectedItems = {};
this._selectedItemCount = 0; this._selectedItemCount = 0;
// TODO: Test this on IE: this.setCurrentStyle(null);
if (this.canvas.attributes.stats) { },
this.stats = new Stats();
// Align top-left to the canvas getBounds: function() {
var element = this.stats.domElement, // TODO: Consider LinkedRectangle once it is required.
style = element.style, return this._bounds;
offset = DomElement.getOffset(this.canvas); },
style.position = 'absolute';
style.left = offset.x + 'px'; setBounds: function(rect) {
style.top = offset.y + 'px'; rect = Rectangle.read(arguments);
document.body.appendChild(element); this._bounds.set(rect.x, rect.y, rect.width, rect.height);
}
}, },
getSize: function() { getSize: function() {
return this._size; return this._bounds.getSize();
}, },
setSize: function(size) { setSize: function(size) {
size = Size.read(arguments); // TODO: Once _bounds is a LinkedRectangle, this will recurse
if (this.canvas) { this._bounds.setSize.apply(this._bounds, arguments);
this.canvas.width = size.width;
this.canvas.height = size.height;
}
this._size = size;
this.bounds.setSize(size);
}, },
getCurrentStyle: function() { getCurrentStyle: function() {
@ -170,17 +129,7 @@ var Document = this.Document = Base.extend({
this._selectedItems[i].setSelected(false); this._selectedItems[i].setSelected(false);
}, },
draw: function() { draw: function(ctx) {
if (this.canvas) {
if (this.stats)
this.stats.update();
var ctx = this.context;
// Initial tests conclude that clearing the canvas using clearRect
// is always faster than setting canvas.width = canvas.width
// http://jsperf.com/clearrect-vs-setting-width/7
ctx.clearRect(0, 0, this.size.width + 1, this.size.height + 1);
ctx.save(); ctx.save();
var param = { offset: new Point(0, 0) }; var param = { offset: new Point(0, 0) };
for (var i = 0, l = this.layers.length; i < l; i++) for (var i = 0, l = this.layers.length; i < l; i++)
@ -199,10 +148,10 @@ var Document = this.Document = Base.extend({
}); });
ctx.restore(); ctx.restore();
} }
}
}, },
redraw: function() { redraw: function() {
this.draw(); for (var i = 0, l = this.views.length; i < l; i++)
this.views[i].draw();
} }
}); });

View file

@ -17,24 +17,86 @@
var DocumentView = this.DocumentView = Base.extend({ var DocumentView = this.DocumentView = Base.extend({
beans: true, beans: true,
initialize: function(document) { // TODO: Add bounds parameter that defines position within canvas?
this.document = document; // Find a good name for these bounds, since #bounds is already the artboard
this._bounds = this.document.bounds.clone(); // bounds of the visible area.
initialize: function(canvas) {
// To go with the convention of never passing document to constructors,
// in all items, associate the view with the currently active document.
this._document = paper.document;
// Push it onto document.views and set index:
this._index = this._document.views.push(this) - 1;
// Handle canvas argument
if (canvas && canvas instanceof HTMLCanvasElement) {
this._canvas = canvas;
var offset = DomElement.getOffset(canvas);
// If the canvas has the resize attribute, resize the it to fill the
// window and resize it again whenever the user resizes the window.
if (canvas.attributes.resize) {
this._size = DomElement.getWindowSize().subtract(offset);
canvas.width = this._size.width;
canvas.height = this._size.height;
var that = this;
DomEvent.add(window, {
resize: function(event) {
// Only get canvas offset if it's not invisible (size is
// 0, 0), as otherwise the offset would be wrong.
if (!DomElement.getSize(canvas).equals([0, 0]))
offset = DomElement.getOffset(canvas);
that.setSize(DomElement.getWindowSize().subtract(offset));
that.draw();
}
});
} else {
this._size = Size.create(
canvas.offsetWidth, canvas.offsetHeight);
}
// TODO: Test this on IE:
if (canvas.attributes.stats) {
this._stats = new Stats();
// Align top-left to the canvas
var element = this._stats.domElement,
style = element.style;
style.position = 'absolute';
style.left = offset.x + 'px';
style.top = offset.y + 'px';
document.body.appendChild(element);
}
} else {
// 2nd argument onwards could be view size, otherwise use default:
this._size = Size.read(arguments, 1);
if (this._size.isZero())
this._size = new Size(1024, 768);
this._canvas = CanvasProvider.getCanvas(this._size);
}
this._context = this._canvas.getContext('2d');
this._matrix = new Matrix(); this._matrix = new Matrix();
this._zoom = 1; this._zoom = 1;
this._center = this._bounds.center;
}, },
// TODO: test this. getDocument: function() {
return this._document;
},
getSize: function() {
return LinkedSize.create(this, 'setSize',
this._size.width, this._size.height);
},
setSize: function(size) {
this._size = Size.read(arguments);
this._canvas.width = this._size.width;
this._canvas.height = this._size.height;
},
getBounds: function() {
return this._bounds;
},
getCenter: function() { getCenter: function() {
return this._center;
}, },
setCenter: function(center) { setCenter: function(center) {
center = Point.read(arguments);
var delta = center.subtract(this._center);
this.scrollBy(delta);
this._center = center;
}, },
getZoom: function() { getZoom: function() {
@ -42,7 +104,7 @@ var DocumentView = this.DocumentView = Base.extend({
}, },
setZoom: function(zoom) { setZoom: function(zoom) {
// TODO: clamp the view between 1/32 and 64? // TODO: Clamp the view between 1/32 and 64, just like Illustrator?
var mx = new Matrix(); var mx = new Matrix();
mx.scale(zoom / this._zoom, this._center); mx.scale(zoom / this._zoom, this._center);
this.transform(mx); this.transform(mx);
@ -50,8 +112,29 @@ var DocumentView = this.DocumentView = Base.extend({
}, },
scrollBy: function(point) { scrollBy: function(point) {
point = Point.read(arguments); this.transform(new Matrix().translate(Point.read(arguments).negate()));
this.transform(new Matrix().translate(point.negate())); },
draw: function() {
if (this._stats)
this._stats.update();
// Initial tests conclude that clearing the canvas using clearRect
// is always faster than setting canvas.width = canvas.width
// http://jsperf.com/clearrect-vs-setting-width/7
this._context.clearRect(0, 0, this._size.width + 1, this._size.height + 1);
this._document.draw(this._context);
},
activate: function() {
this._document.activeView = this;
},
remove: function() {
var res = Base.splice(this._document.views, null, this._index, 1);
this._document = null;
return !!res.length;
}, },
transform: function(matrix, flags) { transform: function(matrix, flags) {
@ -61,24 +144,11 @@ var DocumentView = this.DocumentView = Base.extend({
}, },
_getInverse: function() { _getInverse: function() {
if (!this._inverse) { if (!this._inverse)
this._inverse = this._matrix.createInverse(); this._inverse = this._matrix.createInverse();
}
return this._inverse; return this._inverse;
}, },
getBounds: function() {
if (!this._bounds) {
this._bounds = this._matrix.transformBounds(this.document.bounds);
}
return this._bounds;
},
// TODO:
// setBounds: function(rect) {
//
// },
// TODO: getInvalidBounds // TODO: getInvalidBounds
// TODO: invalidate(rect) // TODO: invalidate(rect)
// TODO: style: artwork / preview / raster / opaque / ink // TODO: style: artwork / preview / raster / opaque / ink

View file

@ -98,7 +98,7 @@ var Tool = this.Tool = ToolHandler.extend(new function() {
} }
}; };
DomEvent.add(doc.canvas, events); DomEvent.add(doc.activeView._canvas, events);
}, },
getDocument: function() { getDocument: function() {