konva/src/Node.js

2373 lines
75 KiB
JavaScript
Raw Normal View History

2015-05-04 17:02:16 +08:00
(function(Konva) {
'use strict';
// CONSTANTS
2013-08-11 02:55:52 +08:00
var ABSOLUTE_OPACITY = 'absoluteOpacity',
ABSOLUTE_TRANSFORM = 'absoluteTransform',
ABSOLUTE_SCALE = 'absoluteScale',
CHANGE = 'Change',
2013-08-10 14:00:35 +08:00
CHILDREN = 'children',
DOT = '.',
EMPTY_STRING = '',
GET = 'get',
ID = 'id',
2015-01-30 05:25:49 +08:00
KONVA = 'konva',
LISTENING = 'listening',
MOUSEENTER = 'mouseenter',
MOUSELEAVE = 'mouseleave',
2013-08-10 14:00:35 +08:00
NAME = 'name',
SET = 'set',
SHAPE = 'Shape',
SPACE = ' ',
STAGE = 'stage',
2013-08-10 12:09:06 +08:00
TRANSFORM = 'transform',
UPPER_STAGE = 'Stage',
2013-08-10 14:00:35 +08:00
VISIBLE = 'visible',
CLONE_BLACK_LIST = ['id'],
TRANSFORM_CHANGE_STR = [
2015-01-27 15:07:51 +08:00
'xChange.konva',
'yChange.konva',
'scaleXChange.konva',
'scaleYChange.konva',
'skewXChange.konva',
'skewYChange.konva',
'rotationChange.konva',
'offsetXChange.konva',
'offsetYChange.konva',
'transformsEnabledChange.konva'
].join(SPACE),
SCALE_CHANGE_STR = [
'scaleXChange.konva',
'scaleYChange.konva'
].join(SPACE);
2013-08-10 15:58:53 +08:00
2015-04-08 23:26:43 +08:00
/**
* Node constructor. Nodes are entities that can be transformed, layered,
* and have bound events. The stage, layers, groups, and shapes all extend Node.
* @constructor
* @memberof Konva
* @abstract
* @param {Object} config
* @@nodeParams
*/
Konva.Node = function(config) {
this._init(config);
};
2015-01-27 15:07:51 +08:00
Konva.Util.addMethods(Konva.Node, {
_init: function(config) {
var that = this;
2015-01-27 15:07:51 +08:00
this._id = Konva.idCounter++;
this.eventListeners = {};
this.attrs = {};
this._cache = {};
this._filterUpToDate = false;
this.setAttrs(config);
// event bindings for cache handling
this.on(TRANSFORM_CHANGE_STR, function() {
this._clearCache(TRANSFORM);
that._clearSelfAndDescendantCache(ABSOLUTE_TRANSFORM);
});
this.on(SCALE_CHANGE_STR, function() {
that._clearSelfAndDescendantCache(ABSOLUTE_SCALE);
});
2015-01-27 15:07:51 +08:00
this.on('visibleChange.konva', function() {
that._clearSelfAndDescendantCache(VISIBLE);
});
2015-01-27 15:07:51 +08:00
this.on('listeningChange.konva', function() {
that._clearSelfAndDescendantCache(LISTENING);
});
2015-01-27 15:07:51 +08:00
this.on('opacityChange.konva', function() {
that._clearSelfAndDescendantCache(ABSOLUTE_OPACITY);
});
},
2013-08-10 12:09:06 +08:00
_clearCache: function(attr){
if (attr) {
delete this._cache[attr];
}
else {
2014-02-27 08:49:18 +08:00
this._cache = {};
}
2013-08-10 12:09:06 +08:00
},
2013-08-10 13:31:25 +08:00
_getCache: function(attr, privateGetter){
var cache = this._cache[attr];
2013-08-25 15:34:49 +08:00
2013-08-10 13:31:25 +08:00
// if not cached, we need to set it using the private getter method.
2013-08-10 14:00:35 +08:00
if (cache === undefined) {
this._cache[attr] = privateGetter.call(this);
}
2013-08-10 13:31:25 +08:00
2014-02-27 08:49:18 +08:00
return this._cache[attr];
},
/*
* when the logic for a cached result depends on ancestor propagation, use this
* method to clear self and children cache
*/
_clearSelfAndDescendantCache: function(attr) {
this._clearCache(attr);
if (this.children) {
this.getChildren().each(function(node) {
node._clearSelfAndDescendantCache(attr);
});
}
},
/**
* clear cached canvas
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {Konva.Node}
* @example
* node.clearCache();
*/
2014-01-06 05:21:05 +08:00
clearCache: function() {
delete this._cache.canvas;
2014-01-06 05:21:05 +08:00
this._filterUpToDate = false;
return this;
},
/**
2015-02-15 08:18:09 +08:00
* cache node to improve drawing performance, apply filters, or create more accurate
* hit regions. For all basic shapes size of cache canvas will be automatically detected.
* If you need to cache your custom `Konva.Shape` instance you have to pass shape's bounding box
* properties. Look at [link to demo page](link to demo page) for more information.
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
2015-02-15 08:18:09 +08:00
* @param {Object} [config]
2014-01-06 05:21:05 +08:00
* @param {Number} [config.x]
* @param {Number} [config.y]
* @param {Number} [config.width]
* @param {Number} [config.height]
2015-02-15 08:18:09 +08:00
* @param {Number} [config.offset] increase canvas size by `offset` pixel in all directions.
2014-02-24 22:33:25 +08:00
* @param {Boolean} [config.drawBorder] when set to true, a red border will be drawn around the cached
2014-01-06 05:21:05 +08:00
* region for debugging purposes
2015-01-27 15:07:51 +08:00
* @returns {Konva.Node}
* @example
2014-04-04 11:17:09 +08:00
* // cache a shape with the x,y position of the bounding box at the center and
* // the width and height of the bounding box equal to the width and height of
* // the shape obtained from shape.width() and shape.height()
* image.cache();
2014-01-06 06:55:35 +08:00
*
2014-04-04 11:17:09 +08:00
* // cache a node and define the bounding box position and size
* node.cache({
* x: -30,
* y: -30,
* width: 100,
* height: 200
* });
2014-01-06 06:55:35 +08:00
*
2014-04-04 11:17:09 +08:00
* // cache a node and draw a red border around the bounding box
* // for debugging purposes
* node.cache({
* x: -30,
* y: -30,
* width: 100,
* height: 200,
* offset : 10,
2014-04-04 11:17:09 +08:00
* drawBorder: true
2014-01-06 06:55:35 +08:00
* });
*/
2014-01-06 05:21:05 +08:00
cache: function(config) {
var conf = config || {},
rect = this.getClientRect(true),
width = conf.width || rect.width,
height = conf.height || rect.height,
x = conf.x || rect.x,
y = conf.y || rect.y,
offset = conf.offset || 0,
drawBorder = conf.drawBorder || false;
if (!width || !height) {
throw new Error('Width or height of caching configuration equals 0.');
2014-03-12 00:01:58 +08:00
}
width += offset * 2;
height += offset * 2;
x -= offset;
y -= offset;
2015-01-27 15:07:51 +08:00
var cachedSceneCanvas = new Konva.SceneCanvas({
width: width,
height: height
}),
cachedFilterCanvas = new Konva.SceneCanvas({
width: width,
height: height
}),
cachedHitCanvas = new Konva.HitCanvas({
2015-05-04 17:02:16 +08:00
pixelRatio: 1,
width: width,
height: height
}),
sceneContext = cachedSceneCanvas.getContext(),
hitContext = cachedHitCanvas.getContext();
2014-01-06 05:21:05 +08:00
cachedHitCanvas.isCache = true;
2014-01-06 05:21:05 +08:00
this.clearCache();
2015-05-04 17:02:16 +08:00
sceneContext.save();
hitContext.save();
sceneContext.translate(-x, -y);
hitContext.translate(-x, -y);
this.drawScene(cachedSceneCanvas, this, true);
this.drawHit(cachedHitCanvas, this, true);
sceneContext.restore();
hitContext.restore();
2014-01-06 05:21:05 +08:00
// this will draw a red border around the cached box for
// debugging purposes
if (drawBorder) {
2014-01-06 05:21:05 +08:00
sceneContext.save();
sceneContext.beginPath();
sceneContext.rect(0, 0, width, height);
sceneContext.closePath();
sceneContext.setAttr('strokeStyle', 'red');
sceneContext.setAttr('lineWidth', 5);
sceneContext.stroke();
sceneContext.restore();
}
this._cache.canvas = {
scene: cachedSceneCanvas,
filter: cachedFilterCanvas,
hit: cachedHitCanvas,
2015-05-04 17:02:16 +08:00
x: x,
y: y
};
return this;
},
/**
* Return client rectangle {x, y, width, height} of node. This rectangle also include all styling (strokes, shadows, etc).
* The rectangle position is relative to parent container.
* @method
* @memberof Konva.Node.prototype
2015-02-15 08:18:09 +08:00
* @param {Boolean} [skipTransform] flag should we skip transformation to rectangle
* @returns {Object} rect with {x, y, width, height} properties
* @example
2015-02-15 08:18:09 +08:00
* var rect = new Konva.Rect({
* width : 100,
* height : 100,
* x : 50,
* y : 50,
* strokeWidth : 4,
* stroke : 'black',
* offsetX : 50,
* scaleY : 2
* });
*
2015-02-15 08:18:09 +08:00
* // get client rect without think off transformations (position, rotation, scale, offset, etc)
* rect.getClientRect(true);
* // returns {
* // x : -2, // two pixels for stroke / 2
* // y : -2,
* // width : 104, // increased by 4 for stroke
* // height : 104
* //}
*
* // get client rect with transformation applied
* rect.getClientRect();
* // returns Object {x: -2, y: 46, width: 104, height: 208}
*/
2015-05-04 17:02:16 +08:00
getClientRect: function() {
// abstract method
// redefine in Container and Shape
2015-10-22 13:32:07 +08:00
throw new Error('abstract "getClientRect" method call');
},
2015-05-04 17:02:16 +08:00
_transformedRect: function(rect) {
var points = [
2015-05-04 17:02:16 +08:00
{x: rect.x, y: rect.y},
{x: rect.x + rect.width, y: rect.y},
{x: rect.x + rect.width, y: rect.y + rect.height},
{x: rect.x, y: rect.y + rect.height}
];
var minX, minY, maxX, maxY;
var trans = this.getTransform();
points.forEach(function(point) {
var transformed = trans.point(point);
if (minX === undefined) {
minX = maxX = transformed.x;
minY = maxY = transformed.y;
}
minX = Math.min(minX, transformed.x);
minY = Math.min(minY, transformed.y);
maxX = Math.max(maxX, transformed.x);
maxY = Math.max(maxY, transformed.y);
});
return {
2016-02-28 16:57:15 +08:00
x: minX,
y: minY,
width: maxX - minX,
height: maxY - minY
};
},
_drawCachedSceneCanvas: function(context) {
context.save();
2015-02-14 23:12:54 +08:00
context._applyOpacity(this);
context.translate(
this._cache.canvas.x,
this._cache.canvas.y
);
2015-02-14 23:12:54 +08:00
var cacheCanvas = this._getCachedSceneCanvas();
2015-04-16 22:49:08 +08:00
var ratio = cacheCanvas.pixelRatio;
2015-02-14 23:12:54 +08:00
2015-04-16 22:49:08 +08:00
context.drawImage(cacheCanvas._canvas, 0, 0, cacheCanvas.width / ratio, cacheCanvas.height / ratio);
context.restore();
},
_drawCachedHitCanvas: function(context) {
var cachedCanvas = this._cache.canvas,
hitCanvas = cachedCanvas.hit;
context.save();
context.translate(
this._cache.canvas.x,
this._cache.canvas.y
);
context.drawImage(hitCanvas._canvas, 0, 0);
context.restore();
},
_getCachedSceneCanvas: function() {
var filters = this.filters(),
cachedCanvas = this._cache.canvas,
sceneCanvas = cachedCanvas.scene,
filterCanvas = cachedCanvas.filter,
filterContext = filterCanvas.getContext(),
len, imageData, n, filter;
if (filters) {
if (!this._filterUpToDate) {
var ratio = sceneCanvas.pixelRatio;
2015-05-04 17:02:16 +08:00
try {
len = filters.length;
filterContext.clear();
// copy cached canvas onto filter context
filterContext.drawImage(sceneCanvas._canvas, 0, 0, sceneCanvas.getWidth() / ratio, sceneCanvas.getHeight() / ratio);
imageData = filterContext.getImageData(0, 0, filterCanvas.getWidth(), filterCanvas.getHeight());
// apply filters to filter context
2015-05-04 17:02:16 +08:00
for (n = 0; n < len; n++) {
filter = filters[n];
filter.call(this, imageData);
filterContext.putImageData(imageData, 0, 0);
}
}
catch(e) {
2015-01-27 15:07:51 +08:00
Konva.Util.warn('Unable to apply filter. ' + e.message);
}
this._filterUpToDate = true;
}
return filterCanvas;
}
2015-10-22 13:32:07 +08:00
return sceneCanvas;
},
/**
2015-01-27 15:07:51 +08:00
* bind events to the node. KonvaJS supports mouseover, mousemove,
* mouseout, mouseenter, mouseleave, mousedown, mouseup, wheel, click, dblclick, touchstart, touchmove,
2015-01-27 15:07:51 +08:00
* touchend, tap, dbltap, dragstart, dragmove, and dragend events. The Konva Stage supports
* contentMouseover, contentMousemove, contentMouseout, contentMousedown, contentMouseup, contentWheel
* contentClick, contentDblclick, contentTouchstart, contentTouchmove, contentTouchend, contentTap,
* and contentDblTap. Pass in a string of events delimmited by a space to bind multiple events at once
2012-11-27 11:12:02 +08:00
* such as 'mousedown mouseup mousemove'. Include a namespace to bind an
* event by name such as 'click.foobar'.
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @param {String} evtStr e.g. 'click', 'mousedown touchstart', 'mousedown.foo touchstart.foo'
2012-11-27 11:12:02 +08:00
* @param {Function} handler The handler function is passed an event object
2015-01-27 15:07:51 +08:00
* @returns {Konva.Node}
* @example
2014-04-04 11:17:09 +08:00
* // add click listener
* node.on('click', function() {
* console.log('you clicked me!');
* });
*
2014-04-04 11:17:09 +08:00
* // get the target node
* node.on('click', function(evt) {
* console.log(evt.target);
* });
*
2014-04-04 11:17:09 +08:00
* // stop event propagation
* node.on('click', function(evt) {
* evt.cancelBubble = true;
* });
*
2014-04-04 11:17:09 +08:00
* // bind multiple listeners
* node.on('click touchstart', function() {
* console.log('you clicked/touched me!');
* });
*
2014-04-04 11:17:09 +08:00
* // namespace listener
* node.on('click.foo', function() {
* console.log('you clicked/touched me!');
* });
*
2014-04-04 11:17:09 +08:00
* // get the event type
* node.on('click tap', function(evt) {
* var eventType = evt.type;
* });
*
2014-04-04 11:17:09 +08:00
* // get native event object
* node.on('click tap', function(evt) {
* var nativeEvent = evt.evt;
* });
*
2014-04-04 11:17:09 +08:00
* // for change events, get the old and new val
* node.on('xChange', function(evt) {
* var oldVal = evt.oldVal;
* var newVal = evt.newVal;
2013-05-16 15:28:49 +08:00
* });
2015-11-22 11:44:33 +08:00
*
2015-11-22 23:26:04 +08:00
* // get event targets
* // with event delegations
2015-11-22 11:44:33 +08:00
* layer.on('click', 'Group', function(evt) {
* var shape = evt.target;
* var group = evtn.currentTarger;
* });
*/
on: function(evtStr, handler) {
2015-11-22 11:44:33 +08:00
if (arguments.length === 3) {
return this._delegate.apply(this, arguments);
}
var events = evtStr.split(SPACE),
len = events.length,
n, event, parts, baseEvent, name;
/*
* loop through types and attach event listeners to
* each one. eg. 'click mouseover.namespace mouseout'
* will create three event bindings
*/
for(n = 0; n < len; n++) {
event = events[n];
parts = event.split(DOT);
baseEvent = parts[0];
name = parts[1] || EMPTY_STRING;
// create events array if it doesn't exist
if(!this.eventListeners[baseEvent]) {
this.eventListeners[baseEvent] = [];
}
this.eventListeners[baseEvent].push({
name: name,
handler: handler
});
}
return this;
},
/**
2012-11-27 11:12:02 +08:00
* remove event bindings from the node. Pass in a string of
* event types delimmited by a space to remove multiple event
* bindings at once such as 'mousedown mouseup mousemove'.
* include a namespace to remove an event binding by name
* such as 'click.foobar'. If you only give a name like '.foobar',
* all events in that namespace will be removed.
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @param {String} evtStr e.g. 'click', 'mousedown touchstart', '.foobar'
2015-01-27 15:07:51 +08:00
* @returns {Konva.Node}
* @example
2014-04-04 11:17:09 +08:00
* // remove listener
* node.off('click');
*
2014-04-04 11:17:09 +08:00
* // remove multiple listeners
* node.off('click touchstart');
*
2014-04-04 11:17:09 +08:00
* // remove listener by name
* node.off('click.foo');
*/
off: function(evtStr) {
2014-05-15 23:44:14 +08:00
var events = (evtStr || '').split(SPACE),
len = events.length,
n, t, event, parts, baseEvent, name;
2014-05-15 23:44:14 +08:00
if (!evtStr) {
// remove all events
for(t in this.eventListeners) {
this._off(t);
}
}
for(n = 0; n < len; n++) {
event = events[n];
parts = event.split(DOT);
baseEvent = parts[0];
name = parts[1];
if(baseEvent) {
if(this.eventListeners[baseEvent]) {
this._off(baseEvent, name);
}
}
else {
for(t in this.eventListeners) {
this._off(t, name);
}
}
}
return this;
},
// some event aliases for third party integration like HammerJS
2014-02-21 09:03:21 +08:00
dispatchEvent: function(evt) {
var e = {
target: this,
type: evt.type,
evt: evt
};
this.fire(evt.type, e);
return this;
2014-02-21 09:03:21 +08:00
},
addEventListener: function(type, handler) {
// we have to pass native event to handler
this.on(type, function(evt){
handler.call(this, evt.evt);
});
return this;
2014-02-21 09:03:21 +08:00
},
2015-05-04 17:02:16 +08:00
removeEventListener: function(type) {
this.off(type);
return this;
},
2015-11-22 23:26:04 +08:00
// like node.on
2015-11-22 11:44:33 +08:00
_delegate: function(event, selector, handler) {
var stopNode = this;
this.on(event, function(evt) {
var targets = evt.target.findAncestors(selector, true, stopNode);
2015-11-22 11:44:33 +08:00
for(var i = 0; i < targets.length; i++) {
evt = Konva.Util.cloneObject(evt);
evt.currentTarget = targets[i];
handler.call(targets[i], evt);
}
});
},
/**
* remove self from parent, but don't destroy
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {Konva.Node}
* @example
* node.remove();
*/
remove: function() {
var parent = this.getParent();
if(parent && parent.children) {
parent.children.splice(this.index, 1);
parent._setChildrenIndices();
delete this.parent;
}
// every cached attr that is calculated via node tree
// traversal must be cleared when removing a node
this._clearSelfAndDescendantCache(STAGE);
this._clearSelfAndDescendantCache(ABSOLUTE_TRANSFORM);
this._clearSelfAndDescendantCache(VISIBLE);
this._clearSelfAndDescendantCache(LISTENING);
this._clearSelfAndDescendantCache(ABSOLUTE_OPACITY);
return this;
},
/**
* remove and destroy self
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @example
* node.destroy();
*/
destroy: function() {
// remove from ids and names hashes
2015-01-27 15:07:51 +08:00
Konva._removeId(this.getId());
Konva._removeName(this.getName(), this._id);
this.remove();
return this;
},
/**
* get attr
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @param {String} attr
* @returns {Integer|String|Object|Array}
* @example
* var x = node.getAttr('x');
*/
getAttr: function(attr) {
2015-01-27 15:07:51 +08:00
var method = GET + Konva.Util._capitalize(attr);
if(Konva.Util._isFunction(this[method])) {
return this[method]();
}
// otherwise get directly
2015-10-22 13:32:07 +08:00
return this.attrs[attr];
},
2013-08-10 15:58:53 +08:00
/**
* get ancestors
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {Konva.Collection}
2013-08-10 15:58:53 +08:00
* @example
* shape.getAncestors().each(function(node) {
* console.log(node.getId());
* })
*/
getAncestors: function() {
var parent = this.getParent(),
2015-01-27 15:07:51 +08:00
ancestors = new Konva.Collection();
2013-08-10 15:58:53 +08:00
while (parent) {
ancestors.push(parent);
parent = parent.getParent();
}
return ancestors;
},
/**
* get attrs object literal
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {Object}
*/
getAttrs: function() {
return this.attrs || {};
},
/**
* set multiple attrs at once using an object literal
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
2012-11-27 11:12:02 +08:00
* @param {Object} config object containing key value pairs
2015-01-27 15:07:51 +08:00
* @returns {Konva.Node}
* @example
2014-04-04 11:17:09 +08:00
* node.setAttrs({
* x: 5,
* fill: 'red'
* });
*/
setAttrs: function(config) {
var key, method;
2015-05-04 17:02:16 +08:00
if(!config) {
return this;
}
for(key in config) {
if (key === CHILDREN) {
continue;
}
method = SET + Konva.Util._capitalize(key);
// use setter if available
if(Konva.Util._isFunction(this[method])) {
this[method](config[key]);
}
// otherwise set directly
else {
this._setAttr(key, config[key]);
}
}
return this;
},
/**
* determine if node is listening for events by taking into account ancestors.
*
* Parent | Self | isListening
2015-05-04 17:02:16 +08:00
* listening | listening |
* ----------+-----------+------------
2015-05-04 17:02:16 +08:00
* T | T | T
* T | F | F
2015-05-04 17:02:16 +08:00
* F | T | T
* F | F | F
* ----------+-----------+------------
* T | I | T
* F | I | F
* I | I | T
*
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {Boolean}
*/
isListening: function() {
return this._getCache(LISTENING, this._isListening);
},
_isListening: function() {
var listening = this.getListening(),
parent = this.getParent();
// the following conditions are a simplification of the truth table above.
// please modify carefully
if (listening === 'inherit') {
if (parent) {
return parent.isListening();
}
else {
return true;
}
}
else {
return listening;
}
},
/**
* determine if node is visible by taking into account ancestors.
*
* Parent | Self | isVisible
2015-05-04 17:02:16 +08:00
* visible | visible |
* ----------+-----------+------------
2015-05-04 17:02:16 +08:00
* T | T | T
* T | F | F
2015-05-04 17:02:16 +08:00
* F | T | T
* F | F | F
* ----------+-----------+------------
* T | I | T
* F | I | F
* I | I | T
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {Boolean}
*/
isVisible: function() {
return this._getCache(VISIBLE, this._isVisible);
2013-08-10 14:00:35 +08:00
},
_isVisible: function() {
var visible = this.getVisible(),
parent = this.getParent();
// the following conditions are a simplification of the truth table above.
// please modify carefully
if (visible === 'inherit') {
if (parent) {
return parent.isVisible();
}
else {
return true;
}
}
else {
return visible;
}
},
/**
* determine if listening is enabled by taking into account descendants. If self or any children
* have _isListeningEnabled set to true, then self also has listening enabled.
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {Boolean}
*/
shouldDrawHit: function(canvas) {
var layer = this.getLayer();
2015-05-04 17:02:16 +08:00
return (canvas && canvas.isCache) || (layer && layer.hitGraphEnabled())
&& this.isListening() && this.isVisible();
},
/**
* show node
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {Konva.Node}
*/
show: function() {
this.setVisible(true);
return this;
},
/**
* hide node. Hidden nodes are no longer detectable
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {Konva.Node}
*/
hide: function() {
this.setVisible(false);
return this;
},
/**
2012-11-27 11:12:02 +08:00
* get zIndex relative to the node's siblings who share the same parent
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {Integer}
*/
getZIndex: function() {
return this.index || 0;
},
/**
* get absolute z-index which takes into account sibling
2012-11-27 11:12:02 +08:00
* and ancestor indices
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {Integer}
*/
getAbsoluteZIndex: function() {
2014-01-03 15:03:27 +08:00
var depth = this.getDepth(),
that = this,
index = 0,
nodes, len, n, child;
function addChildren(children) {
nodes = [];
len = children.length;
for(n = 0; n < len; n++) {
child = children[n];
index++;
if(child.nodeType !== SHAPE) {
nodes = nodes.concat(child.getChildren().toArray());
}
if(child._id === that._id) {
n = len;
}
}
2014-01-03 15:03:27 +08:00
if(nodes.length > 0 && nodes[0].getDepth() <= depth) {
addChildren(nodes);
}
}
if(that.nodeType !== UPPER_STAGE) {
addChildren(that.getStage().getChildren());
}
return index;
},
/**
2014-04-04 11:17:09 +08:00
* get node depth in node tree. Returns an integer.
2014-01-03 15:03:27 +08:00
* e.g. Stage depth will always be 0. Layers will always be 1. Groups and Shapes will always
2012-11-27 11:12:02 +08:00
* be >= 2
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {Integer}
*/
2014-01-03 15:03:27 +08:00
getDepth: function() {
var depth = 0,
parent = this.parent;
while(parent) {
2014-01-03 15:03:27 +08:00
depth++;
parent = parent.parent;
}
2014-01-03 15:03:27 +08:00
return depth;
},
2013-12-02 15:47:24 +08:00
setPosition: function(pos) {
this.setX(pos.x);
this.setY(pos.y);
return this;
},
getPosition: function() {
return {
x: this.getX(),
y: this.getY()
};
},
/**
2012-11-27 11:12:02 +08:00
* get absolute position relative to the top left corner of the stage container div
* or relative to passed node
* @method
* @param {Object} [top] optional parent node
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {Object}
*/
getAbsolutePosition: function(top) {
var absoluteMatrix = this.getAbsoluteTransform(top).getMatrix(),
2015-01-27 15:07:51 +08:00
absoluteTransform = new Konva.Transform(),
offset = this.offset();
2013-08-25 15:34:49 +08:00
// clone the matrix array
absoluteTransform.m = absoluteMatrix.slice();
absoluteTransform.translate(offset.x, offset.y);
2013-08-25 15:34:49 +08:00
2013-08-12 18:00:08 +08:00
return absoluteTransform.getTranslation();
},
/**
* set absolute position
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
2013-12-02 15:47:24 +08:00
* @param {Object} pos
* @param {Number} pos.x
* @param {Number} pos.y
2015-01-27 15:07:51 +08:00
* @returns {Konva.Node}
*/
2013-12-02 15:47:24 +08:00
setAbsolutePosition: function(pos) {
var origTrans = this._clearTransform(),
it;
// don't clear translation
this.attrs.x = origTrans.x;
this.attrs.y = origTrans.y;
delete origTrans.x;
delete origTrans.y;
// unravel transform
it = this.getAbsoluteTransform();
it.invert();
it.translate(pos.x, pos.y);
pos = {
x: this.attrs.x + it.getTranslation().x,
y: this.attrs.y + it.getTranslation().y
};
2015-05-04 17:02:16 +08:00
this.setPosition({x: pos.x, y: pos.y});
this._setTransform(origTrans);
return this;
},
2013-08-12 18:00:08 +08:00
_setTransform: function(trans) {
var key;
for(key in trans) {
this.attrs[key] = trans[key];
}
this._clearCache(TRANSFORM);
this._clearSelfAndDescendantCache(ABSOLUTE_TRANSFORM);
2013-08-12 18:00:08 +08:00
},
_clearTransform: function() {
var trans = {
x: this.getX(),
y: this.getY(),
rotation: this.getRotation(),
scaleX: this.getScaleX(),
scaleY: this.getScaleY(),
offsetX: this.getOffsetX(),
offsetY: this.getOffsetY(),
2013-08-12 18:00:08 +08:00
skewX: this.getSkewX(),
skewY: this.getSkewY()
};
this.attrs.x = 0;
this.attrs.y = 0;
this.attrs.rotation = 0;
this.attrs.scaleX = 1;
this.attrs.scaleY = 1;
this.attrs.offsetX = 0;
this.attrs.offsetY = 0;
2013-08-12 18:00:08 +08:00
this.attrs.skewX = 0;
this.attrs.skewY = 0;
this._clearCache(TRANSFORM);
this._clearSelfAndDescendantCache(ABSOLUTE_TRANSFORM);
2013-08-12 18:00:08 +08:00
// return original transform
2013-08-12 18:00:08 +08:00
return trans;
},
/**
2012-11-27 11:12:02 +08:00
* move node by an amount relative to its current position
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
2013-12-02 15:47:24 +08:00
* @param {Object} change
* @param {Number} change.x
* @param {Number} change.y
2015-01-27 15:07:51 +08:00
* @returns {Konva.Node}
* @example
2014-04-04 11:17:09 +08:00
* // move node in x direction by 1px and y direction by 2px
* node.move({
* x: 1,
* y: 2)
2013-05-18 06:09:57 +08:00
* });
*/
2013-12-02 15:47:24 +08:00
move: function(change) {
var changeX = change.x,
changeY = change.y,
x = this.getX(),
y = this.getY();
2013-12-02 15:47:24 +08:00
if(changeX !== undefined) {
x += changeX;
}
2013-12-02 15:47:24 +08:00
if(changeY !== undefined) {
y += changeY;
}
2015-05-04 17:02:16 +08:00
this.setPosition({x: x, y: y});
return this;
},
_eachAncestorReverse: function(func, top) {
var family = [],
parent = this.getParent(),
len, n;
// if top node is defined, and this node is top node,
// there's no need to build a family tree. just execute
// func with this because it will be the only node
if (top && top._id === this._id) {
func(this);
return true;
2012-12-31 17:47:49 +08:00
}
family.unshift(this);
while(parent && (!top || parent._id !== top._id)) {
family.unshift(parent);
parent = parent.parent;
}
len = family.length;
for(n = 0; n < len; n++) {
func(family[n]);
}
},
/**
* rotate node by an amount in degrees relative to its current rotation
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @param {Number} theta
2015-01-27 15:07:51 +08:00
* @returns {Konva.Node}
*/
rotate: function(theta) {
this.setRotation(this.getRotation() + theta);
return this;
},
/**
* move node to the top of its siblings
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {Boolean}
*/
moveToTop: function() {
if (!this.parent) {
2015-01-27 15:07:51 +08:00
Konva.Util.warn('Node has no parent. moveToTop function is ignored.');
2015-05-04 17:02:16 +08:00
return false;
}
var index = this.index;
this.parent.children.splice(index, 1);
this.parent.children.push(this);
this.parent._setChildrenIndices();
return true;
},
/**
* move node up
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
2015-04-07 16:03:08 +08:00
* @returns {Boolean} flag is moved or not
*/
moveUp: function() {
if (!this.parent) {
2015-01-27 15:07:51 +08:00
Konva.Util.warn('Node has no parent. moveUp function is ignored.');
2015-05-04 17:02:16 +08:00
return false;
}
var index = this.index,
len = this.parent.getChildren().length;
if(index < len - 1) {
this.parent.children.splice(index, 1);
this.parent.children.splice(index + 1, 0, this);
this.parent._setChildrenIndices();
return true;
}
return false;
},
/**
* move node down
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {Boolean}
*/
moveDown: function() {
if (!this.parent) {
2015-01-27 15:07:51 +08:00
Konva.Util.warn('Node has no parent. moveDown function is ignored.');
2015-05-04 17:02:16 +08:00
return false;
}
var index = this.index;
if(index > 0) {
this.parent.children.splice(index, 1);
this.parent.children.splice(index - 1, 0, this);
this.parent._setChildrenIndices();
return true;
}
return false;
},
/**
* move node to the bottom of its siblings
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {Boolean}
*/
moveToBottom: function() {
if (!this.parent) {
2015-01-27 15:07:51 +08:00
Konva.Util.warn('Node has no parent. moveToBottom function is ignored.');
2015-05-04 17:02:16 +08:00
return false;
}
var index = this.index;
if(index > 0) {
this.parent.children.splice(index, 1);
this.parent.children.unshift(this);
this.parent._setChildrenIndices();
return true;
}
return false;
},
/**
2012-11-27 11:12:02 +08:00
* set zIndex relative to siblings
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @param {Integer} zIndex
2015-01-27 15:07:51 +08:00
* @returns {Konva.Node}
*/
setZIndex: function(zIndex) {
if (!this.parent) {
2015-01-27 15:07:51 +08:00
Konva.Util.warn('Node has no parent. zIndex parameter is ignored.');
2015-05-04 17:02:16 +08:00
return false;
}
var index = this.index;
this.parent.children.splice(index, 1);
this.parent.children.splice(zIndex, 0, this);
this.parent._setChildrenIndices();
return this;
},
/**
* get absolute opacity
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {Number}
*/
getAbsoluteOpacity: function() {
2013-08-11 02:55:52 +08:00
return this._getCache(ABSOLUTE_OPACITY, this._getAbsoluteOpacity);
},
_getAbsoluteOpacity: function() {
var absOpacity = this.getOpacity();
if(this.getParent()) {
absOpacity *= this.getParent().getAbsoluteOpacity();
}
return absOpacity;
},
/**
* move node to another container
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @param {Container} newContainer
2015-01-27 15:07:51 +08:00
* @returns {Konva.Node}
* @example
2014-04-04 11:17:09 +08:00
* // move node from current layer into layer2
* node.moveTo(layer2);
*/
moveTo: function(newContainer) {
2014-08-19 19:48:20 +08:00
// do nothing if new container is already parent
if (this.getParent() !== newContainer) {
this.remove();
newContainer.add(this);
}
return this;
},
/**
2012-11-27 11:12:02 +08:00
* convert Node into an object for serialization. Returns an object.
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {Object}
*/
toObject: function() {
2015-04-13 08:17:32 +08:00
var obj = {},
attrs = this.getAttrs(),
2013-11-28 00:47:52 +08:00
key, val, getter, defaultValue;
obj.attrs = {};
for(key in attrs) {
val = attrs[key];
2015-04-13 08:17:32 +08:00
// serialize only attributes that are not function, image, DOM, or objects with methods
if (Konva.Util._isFunction(val) || Konva.Util._isElement(val) ||
(Konva.Util._isObject(val) || Konva.Util._hasMethods(val))) {
continue;
}
getter = this[key];
// remove attr value so that we can extract the default value from the getter
delete attrs[key];
defaultValue = getter ? getter.call(this) : null;
// restore attr value
attrs[key] = val;
if (defaultValue !== val) {
obj.attrs[key] = val;
}
}
obj.className = this.getClassName();
return obj;
},
2012-11-27 11:12:02 +08:00
/**
* convert Node into a JSON string. Returns a JSON string.
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {String}}
2012-11-27 11:12:02 +08:00
*/
toJSON: function() {
return JSON.stringify(this.toObject());
},
/**
* get parent container
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {Konva.Node}
*/
getParent: function() {
return this.parent;
},
/**
* get all ancestros (parent then parent of the parent, etc) of the node
* @method
* @memberof Konva.Node.prototype
* @param {String} [selector] selector for search
* @param {Boolean} [includeSelf] show we think that node is ancestro itself?
* @param {Konva.Node} [stopNode] optional node where we need to stop searching (one of ancestors)
* @returns {Array} [ancestors]
* @example
* // get one of the parent group
* var parentGroups = node.findAncestors('Group');
*/
findAncestors: function(selector, includeSelf, stopNode) {
2015-11-22 11:44:33 +08:00
var res = [];
if (includeSelf && this._isMatch(selector)) {
2015-11-22 11:44:33 +08:00
res.push(this);
}
var ancestor = this.parent;
while(ancestor) {
if (ancestor === stopNode) {
return res;
}
if (ancestor._isMatch(selector)) {
res.push(ancestor);
}
ancestor = ancestor.parent;
2015-11-22 11:44:33 +08:00
}
return res;
},
/**
* get ancestor (parent or parent of the parent, etc) of the node that match passed selector
* @method
* @memberof Konva.Node.prototype
* @param {String} [selector] selector for search
* @param {Boolean} [includeSelf] show we think that node is ancestro itself?
* @param {Konva.Node} [stopNode] optional node where we need to stop searching (one of ancestors)
* @returns {Konva.Node} ancestor
* @example
* // get one of the parent group
* var group = node.findAncestors('.mygroup');
*/
findAncestor: function(selector, includeSelf, stopNode) {
return this.findAncestors(selector, includeSelf, stopNode)[0];
2015-11-22 11:44:33 +08:00
},
2015-11-22 23:26:04 +08:00
// is current node match passed selector?
2015-11-22 11:44:33 +08:00
_isMatch: function(selector) {
if (!selector) {
return false;
}
2015-11-22 11:44:33 +08:00
var selectorArr = selector.replace(/ /g, '').split(','),
len = selectorArr.length,
n, sel;
for (n = 0; n < len; n++) {
sel = selectorArr[n];
if (!Konva.Util.isValidSelector(sel)) {
Konva.Util.warn('Selector "' + sel + '" is invalid. Allowed selectors examples are "#foo", ".bar" or "Group".');
Konva.Util.warn('If you have a custom shape with such className, please change it to start with upper letter like "Triangle".');
Konva.Util.warn('Konva is awesome, right?');
}
// id selector
if(sel.charAt(0) === '#') {
if (this.id() === sel.slice(1)) {
return true;
}
}
// name selector
else if(sel.charAt(0) === '.') {
if (this.hasName(sel.slice(1))) {
return true;
}
} else if (this._get(sel).length !== 0) {
return true;
}
}
return false;
},
/**
2012-11-27 11:12:02 +08:00
* get layer ancestor
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {Konva.Layer}
*/
getLayer: function() {
2013-11-28 00:24:47 +08:00
var parent = this.getParent();
return parent ? parent.getLayer() : null;
},
/**
2012-11-27 11:12:02 +08:00
* get stage ancestor
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {Konva.Stage}
*/
getStage: function() {
return this._getCache(STAGE, this._getStage);
},
_getStage: function() {
var parent = this.getParent();
if(parent) {
return parent.getStage();
}
else {
return undefined;
}
},
/**
* fire event
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @param {String} eventType event type. can be a regular event, like click, mouseover, or mouseout, or it can be a custom event, like myCustomEvent
* @param {Event} [evt] event object
2014-01-13 00:46:18 +08:00
* @param {Boolean} [bubble] setting the value to false, or leaving it undefined, will result in the event
* not bubbling. Setting the value to true will result in the event bubbling.
2015-01-27 15:07:51 +08:00
* @returns {Konva.Node}
* @example
2014-04-04 11:17:09 +08:00
* // manually fire click event
* node.fire('click');
*
2014-04-04 11:17:09 +08:00
* // fire custom event
* node.fire('foo');
*
2014-04-04 11:17:09 +08:00
* // fire custom event with custom event object
* node.fire('foo', {
* bar: 10
* });
*
2014-04-04 11:17:09 +08:00
* // fire click event that bubbles
* node.fire('click', null, true);
*/
fire: function(eventType, evt, bubble) {
2016-01-04 08:24:27 +08:00
evt = evt || {};
evt.target = evt.target || this;
// bubble
if (bubble) {
2015-11-22 11:44:33 +08:00
this._fireAndBubble(eventType, evt);
}
// no bubble
else {
2015-11-22 11:44:33 +08:00
this._fire(eventType, evt);
}
return this;
},
/**
* get absolute transform of the node which takes into
2012-11-27 11:12:02 +08:00
* account its ancestor transforms
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {Konva.Transform}
*/
getAbsoluteTransform: function(top) {
// if using an argument, we can't cache the result.
if (top) {
return this._getAbsoluteTransform(top);
}
// if no argument, we can cache the result
else {
return this._getCache(ABSOLUTE_TRANSFORM, this._getAbsoluteTransform);
}
2013-08-10 15:58:53 +08:00
},
_getAbsoluteTransform: function(top) {
2015-01-27 15:07:51 +08:00
var at = new Konva.Transform(),
transformsEnabled, trans;
// start with stage and traverse downwards to self
2012-12-31 17:47:49 +08:00
this._eachAncestorReverse(function(node) {
transformsEnabled = node.transformsEnabled();
trans = node.getTransform();
2014-02-27 08:49:18 +08:00
if (transformsEnabled === 'all') {
at.multiply(trans);
}
else if (transformsEnabled === 'position') {
at.translate(node.x(), node.y());
}
}, top);
return at;
},
/**
* get absolute scale of the node which takes into
* account its ancestor scales
* @method
* @memberof Konva.Node.prototype
* @returns {Konva.Transform}
*/
getAbsoluteScale: function(top) {
// if using an argument, we can't cache the result.
if (top) {
return this._getAbsoluteTransform(top);
}
// if no argument, we can cache the result
else {
return this._getCache(ABSOLUTE_SCALE, this._getAbsoluteScale);
}
},
_getAbsoluteScale: function(top) {
var scaleX = 1, scaleY = 1;
// start with stage and traverse downwards to self
this._eachAncestorReverse(function(node) {
scaleX *= node.scaleX();
scaleY *= node.scaleY();
}, top);
return {
x: scaleX,
y: scaleY
};
},
/**
* get transform of the node
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {Konva.Transform}
*/
getTransform: function() {
return this._getCache(TRANSFORM, this._getTransform);
},
_getTransform: function() {
2015-01-27 15:07:51 +08:00
var m = new Konva.Transform(),
x = this.getX(),
y = this.getY(),
2015-01-27 15:07:51 +08:00
rotation = Konva.getAngle(this.getRotation()),
scaleX = this.getScaleX(),
scaleY = this.getScaleY(),
skewX = this.getSkewX(),
skewY = this.getSkewY(),
offsetX = this.getOffsetX(),
offsetY = this.getOffsetY();
if(x !== 0 || y !== 0) {
m.translate(x, y);
}
if(rotation !== 0) {
m.rotate(rotation);
}
if(skewX !== 0 || skewY !== 0) {
m.skew(skewX, skewY);
}
if(scaleX !== 1 || scaleY !== 1) {
m.scale(scaleX, scaleY);
}
if(offsetX !== 0 || offsetY !== 0) {
m.translate(-1 * offsetX, -1 * offsetY);
}
return m;
},
/**
* clone node. Returns a new Node instance with identical attributes. You can also override
* the node properties with an object literal, enabling you to use an existing node as a template
* for another node
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @param {Object} obj override attrs
2015-01-27 15:07:51 +08:00
* @returns {Konva.Node}
* @example
2014-04-04 11:17:09 +08:00
* // simple clone
* var clone = node.clone();
*
2014-04-04 11:17:09 +08:00
* // clone a node and override the x position
* var clone = rect.clone({
* x: 5
* });
*/
clone: function(obj) {
// instantiate new node
2015-04-27 12:20:55 +08:00
var attrs = Konva.Util.cloneObject(this.attrs),
key, allListeners, len, n, listener;
// filter black attrs
for (var i in CLONE_BLACK_LIST) {
var blockAttr = CLONE_BLACK_LIST[i];
delete attrs[blockAttr];
}
// apply attr overrides
for (key in obj) {
attrs[key] = obj[key];
}
var node = new this.constructor(attrs);
// copy over listeners
for(key in this.eventListeners) {
allListeners = this.eventListeners[key];
len = allListeners.length;
for(n = 0; n < len; n++) {
listener = allListeners[n];
/*
2015-01-27 15:07:51 +08:00
* don't include konva namespaced listeners because
* these are generated by the constructors
*/
2015-01-30 05:25:49 +08:00
if(listener.name.indexOf(KONVA) < 0) {
// if listeners array doesn't exist, then create it
if(!node.eventListeners[key]) {
node.eventListeners[key] = [];
}
node.eventListeners[key].push(listener);
}
}
}
return node;
},
/**
* Creates a composite data URL. If MIME type is not
* specified, then "image/png" will result. For "image/jpeg", specify a quality
* level as quality (range 0.0 - 1.0)
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @param {Object} config
2012-12-17 12:52:07 +08:00
* @param {String} [config.mimeType] can be "image/png" or "image/jpeg".
* "image/png" is the default
2012-12-17 12:52:07 +08:00
* @param {Number} [config.x] x position of canvas section
* @param {Number} [config.y] y position of canvas section
* @param {Number} [config.width] width of canvas section
* @param {Number} [config.height] height of canvas section
* @param {Number} [config.quality] jpeg quality. If using an "image/jpeg" mimeType,
* you can specify the quality from 0 to 1, where 0 is very poor quality and 1
* is very high quality
2015-04-27 12:20:55 +08:00
* @paremt {Number} [config.pixelRatio] pixelRatio of ouput image url. Default is 1
* @returns {String}
*/
toDataURL: function(config) {
2013-06-02 13:03:02 +08:00
config = config || {};
var mimeType = config.mimeType || null,
quality = config.quality || null,
stage = this.getStage(),
x = config.x || 0,
y = config.y || 0,
2015-04-27 12:20:55 +08:00
pixelRatio = config.pixelRatio || 1,
2015-01-27 15:07:51 +08:00
canvas = new Konva.SceneCanvas({
2013-11-12 19:03:15 +08:00
width: config.width || this.getWidth() || (stage ? stage.getWidth() : 0),
height: config.height || this.getHeight() || (stage ? stage.getHeight() : 0),
2015-04-27 12:20:55 +08:00
pixelRatio: pixelRatio
}),
context = canvas.getContext();
context.save();
if(x || y) {
context.translate(-1 * x, -1 * y);
}
this.drawScene(canvas);
context.restore();
return canvas.toDataURL(mimeType, quality);
},
/**
* converts node into an image. Since the toImage
2012-11-27 11:12:02 +08:00
* method is asynchronous, a callback is required. toImage is most commonly used
* to cache complex drawings as an image so that they don't have to constantly be redrawn
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @param {Object} config
2012-12-17 12:52:07 +08:00
* @param {Function} config.callback function executed when the composite has completed
* @param {String} [config.mimeType] can be "image/png" or "image/jpeg".
* "image/png" is the default
2012-12-17 12:52:07 +08:00
* @param {Number} [config.x] x position of canvas section
* @param {Number} [config.y] y position of canvas section
* @param {Number} [config.width] width of canvas section
* @param {Number} [config.height] height of canvas section
* @param {Number} [config.quality] jpeg quality. If using an "image/jpeg" mimeType,
* you can specify the quality from 0 to 1, where 0 is very poor quality and 1
* is very high quality
2015-04-27 12:20:55 +08:00
* @paremt {Number} [config.pixelRatio] pixelRatio of ouput image. Default is 1.
* @example
2014-04-04 11:17:09 +08:00
* var image = node.toImage({
* callback: function(img) {
* // do stuff with img
* }
2013-05-18 06:09:57 +08:00
* });
*/
toImage: function(config) {
if (!config || !config.callback) {
2015-04-27 12:20:55 +08:00
throw 'callback required for toImage method config argument';
}
2015-01-27 15:07:51 +08:00
Konva.Util._getImage(this.toDataURL(config), function(img) {
config.callback(img);
});
},
2013-12-02 15:47:24 +08:00
setSize: function(size) {
this.setWidth(size.width);
this.setHeight(size.height);
return this;
},
getSize: function() {
return {
width: this.getWidth(),
height: this.getHeight()
};
},
getWidth: function() {
return this.attrs.width || 0;
},
getHeight: function() {
return this.attrs.height || 0;
},
/**
* get class name, which may return Stage, Layer, Group, or shape class names like Rect, Circle, Text, etc.
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {String}
*/
getClassName: function() {
return this.className || this.nodeType;
},
/**
* get the node type, which may return Stage, Layer, Group, or Node
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {String}
*/
getType: function() {
return this.nodeType;
},
2014-03-11 23:14:03 +08:00
getDragDistance: function() {
// compare with undefined because we need to track 0 value
if (this.attrs.dragDistance !== undefined) {
return this.attrs.dragDistance;
} else if (this.parent) {
return this.parent.getDragDistance();
} else {
2015-01-27 15:07:51 +08:00
return Konva.dragDistance;
2014-03-11 23:14:03 +08:00
}
},
_get: function(selector) {
2014-04-25 19:17:12 +08:00
return this.className === selector || this.nodeType === selector ? [this] : [];
},
_off: function(type, name) {
var evtListeners = this.eventListeners[type],
i, evtName;
for(i = 0; i < evtListeners.length; i++) {
evtName = evtListeners[i].name;
// the following two conditions must be true in order to remove a handler:
2015-01-27 15:07:51 +08:00
// 1) the current event name cannot be konva unless the event name is konva
// this enables developers to force remove a konva specific listener for whatever reason
// 2) an event name is not specified, or if one is specified, it matches the current event name
2015-01-27 15:07:51 +08:00
if((evtName !== 'konva' || name === 'konva') && (!name || evtName === name)) {
evtListeners.splice(i, 1);
if(evtListeners.length === 0) {
delete this.eventListeners[type];
break;
}
i--;
}
}
},
_fireChangeEvent: function(attr, oldVal, newVal) {
this._fire(attr + CHANGE, {
oldVal: oldVal,
newVal: newVal
});
},
setId: function(id) {
var oldId = this.getId();
2015-01-27 15:07:51 +08:00
Konva._removeId(oldId);
Konva._addId(this, id);
this._setAttr(ID, id);
return this;
},
setName: function(name) {
2015-02-08 10:52:19 +08:00
var oldNames = (this.getName() || '').split(/\s/g);
var newNames = (name || '').split(/\s/g);
var subname, i;
// remove all subnames
for(i = 0; i < oldNames.length; i++) {
subname = oldNames[i];
if ((newNames.indexOf(subname)) === -1 && subname) {
Konva._removeName(subname, this._id);
}
}
// add new names
for(i = 0; i < newNames.length; i++) {
subname = newNames[i];
if ((oldNames.indexOf(subname) === -1) && subname) {
Konva._addName(this, subname);
}
}
this._setAttr(NAME, name);
return this;
},
2015-02-08 07:24:11 +08:00
// naming methods
/**
* add name to node
* @method
* @memberof Konva.Node.prototype
* @param {String} name
* @returns {Konva.Node}
* @example
* node.name('red');
* node.addName('selected');
* node.name(); // return 'red selected'
*/
2015-05-04 17:02:16 +08:00
addName: function(name) {
2015-02-08 07:24:11 +08:00
if (!this.hasName(name)) {
2015-02-08 08:39:43 +08:00
var oldName = this.name();
var newName = oldName ? (oldName + ' ' + name) : name;
2015-02-08 07:24:11 +08:00
this.setName(newName);
}
return this;
},
/**
* check is node has name
* @method
* @memberof Konva.Node.prototype
* @param {String} name
* @returns {Boolean}
* @example
* node.name('red');
* node.hasName('red'); // return true
* node.hasName('selected'); // return false
*/
2015-05-04 17:02:16 +08:00
hasName: function(name) {
2015-02-08 08:39:43 +08:00
var names = (this.name() || '').split(/\s/g);
2015-02-08 07:24:11 +08:00
return names.indexOf(name) !== -1;
},
/**
* remove name from node
* @method
* @memberof Konva.Node.prototype
* @param {String} name
* @returns {Konva.Node}
* @example
* node.name('red selected');
* node.removeName('selected');
* node.hasName('selected'); // return false
* node.name(); // return 'red'
*/
2015-05-04 17:02:16 +08:00
removeName: function(name) {
2015-02-08 08:39:43 +08:00
var names = (this.name() || '').split(/\s/g);
2015-02-08 07:24:11 +08:00
var index = names.indexOf(name);
if (index !== -1) {
names.splice(index, 1);
this.setName(names.join(' '));
}
return this;
2015-02-08 07:24:11 +08:00
},
2013-12-05 00:56:21 +08:00
/**
* set attr
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
2013-12-05 00:56:21 +08:00
* @param {String} attr
* @param {*} val
2015-01-27 15:07:51 +08:00
* @returns {Konva.Node}
2013-12-05 00:56:21 +08:00
* @example
* node.setAttr('x', 5);
*/
setAttr: function(attr, val) {
2015-01-27 15:07:51 +08:00
var method = SET + Konva.Util._capitalize(attr),
2013-12-05 00:56:21 +08:00
func = this[method];
2015-01-27 15:07:51 +08:00
if(Konva.Util._isFunction(func)) {
2013-12-05 00:56:21 +08:00
func.call(this, val);
}
// otherwise set directly
else {
this._setAttr(attr, val);
}
return this;
},
_setAttr: function(key, val) {
var oldVal;
if(val !== undefined) {
oldVal = this.attrs[key];
2016-01-07 15:57:36 +08:00
if (oldVal === val) {
return;
}
this.attrs[key] = val;
this._fireChangeEvent(key, oldVal, val);
}
},
_setComponentAttr: function(key, component, val) {
2013-12-02 15:47:24 +08:00
var oldVal;
if(val !== undefined) {
oldVal = this.attrs[key];
if (!oldVal) {
// set value to default value using getAttr
this.attrs[key] = this.getAttr(key);
2013-12-02 15:47:24 +08:00
}
2015-05-04 17:02:16 +08:00
2013-12-02 15:47:24 +08:00
this.attrs[key][component] = val;
this._fireChangeEvent(key, oldVal, val);
}
},
_fireAndBubble: function(eventType, evt, compareShape) {
var okayToRun = true;
if(evt && this.nodeType === SHAPE) {
evt.target = this;
}
if(eventType === MOUSEENTER && compareShape && (this._id === compareShape._id || (this.isAncestorOf && this.isAncestorOf(compareShape)))) {
okayToRun = false;
}
else if(eventType === MOUSELEAVE && compareShape && (this._id === compareShape._id || (this.isAncestorOf && this.isAncestorOf(compareShape)))) {
okayToRun = false;
}
if(okayToRun) {
this._fire(eventType, evt);
// simulate event bubbling
2016-06-07 18:25:32 +08:00
var stopBubble =
(eventType === MOUSEENTER || eventType === MOUSELEAVE) &&
((compareShape && compareShape.isAncestorOf && compareShape.isAncestorOf(this) && !compareShape.isAncestorOf(this.parent)));
2016-01-04 08:24:27 +08:00
if((evt && !evt.cancelBubble || !evt) && this.parent && this.parent.isListening() && (!stopBubble)) {
2016-06-07 18:25:32 +08:00
if (compareShape && compareShape.parent) {
this._fireAndBubble.call(this.parent, eventType, evt, compareShape.parent);
}
else {
this._fireAndBubble.call(this.parent, eventType, evt);
}
}
}
},
_fire: function(eventType, evt) {
var events = this.eventListeners[eventType],
2013-10-05 03:43:08 +08:00
i;
evt = evt || {};
2016-01-04 08:24:27 +08:00
evt.currentTarget = this;
evt.type = eventType;
if (events) {
for(i = 0; i < events.length; i++) {
events[i].handler.call(this, evt);
}
}
},
2013-10-14 03:35:29 +08:00
/**
* draw both scene and hit graphs. If the node being drawn is the stage, all of the layers will be cleared and redrawn
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {Konva.Node}
*/
draw: function() {
this.drawScene();
this.drawHit();
return this;
}
});
/**
2015-08-28 11:29:52 +08:00
* create node with JSON string or an Object. De-serializtion does not generate custom
* shape drawing functions, images, or event handlers (this would make the
2013-06-02 13:03:02 +08:00
* serialized object huge). If your app uses custom shapes, images, and
* event handlers (it probably does), then you need to select the appropriate
* shapes after loading the stage and set these properties via on(), setDrawFunc(),
2012-11-27 11:12:02 +08:00
* and setImage() methods
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node
2015-08-28 11:29:52 +08:00
* @param {String|Object} json string or object
* @param {Element} [container] optional container dom element used only if you're
2012-11-27 11:12:02 +08:00
* creating a stage node
*/
2015-08-28 11:29:52 +08:00
Konva.Node.create = function(data, container) {
if (Konva.Util._isString(data)) {
data = JSON.parse(data);
}
return this._createNode(data, container);
};
2015-01-27 15:07:51 +08:00
Konva.Node._createNode = function(obj, container) {
var className = Konva.Node.prototype.getClassName.call(obj),
children = obj.children,
no, len, n;
// if container was passed in, add it to attrs
if(container) {
obj.attrs.container = container;
}
2015-01-27 15:07:51 +08:00
no = new Konva[className](obj.attrs);
if(children) {
len = children.length;
for(n = 0; n < len; n++) {
no.add(this._createNode(children[n]));
}
}
return no;
};
2014-01-06 06:55:35 +08:00
// =========================== add getters setters ===========================
2015-01-27 15:07:51 +08:00
Konva.Factory.addOverloadedGetterSetter(Konva.Node, 'position');
/**
2014-01-06 06:55:35 +08:00
* get/set node position relative to parent
* @name position
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @param {Object} pos
* @param {Number} pos.x
* @param {Number} pos.y
* @returns {Object}
* @example
2014-04-04 11:17:09 +08:00
* // get position
* var position = node.position();
*
2014-04-04 11:17:09 +08:00
* // set position
* node.position({
* x: 5
* y: 10
* });
*/
2015-01-27 15:07:51 +08:00
Konva.Factory.addGetterSetter(Konva.Node, 'x', 0);
/**
2014-01-06 06:55:35 +08:00
* get/set x position
* @name x
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @param {Number} x
* @returns {Object}
* @example
2014-04-04 11:17:09 +08:00
* // get x
* var x = node.x();
*
2014-04-04 11:17:09 +08:00
* // set x
* node.x(5);
*/
2015-01-27 15:07:51 +08:00
Konva.Factory.addGetterSetter(Konva.Node, 'y', 0);
/**
2014-01-06 06:55:35 +08:00
* get/set y position
* @name y
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @param {Number} y
* @returns {Integer}
* @example
2014-04-04 11:17:09 +08:00
* // get y
* var y = node.y();
*
2014-04-04 11:17:09 +08:00
* // set y
* node.y(5);
2012-11-27 11:12:02 +08:00
*/
2015-01-27 15:07:51 +08:00
Konva.Factory.addGetterSetter(Konva.Node, 'opacity', 1);
2012-11-27 11:12:02 +08:00
/**
2014-01-06 06:55:35 +08:00
* get/set opacity. Opacity values range from 0 to 1.
* A node with an opacity of 0 is fully transparent, and a node
* with an opacity of 1 is fully opaque
* @name opacity
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @param {Object} opacity
* @returns {Number}
* @example
2014-04-04 11:17:09 +08:00
* // get opacity
* var opacity = node.opacity();
*
2014-04-04 11:17:09 +08:00
* // set opacity
* node.opacity(0.5);
*/
2015-01-27 15:07:51 +08:00
Konva.Factory.addGetter(Konva.Node, 'name');
Konva.Factory.addOverloadedGetterSetter(Konva.Node, 'name');
/**
2014-01-06 06:55:35 +08:00
* get/set name
* @name name
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @param {String} name
* @returns {String}
* @example
2014-04-04 11:17:09 +08:00
* // get name
* var name = node.name();
*
2014-04-04 11:17:09 +08:00
* // set name
* node.name('foo');
2014-09-24 10:26:42 +08:00
*
* // also node may have multiple names (as css classes)
* node.name('foo bar');
*/
2015-01-27 15:07:51 +08:00
Konva.Factory.addGetter(Konva.Node, 'id');
Konva.Factory.addOverloadedGetterSetter(Konva.Node, 'id');
/**
2015-05-14 08:07:55 +08:00
* get/set id. Id is global for whole page.
* @name id
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @param {String} id
* @returns {String}
* @example
2014-04-04 11:17:09 +08:00
* // get id
* var name = node.id();
*
2014-04-04 11:17:09 +08:00
* // set id
* node.id('foo');
*/
2015-01-27 15:07:51 +08:00
Konva.Factory.addGetterSetter(Konva.Node, 'rotation', 0);
2013-01-03 13:35:51 +08:00
/**
* get/set rotation in degrees
* @name rotation
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
2014-01-06 06:55:35 +08:00
* @param {Number} rotation
* @returns {Number}
* @example
2014-04-04 11:17:09 +08:00
* // get rotation in degrees
* var rotation = node.rotation();
*
2014-04-04 11:17:09 +08:00
* // set rotation in degrees
* node.rotation(45);
*/
2015-01-27 15:07:51 +08:00
Konva.Factory.addComponentsGetterSetter(Konva.Node, 'scale', ['x', 'y']);
2013-01-03 13:35:51 +08:00
/**
2014-01-06 06:55:35 +08:00
* get/set scale
* @name scale
2013-12-02 15:47:24 +08:00
* @param {Object} scale
* @param {Number} scale.x
* @param {Number} scale.y
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {Object}
* @example
2014-04-04 11:17:09 +08:00
* // get scale
* var scale = node.scale();
*
2015-05-04 17:02:16 +08:00
* // set scale
2014-04-04 11:17:09 +08:00
* shape.scale({
* x: 2
* y: 3
2013-12-02 15:47:24 +08:00
* });
2013-01-03 13:35:51 +08:00
*/
2015-01-27 15:07:51 +08:00
Konva.Factory.addGetterSetter(Konva.Node, 'scaleX', 1);
2013-12-02 15:47:24 +08:00
/**
2014-01-06 06:55:35 +08:00
* get/set scale x
* @name scaleX
2013-12-02 15:47:24 +08:00
* @param {Number} x
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {Number}
* @example
2014-04-04 11:17:09 +08:00
* // get scale x
* var scaleX = node.scaleX();
*
2014-04-04 11:17:09 +08:00
* // set scale x
* node.scaleX(2);
*/
2015-01-27 15:07:51 +08:00
Konva.Factory.addGetterSetter(Konva.Node, 'scaleY', 1);
2013-12-02 15:47:24 +08:00
/**
2014-01-06 06:55:35 +08:00
* get/set scale y
* @name scaleY
2013-12-02 15:47:24 +08:00
* @param {Number} y
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {Number}
* @example
2014-04-04 11:17:09 +08:00
* // get scale y
* var scaleY = node.scaleY();
*
2014-04-04 11:17:09 +08:00
* // set scale y
* node.scaleY(2);
2013-05-08 01:19:54 +08:00
*/
2015-01-27 15:07:51 +08:00
Konva.Factory.addComponentsGetterSetter(Konva.Node, 'skew', ['x', 'y']);
/**
2014-01-06 06:55:35 +08:00
* get/set skew
* @name skew
* @param {Object} skew
* @param {Number} skew.x
* @param {Number} skew.y
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
2014-01-06 06:55:35 +08:00
* @returns {Object}
* @example
2014-04-04 11:17:09 +08:00
* // get skew
* var skew = node.skew();
2014-01-06 06:55:35 +08:00
*
2015-05-04 17:02:16 +08:00
* // set skew
2014-04-04 11:17:09 +08:00
* node.skew({
* x: 20
* y: 10
* });
*/
2015-01-27 15:07:51 +08:00
Konva.Factory.addGetterSetter(Konva.Node, 'skewX', 0);
/**
2014-01-06 06:55:35 +08:00
* get/set skew x
* @name skewX
* @param {Number} x
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {Number}
2014-01-06 06:55:35 +08:00
* @example
2014-04-04 11:17:09 +08:00
* // get skew x
* var skewX = node.skewX();
2014-01-06 06:55:35 +08:00
*
2014-04-04 11:17:09 +08:00
* // set skew x
2014-01-06 06:55:35 +08:00
* node.skewX(3);
*/
2015-01-27 15:07:51 +08:00
Konva.Factory.addGetterSetter(Konva.Node, 'skewY', 0);
/**
2014-01-06 06:55:35 +08:00
* get/set skew y
* @name skewY
* @param {Number} y
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @returns {Number}
2014-01-06 06:55:35 +08:00
* @example
2014-04-04 11:17:09 +08:00
* // get skew y
* var skewY = node.skewY();
2014-01-06 06:55:35 +08:00
*
2014-04-04 11:17:09 +08:00
* // set skew y
2014-01-06 06:55:35 +08:00
* node.skewY(3);
2013-05-08 01:19:54 +08:00
*/
2015-01-27 15:07:51 +08:00
Konva.Factory.addComponentsGetterSetter(Konva.Node, 'offset', ['x', 'y']);
/**
* get/set offset. Offsets the default position and rotation point
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @param {Object} offset
* @param {Number} offset.x
* @param {Number} offset.y
* @returns {Object}
* @example
2014-04-04 11:17:09 +08:00
* // get offset
* var offset = node.offset();
2014-01-06 06:55:35 +08:00
*
2014-04-04 11:17:09 +08:00
* // set offset
* node.offset({
* x: 20
* y: 10
2014-01-06 06:55:35 +08:00
* });
*/
2015-01-27 15:07:51 +08:00
Konva.Factory.addGetterSetter(Konva.Node, 'offsetX', 0);
2013-12-02 15:47:24 +08:00
/**
* get/set offset x
* @name offsetX
2015-01-20 18:06:21 +08:00
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
2013-12-02 15:47:24 +08:00
* @param {Number} x
2014-01-06 06:55:35 +08:00
* @returns {Number}
* @example
2014-04-04 11:17:09 +08:00
* // get offset x
* var offsetX = node.offsetX();
2014-01-06 06:55:35 +08:00
*
2014-04-04 11:17:09 +08:00
* // set offset x
* node.offsetX(3);
*/
2015-01-27 15:07:51 +08:00
Konva.Factory.addGetterSetter(Konva.Node, 'offsetY', 0);
2014-03-11 23:14:03 +08:00
/**
2015-01-20 18:06:21 +08:00
* get/set offset y
* @name offsetY
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
2015-01-20 18:06:21 +08:00
* @param {Number} y
2014-03-11 23:14:03 +08:00
* @returns {Number}
* @example
2015-01-20 18:06:21 +08:00
* // get offset y
* var offsetY = node.offsetY();
2014-03-11 23:14:03 +08:00
*
2015-01-20 18:06:21 +08:00
* // set offset y
* node.offsetY(3);
2014-03-11 23:14:03 +08:00
*/
2015-01-27 15:07:51 +08:00
Konva.Factory.addSetter(Konva.Node, 'dragDistance');
Konva.Factory.addOverloadedGetterSetter(Konva.Node, 'dragDistance');
2014-03-11 23:14:03 +08:00
2013-12-02 15:47:24 +08:00
/**
2015-01-20 18:06:21 +08:00
* get/set drag distance
* @name dragDistance
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
2015-01-20 18:06:21 +08:00
* @param {Number} distance
2014-01-06 06:55:35 +08:00
* @returns {Number}
* @example
2015-01-20 18:06:21 +08:00
* // get drag distance
* var dragDistance = node.dragDistance();
2014-01-06 06:55:35 +08:00
*
2015-01-20 18:06:21 +08:00
* // set distance
* // node starts dragging only if pointer moved more then 3 pixels
* node.dragDistance(3);
* // or set globally
2015-01-27 15:07:51 +08:00
* Konva.dragDistance = 3;
2013-05-08 01:19:54 +08:00
*/
2015-01-20 18:06:21 +08:00
2015-01-27 15:07:51 +08:00
Konva.Factory.addSetter(Konva.Node, 'width', 0);
Konva.Factory.addOverloadedGetterSetter(Konva.Node, 'width');
/**
2014-01-06 06:55:35 +08:00
* get/set width
* @name width
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
2013-01-03 13:35:51 +08:00
* @param {Number} width
2014-01-06 06:55:35 +08:00
* @returns {Number}
* @example
2014-04-04 11:17:09 +08:00
* // get width
* var width = node.width();
2014-01-06 06:55:35 +08:00
*
2014-04-04 11:17:09 +08:00
* // set width
2014-01-06 06:55:35 +08:00
* node.width(100);
*/
2015-01-27 15:07:51 +08:00
Konva.Factory.addSetter(Konva.Node, 'height', 0);
Konva.Factory.addOverloadedGetterSetter(Konva.Node, 'height');
/**
2014-01-06 06:55:35 +08:00
* get/set height
* @name height
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
2013-01-03 13:35:51 +08:00
* @param {Number} height
2014-01-06 06:55:35 +08:00
* @returns {Number}
* @example
2014-04-04 11:17:09 +08:00
* // get height
* var height = node.height();
2014-01-06 06:55:35 +08:00
*
2014-04-04 11:17:09 +08:00
* // set height
2014-01-06 06:55:35 +08:00
* node.height(100);
*/
2015-01-27 15:07:51 +08:00
Konva.Factory.addGetterSetter(Konva.Node, 'listening', 'inherit');
/**
2014-01-06 06:55:35 +08:00
* get/set listenig attr. If you need to determine if a node is listening or not
2015-05-04 17:02:16 +08:00
* by taking into account its parents, use the isListening() method
2014-01-06 06:55:35 +08:00
* @name listening
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
2014-01-06 06:55:35 +08:00
* @param {Boolean|String} listening Can be "inherit", true, or false. The default is "inherit".
* @returns {Boolean|String}
2014-01-06 06:55:35 +08:00
* @example
2014-04-04 11:17:09 +08:00
* // get listening attr
* var listening = node.listening();
2014-01-06 06:55:35 +08:00
*
2014-04-04 11:17:09 +08:00
* // stop listening for events
* node.listening(false);
2014-01-06 06:55:35 +08:00
*
2014-04-04 11:17:09 +08:00
* // listen for events
* node.listening(true);
2014-01-06 06:55:35 +08:00
*
2014-04-04 11:17:09 +08:00
* // listen to events according to the parent
2014-01-06 06:55:35 +08:00
* node.listening('inherit');
*/
2015-05-04 17:02:16 +08:00
Konva.Factory.addGetterSetter(Konva.Node, 'filters', undefined, function(val) {this._filterUpToDate = false; return val; });
/**
2014-01-06 06:55:35 +08:00
* get/set filters. Filters are applied to cached canvases
* @name filters
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @param {Array} filters array of filters
* @returns {Array}
* @example
2014-04-04 11:17:09 +08:00
* // get filters
* var filters = node.filters();
2014-01-06 06:55:35 +08:00
*
2014-04-04 11:17:09 +08:00
* // set a single filter
* node.cache();
2015-01-27 15:07:51 +08:00
* node.filters([Konva.Filters.Blur]);
*
2014-04-04 11:17:09 +08:00
* // set multiple filters
* node.cache();
* node.filters([
2015-01-27 15:07:51 +08:00
* Konva.Filters.Blur,
* Konva.Filters.Sepia,
* Konva.Filters.Invert
2014-01-06 06:55:35 +08:00
* ]);
*/
2015-01-27 15:07:51 +08:00
Konva.Factory.addGetterSetter(Konva.Node, 'visible', 'inherit');
/**
2014-01-06 06:55:35 +08:00
* get/set visible attr. Can be "inherit", true, or false. The default is "inherit".
* If you need to determine if a node is visible or not
2015-05-04 17:02:16 +08:00
* by taking into account its parents, use the isVisible() method
2014-01-06 06:55:35 +08:00
* @name visible
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
2014-01-06 06:55:35 +08:00
* @param {Boolean|String} visible
* @returns {Boolean|String}
* @example
2014-04-04 11:17:09 +08:00
* // get visible attr
* var visible = node.visible();
2014-01-06 06:55:35 +08:00
*
2014-04-04 11:17:09 +08:00
* // make invisible
* node.visible(false);
2014-01-06 06:55:35 +08:00
*
2014-04-04 11:17:09 +08:00
* // make visible
* node.visible(true);
2014-01-06 06:55:35 +08:00
*
2014-04-04 11:17:09 +08:00
* // make visible according to the parent
2014-01-06 06:55:35 +08:00
* node.visible('inherit');
2013-01-03 13:35:51 +08:00
*/
2015-01-27 15:07:51 +08:00
Konva.Factory.addGetterSetter(Konva.Node, 'transformsEnabled', 'all');
/**
* get/set transforms that are enabled. Can be "all", "none", or "position". The default
* is "all"
* @name transformsEnabled
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @param {String} enabled
* @returns {String}
2014-01-06 06:55:35 +08:00
* @example
2014-04-04 11:17:09 +08:00
* // enable position transform only to improve draw performance
* node.transformsEnabled('position');
2014-01-06 06:55:35 +08:00
*
2014-04-04 11:17:09 +08:00
* // enable all transforms
2014-01-06 06:55:35 +08:00
* node.transformsEnabled('all');
*/
/**
* get/set node size
* @name size
* @method
2015-01-27 15:07:51 +08:00
* @memberof Konva.Node.prototype
* @param {Object} size
* @param {Number} size.width
* @param {Number} size.height
* @returns {Object}
* @example
2014-04-04 11:17:09 +08:00
* // get node size
* var size = node.size();
* var x = size.x;
* var y = size.y;
*
2014-04-04 11:17:09 +08:00
* // set size
* node.size({
* width: 100,
* height: 200
* });
*/
2015-01-27 15:07:51 +08:00
Konva.Factory.addOverloadedGetterSetter(Konva.Node, 'size');
2015-01-27 15:07:51 +08:00
Konva.Factory.backCompat(Konva.Node, {
rotateDeg: 'rotate',
setRotationDeg: 'setRotation',
getRotationDeg: 'getRotation'
});
2015-01-27 15:07:51 +08:00
Konva.Collection.mapMethods(Konva.Node);
2015-05-04 17:02:16 +08:00
})(Konva);