mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-01-22 07:19:57 -05:00
Implement #insertAbove/Below using the new #insertChild, allowing the removal of one injection scope, and rearrange function sequence for documentation.
This commit is contained in:
parent
69d7e026ba
commit
ee0446fa4f
1 changed files with 222 additions and 236 deletions
458
src/item/Item.js
458
src/item/Item.js
|
@ -510,6 +510,216 @@ var Item = this.Item = Base.extend({
|
||||||
return this._index;
|
return this._index;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clones the item within the same project and places the copy above the
|
||||||
|
* item.
|
||||||
|
*
|
||||||
|
* @return {Item} the newly cloned item
|
||||||
|
*
|
||||||
|
* @example {@paperscript}
|
||||||
|
* // Cloning items:
|
||||||
|
* var circle = new Path.Circle(new Point(50, 50), 10);
|
||||||
|
* circle.fillColor = 'red';
|
||||||
|
*
|
||||||
|
* // Make 20 copies of the circle:
|
||||||
|
* for (var i = 0; i < 20; i++) {
|
||||||
|
* var copy = circle.clone();
|
||||||
|
*
|
||||||
|
* // Distribute the copies horizontally, so we can see them:
|
||||||
|
* copy.position.x += i * copy.bounds.width;
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
clone: function() {
|
||||||
|
return this._clone(new this.constructor());
|
||||||
|
},
|
||||||
|
|
||||||
|
_clone: function(copy) {
|
||||||
|
// Copy over style
|
||||||
|
copy.setStyle(this._style);
|
||||||
|
// If this item has children, clone and append each of them:
|
||||||
|
if (this._children) {
|
||||||
|
for (var i = 0, l = this._children.length; i < l; i++)
|
||||||
|
copy.addChild(this._children[i].clone());
|
||||||
|
}
|
||||||
|
// Only copy over these fields if they are actually defined in 'this'
|
||||||
|
// TODO: Consider moving this to Base once it's useful in more than one
|
||||||
|
// place
|
||||||
|
var keys = ['locked', 'visible', 'opacity', 'blendMode', '_clipMask',
|
||||||
|
'_selected'];
|
||||||
|
for (var i = 0, l = keys.length; i < l; i++) {
|
||||||
|
var key = keys[i];
|
||||||
|
if (this.hasOwnProperty(key))
|
||||||
|
copy[key] = this[key];
|
||||||
|
}
|
||||||
|
// Insert the clone above the original, at the same position.
|
||||||
|
copy.insertAbove(this);
|
||||||
|
// Only set name once the copy is moved, to avoid setting and unsettting
|
||||||
|
// name related structures.
|
||||||
|
if (this._name)
|
||||||
|
copy.setName(this._name);
|
||||||
|
return copy;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When passed a project, copies the item to the project,
|
||||||
|
* or duplicates it within the same project. When passed an item,
|
||||||
|
* copies the item into the specified item.
|
||||||
|
*
|
||||||
|
* @param {Project|Layer|Group|CompoundPath} item the item or project to
|
||||||
|
* copy the item to
|
||||||
|
* @return {Item} the new copy of the item
|
||||||
|
*/
|
||||||
|
copyTo: function(itemOrProject) {
|
||||||
|
var copy = this.clone();
|
||||||
|
if (itemOrProject.layers) {
|
||||||
|
itemOrProject.activeLayer.addChild(copy);
|
||||||
|
} else {
|
||||||
|
itemOrProject.addChild(copy);
|
||||||
|
}
|
||||||
|
return copy;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rasterizes the item into a newly created Raster object. The item itself
|
||||||
|
* is not removed after rasterization.
|
||||||
|
*
|
||||||
|
* @param {Number} [resolution=72] the resolution of the raster in dpi
|
||||||
|
* @return {Raster} the newly created raster item
|
||||||
|
*
|
||||||
|
* @example {@paperscript}
|
||||||
|
* // Rasterizing an item:
|
||||||
|
* var circle = new Path.Circle(new Point(80, 50), 5);
|
||||||
|
* circle.fillColor = 'red';
|
||||||
|
*
|
||||||
|
* // Create a rasterized version of the path:
|
||||||
|
* var raster = circle.rasterize();
|
||||||
|
*
|
||||||
|
* // Move it 100pt to the right:
|
||||||
|
* raster.position.x += 100;
|
||||||
|
*
|
||||||
|
* // Scale the path and the raster by 300%, so we can compare them:
|
||||||
|
* circle.scale(5);
|
||||||
|
* raster.scale(5);
|
||||||
|
*/
|
||||||
|
rasterize: function(resolution) {
|
||||||
|
// TODO: why would we want to pass a size to rasterize? Seems to produce
|
||||||
|
// weird results on Scriptographer. Also we can't use antialiasing, since
|
||||||
|
// Canvas doesn't support it yet. Project colorMode is also out of the
|
||||||
|
// question for now.
|
||||||
|
var bounds = this.getStrokeBounds(),
|
||||||
|
scale = (resolution || 72) / 72,
|
||||||
|
canvas = CanvasProvider.getCanvas(bounds.getSize().multiply(scale)),
|
||||||
|
ctx = canvas.getContext('2d'),
|
||||||
|
matrix = new Matrix().scale(scale).translate(-bounds.x, -bounds.y);
|
||||||
|
matrix.applyToContext(ctx);
|
||||||
|
this.draw(ctx, {});
|
||||||
|
var raster = new Raster(canvas);
|
||||||
|
raster.setPosition(this.getPosition());
|
||||||
|
raster.scale(1 / scale);
|
||||||
|
return raster;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@grouptitle Hierarchy Operations}
|
||||||
|
* Adds the specified item as a child of the item at the end of the
|
||||||
|
* its children list. You can use this function for groups, compound
|
||||||
|
* paths and layers.
|
||||||
|
*
|
||||||
|
* @param {Item} item The item that will be added as a child
|
||||||
|
*/
|
||||||
|
addChild: function(item) {
|
||||||
|
return this.insertChild(undefined, item);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts the specified item as a child of the item at the specified
|
||||||
|
* index in its {@link #children} list. You can use this function for
|
||||||
|
* groups, compound paths and layers.
|
||||||
|
*
|
||||||
|
* @param {Number} index
|
||||||
|
* @param {Item} item The item that will be appended as a child
|
||||||
|
*/
|
||||||
|
insertChild: function(index, item) {
|
||||||
|
if (this._children) {
|
||||||
|
item._removeFromParent();
|
||||||
|
Base.splice(this._children, [item], index, 0);
|
||||||
|
item._parent = this;
|
||||||
|
item._setProject(this._project);
|
||||||
|
if (item._name)
|
||||||
|
item.setName(item._name);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts this item above the specified item.
|
||||||
|
*
|
||||||
|
* @param {Item} item The item above which it should be moved
|
||||||
|
* @return {Boolean} {@true it was inserted}
|
||||||
|
*/
|
||||||
|
insertAbove: function(item) {
|
||||||
|
return item._parent && item._parent.insertChild(
|
||||||
|
item._index + 1, this);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts this item below the specified item.
|
||||||
|
*
|
||||||
|
* @param {Item} item The item above which it should be moved
|
||||||
|
* @return {Boolean} {@true it was inserted}
|
||||||
|
*/
|
||||||
|
insertBelow: function(item) {
|
||||||
|
return item._parent && item._parent.insertChild(
|
||||||
|
item._index - 1, this);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts the specified item as a child of the item by appending it to
|
||||||
|
* the list of children and moving it above all other children. You can
|
||||||
|
* use this function for groups, compound paths and layers.
|
||||||
|
*
|
||||||
|
* @param {Item} item The item that will be appended as a child
|
||||||
|
* @deprecated use {@link #addChild(item)} instead.
|
||||||
|
*/
|
||||||
|
appendTop: function(item) {
|
||||||
|
return this.addChild(item);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts the specified item as a child of this item by appending it to
|
||||||
|
* the list of children and moving it below all other children. You can
|
||||||
|
* use this function for groups, compound paths and layers.
|
||||||
|
*
|
||||||
|
* @param {Item} item The item that will be appended as a child
|
||||||
|
* @deprecated use {@link #insertChild(index, item)} instead.
|
||||||
|
*/
|
||||||
|
appendBottom: function(item) {
|
||||||
|
return this.insertChild(0, item);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves this item above the specified item.
|
||||||
|
*
|
||||||
|
* @param {Item} item The item above which it should be moved
|
||||||
|
* @return {Boolean} {@true it was moved}
|
||||||
|
* @deprecated use {@link #insertAbove(item)} instead.
|
||||||
|
*/
|
||||||
|
moveAbove: function(item) {
|
||||||
|
return this.insertAbove(item);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the item below the specified item.
|
||||||
|
*
|
||||||
|
* @param {Item} item the item below which it should be moved
|
||||||
|
* @return {Boolean} {@true it was moved}
|
||||||
|
* @deprecated use {@link #insertBelow(item)} instead.
|
||||||
|
*/
|
||||||
|
moveBelow: function(item) {
|
||||||
|
return this.insertBelow(item);
|
||||||
|
},
|
||||||
|
|
||||||
_removeFromNamed: function() {
|
_removeFromNamed: function() {
|
||||||
var children = this._parent._children,
|
var children = this._parent._children,
|
||||||
namedChildren = this._parent._namedChildren,
|
namedChildren = this._parent._namedChildren,
|
||||||
|
@ -573,75 +783,6 @@ var Item = this.Item = Base.extend({
|
||||||
return removed;
|
return removed;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* When passed a project, copies the item to the project,
|
|
||||||
* or duplicates it within the same project. When passed an item,
|
|
||||||
* copies the item into the specified item.
|
|
||||||
*
|
|
||||||
* @param {Project|Layer|Group|CompoundPath} item the item or project to
|
|
||||||
* copy the item to
|
|
||||||
* @return {Item} the new copy of the item
|
|
||||||
*/
|
|
||||||
copyTo: function(itemOrProject) {
|
|
||||||
var copy = this.clone();
|
|
||||||
if (itemOrProject.layers) {
|
|
||||||
itemOrProject.activeLayer.addChild(copy);
|
|
||||||
} else {
|
|
||||||
itemOrProject.addChild(copy);
|
|
||||||
}
|
|
||||||
return copy;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clones the item within the same project and places the copy above the
|
|
||||||
* item.
|
|
||||||
*
|
|
||||||
* @return {Item} the newly cloned item
|
|
||||||
*
|
|
||||||
* @example {@paperscript}
|
|
||||||
* // Cloning items:
|
|
||||||
* var circle = new Path.Circle(new Point(50, 50), 10);
|
|
||||||
* circle.fillColor = 'red';
|
|
||||||
*
|
|
||||||
* // Make 20 copies of the circle:
|
|
||||||
* for (var i = 0; i < 20; i++) {
|
|
||||||
* var copy = circle.clone();
|
|
||||||
*
|
|
||||||
* // Distribute the copies horizontally, so we can see them:
|
|
||||||
* copy.position.x += i * copy.bounds.width;
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
clone: function() {
|
|
||||||
return this._clone(new this.constructor());
|
|
||||||
},
|
|
||||||
|
|
||||||
_clone: function(copy) {
|
|
||||||
// Copy over style
|
|
||||||
copy.setStyle(this._style);
|
|
||||||
// If this item has children, clone and append each of them:
|
|
||||||
if (this._children) {
|
|
||||||
for (var i = 0, l = this._children.length; i < l; i++)
|
|
||||||
copy.addChild(this._children[i].clone());
|
|
||||||
}
|
|
||||||
// Only copy over these fields if they are actually defined in 'this'
|
|
||||||
// TODO: Consider moving this to Base once it's useful in more than one
|
|
||||||
// place
|
|
||||||
var keys = ['locked', 'visible', 'opacity', 'blendMode', '_clipMask',
|
|
||||||
'_selected'];
|
|
||||||
for (var i = 0, l = keys.length; i < l; i++) {
|
|
||||||
var key = keys[i];
|
|
||||||
if (this.hasOwnProperty(key))
|
|
||||||
copy[key] = this[key];
|
|
||||||
}
|
|
||||||
// Insert the clone above the original, at the same position.
|
|
||||||
copy.insertAbove(this);
|
|
||||||
// Only set name once the copy is moved, to avoid setting and unsettting
|
|
||||||
// name related structures.
|
|
||||||
if (this._name)
|
|
||||||
copy.setName(this._name);
|
|
||||||
return copy;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reverses the order of the item's children
|
* Reverses the order of the item's children
|
||||||
*/
|
*/
|
||||||
|
@ -654,59 +795,10 @@ var Item = this.Item = Base.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Rasterizes the item into a newly created Raster object. The item itself
|
|
||||||
* is not removed after rasterization.
|
|
||||||
*
|
|
||||||
* @param {Number} [resolution=72] the resolution of the raster in dpi
|
|
||||||
* @return {Raster} the newly created raster item
|
|
||||||
*
|
|
||||||
* @example {@paperscript}
|
|
||||||
* // Rasterizing an item:
|
|
||||||
* var circle = new Path.Circle(new Point(80, 50), 5);
|
|
||||||
* circle.fillColor = 'red';
|
|
||||||
*
|
|
||||||
* // Create a rasterized version of the path:
|
|
||||||
* var raster = circle.rasterize();
|
|
||||||
*
|
|
||||||
* // Move it 100pt to the right:
|
|
||||||
* raster.position.x += 100;
|
|
||||||
*
|
|
||||||
* // Scale the path and the raster by 300%, so we can compare them:
|
|
||||||
* circle.scale(5);
|
|
||||||
* raster.scale(5);
|
|
||||||
*/
|
|
||||||
rasterize: function(resolution) {
|
|
||||||
// TODO: why would we want to pass a size to rasterize? Seems to produce
|
|
||||||
// weird results on Scriptographer. Also we can't use antialiasing, since
|
|
||||||
// Canvas doesn't support it yet. Project colorMode is also out of the
|
|
||||||
// question for now.
|
|
||||||
var bounds = this.getStrokeBounds(),
|
|
||||||
scale = (resolution || 72) / 72,
|
|
||||||
canvas = CanvasProvider.getCanvas(bounds.getSize().multiply(scale)),
|
|
||||||
ctx = canvas.getContext('2d'),
|
|
||||||
matrix = new Matrix().scale(scale).translate(-bounds.x, -bounds.y);
|
|
||||||
matrix.applyToContext(ctx);
|
|
||||||
this.draw(ctx, {});
|
|
||||||
var raster = new Raster(canvas);
|
|
||||||
raster.setPosition(this.getPosition());
|
|
||||||
raster.scale(1 / scale);
|
|
||||||
return raster;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@grouptitle Tests}
|
|
||||||
* Checks if the item contains any children items.
|
|
||||||
*
|
|
||||||
* @return {Boolean} {@true it has one or more children}
|
|
||||||
*/
|
|
||||||
hasChildren: function() {
|
|
||||||
return this._children && this._children.length > 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
// TODO: Item#isEditable is currently ignored in the documentation, as
|
// TODO: Item#isEditable is currently ignored in the documentation, as
|
||||||
// locking an item currently has no effect
|
// locking an item currently has no effect
|
||||||
/**
|
/**
|
||||||
|
* {@grouptitle Tests}
|
||||||
* Checks whether the item is editable.
|
* Checks whether the item is editable.
|
||||||
*
|
*
|
||||||
* @return {Boolean} {@true when neither the item, nor its parents are
|
* @return {Boolean} {@true when neither the item, nor its parents are
|
||||||
|
@ -756,6 +848,17 @@ var Item = this.Item = Base.extend({
|
||||||
return 0;
|
return 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@grouptitle Hierarchy Tests}
|
||||||
|
*
|
||||||
|
* Checks if the item contains any children items.
|
||||||
|
*
|
||||||
|
* @return {Boolean} {@true it has one or more children}
|
||||||
|
*/
|
||||||
|
hasChildren: function() {
|
||||||
|
return this._children && this._children.length > 0;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if this item is above the specified item in the stacking order
|
* Checks if this item is above the specified item in the stacking order
|
||||||
* of the project.
|
* of the project.
|
||||||
|
@ -779,7 +882,6 @@ var Item = this.Item = Base.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@grouptitle Hierarchy Tests}
|
|
||||||
* Checks whether the specified item is the parent of the item.
|
* Checks whether the specified item is the parent of the item.
|
||||||
*
|
*
|
||||||
* @param {Item} item The item to check against
|
* @param {Item} item The item to check against
|
||||||
|
@ -1372,122 +1474,6 @@ var Item = this.Item = Base.extend({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, new function() {
|
}, new function() {
|
||||||
|
|
||||||
function move(above) {
|
|
||||||
return function(item) {
|
|
||||||
// first remove the item from its parent's children list
|
|
||||||
if (item._parent && this._removeFromParent()) {
|
|
||||||
Base.splice(item._parent._children, [this],
|
|
||||||
item._index + (above ? 1 : -1), 0);
|
|
||||||
this._parent = item._parent;
|
|
||||||
this._setProject(item._project);
|
|
||||||
if (item._name)
|
|
||||||
item.setName(item._name);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
/** @lends Item# */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@grouptitle Hierarchy Operations}
|
|
||||||
* Adds the specified item as a child of the item at the end of the
|
|
||||||
* its children list. You can use this function for groups, compound
|
|
||||||
* paths and layers.
|
|
||||||
*
|
|
||||||
* @param {Item} item The item that will be added as a child
|
|
||||||
*/
|
|
||||||
addChild: function(item) {
|
|
||||||
return this.insertChild(undefined, item);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inserts the specified item as a child of the item at the specified
|
|
||||||
* index in its {@link #children} list. You can use this function for
|
|
||||||
* groups, compound paths and layers.
|
|
||||||
*
|
|
||||||
* @param {Number} index
|
|
||||||
* @param {Item} item The item that will be appended as a child
|
|
||||||
*/
|
|
||||||
insertChild: function(index, item) {
|
|
||||||
if (this._children) {
|
|
||||||
item._removeFromParent();
|
|
||||||
Base.splice(this._children, [item], index, 0);
|
|
||||||
item._parent = this;
|
|
||||||
item._setProject(this._project);
|
|
||||||
if (item._name)
|
|
||||||
item.setName(item._name);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inserts this item above the specified item.
|
|
||||||
*
|
|
||||||
* @function
|
|
||||||
* @param {Item} item The item above which it should be moved
|
|
||||||
* @return {Boolean} {@true it was inserted}
|
|
||||||
*/
|
|
||||||
insertAbove: move(true),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inserts this item below the specified item.
|
|
||||||
*
|
|
||||||
* @function
|
|
||||||
* @param {Item} item The item above which it should be moved
|
|
||||||
* @return {Boolean} {@true it was inserted}
|
|
||||||
*/
|
|
||||||
insertBelow: move(false),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inserts the specified item as a child of the item by appending it to
|
|
||||||
* the list of children and moving it above all other children. You can
|
|
||||||
* use this function for groups, compound paths and layers.
|
|
||||||
*
|
|
||||||
* @param {Item} item The item that will be appended as a child
|
|
||||||
* @deprecated use {@link #addChild(item)} instead.
|
|
||||||
*/
|
|
||||||
appendTop: function(item) {
|
|
||||||
return this.addChild(item);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inserts the specified item as a child of this item by appending it to
|
|
||||||
* the list of children and moving it below all other children. You can
|
|
||||||
* use this function for groups, compound paths and layers.
|
|
||||||
*
|
|
||||||
* @param {Item} item The item that will be appended as a child
|
|
||||||
* @deprecated use {@link #insertChild(index, item)} instead.
|
|
||||||
*/
|
|
||||||
appendBottom: function(item) {
|
|
||||||
return this.insertChild(0, item);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Moves this item above the specified item.
|
|
||||||
*
|
|
||||||
* @function
|
|
||||||
* @param {Item} item The item above which it should be moved
|
|
||||||
* @return {Boolean} {@true it was moved}
|
|
||||||
* @deprecated use {@link #insertAbove(item)} instead.
|
|
||||||
*/
|
|
||||||
moveAbove: move(true),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Moves the item below the specified item.
|
|
||||||
*
|
|
||||||
* @function
|
|
||||||
* @param {Item} item the item below which it should be moved
|
|
||||||
* @return {Boolean} {@true it was moved}
|
|
||||||
* @deprecated use {@link #insertBelow(item)} instead.
|
|
||||||
*/
|
|
||||||
moveBelow: move(false)
|
|
||||||
};
|
|
||||||
}, new function() {
|
|
||||||
/**
|
/**
|
||||||
* {@grouptitle Remove On Event}
|
* {@grouptitle Remove On Event}
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in a new issue