mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-01-08 05:42:07 -05:00
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:
parent
0dc2241a9c
commit
f0e8c54008
3 changed files with 140 additions and 121 deletions
|
@ -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();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
Loading…
Reference in a new issue