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({
beans: true,
// XXX: Add arguments to define pages, but do not pass canvas here
initialize: function(canvas) {
// Store reference to the currently active global paper scope:
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:
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.layers = [];
this.activeLayer = new Layer();
this.setCurrentStyle(null);
this.views = [];
this.symbols = [];
this.activeView = new DocumentView(this);
this.views = [this.activeView];
this.activeLayer = new Layer();
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._selectedItemCount = 0;
// TODO: Test this on IE:
if (this.canvas.attributes.stats) {
this.stats = new Stats();
// Align top-left to the canvas
var element = this.stats.domElement,
style = element.style,
offset = DomElement.getOffset(this.canvas);
style.position = 'absolute';
style.left = offset.x + 'px';
style.top = offset.y + 'px';
document.body.appendChild(element);
}
this.setCurrentStyle(null);
},
getBounds: function() {
// TODO: Consider LinkedRectangle once it is required.
return this._bounds;
},
setBounds: function(rect) {
rect = Rectangle.read(arguments);
this._bounds.set(rect.x, rect.y, rect.width, rect.height);
},
getSize: function() {
return this._size;
return this._bounds.getSize();
},
setSize: function(size) {
size = Size.read(arguments);
if (this.canvas) {
this.canvas.width = size.width;
this.canvas.height = size.height;
}
this._size = size;
this.bounds.setSize(size);
// TODO: Once _bounds is a LinkedRectangle, this will recurse
this._bounds.setSize.apply(this._bounds, arguments);
},
getCurrentStyle: function() {
@ -170,39 +129,29 @@ var Document = this.Document = Base.extend({
this._selectedItems[i].setSelected(false);
},
draw: function() {
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);
draw: function(ctx) {
ctx.save();
var param = { offset: new Point(0, 0) };
for (var i = 0, l = this.layers.length; i < l; i++)
Item.draw(this.layers[i], ctx, param);
ctx.restore();
// Draw the selection of the selected items in the document:
if (this._selectedItemCount > 0) {
ctx.save();
var param = { offset: new Point(0, 0) };
for (var i = 0, l = this.layers.length; i < l; i++)
Item.draw(this.layers[i], ctx, param);
ctx.strokeWidth = 1;
// TODO: use Layer#color
ctx.strokeStyle = ctx.fillStyle = '#009dec';
param = { selection: true };
Base.each(this._selectedItems, function(item) {
item.draw(ctx, param);
});
ctx.restore();
// Draw the selection of the selected items in the document:
if (this._selectedItemCount > 0) {
ctx.save();
ctx.strokeWidth = 1;
// TODO: use Layer#color
ctx.strokeStyle = ctx.fillStyle = '#009dec';
param = { selection: true };
Base.each(this._selectedItems, function(item) {
item.draw(ctx, param);
});
ctx.restore();
}
}
},
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({
beans: true,
initialize: function(document) {
this.document = document;
this._bounds = this.document.bounds.clone();
// TODO: Add bounds parameter that defines position within canvas?
// Find a good name for these bounds, since #bounds is already the artboard
// 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._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() {
return this._center;
},
setCenter: function(center) {
center = Point.read(arguments);
var delta = center.subtract(this._center);
this.scrollBy(delta);
this._center = center;
},
getZoom: function() {
@ -42,7 +104,7 @@ var DocumentView = this.DocumentView = Base.extend({
},
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();
mx.scale(zoom / this._zoom, this._center);
this.transform(mx);
@ -50,8 +112,29 @@ var DocumentView = this.DocumentView = Base.extend({
},
scrollBy: function(point) {
point = Point.read(arguments);
this.transform(new Matrix().translate(point.negate()));
this.transform(new Matrix().translate(Point.read(arguments).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) {
@ -61,24 +144,11 @@ var DocumentView = this.DocumentView = Base.extend({
},
_getInverse: function() {
if (!this._inverse) {
if (!this._inverse)
this._inverse = this._matrix.createInverse();
}
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: invalidate(rect)
// 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() {