diff --git a/Thorfile b/Thorfile index b62a8461..ea6120d2 100644 --- a/Thorfile +++ b/Thorfile @@ -4,7 +4,7 @@ class Build < Thor # This is the list of files to concatenate. The first file will appear at the top of the final file. All files are relative to the lib directory. FILES = [ "license.js", "src/Global.js", "src/Transition.js", "src/filters/Grayscale.js", - "src/util/Type.js", "src/util/Canvas.js", "src/util/Class.js", "src/util/Tween.js", "src/util/Transform.js", + "src/util/Type.js", "src/util/Canvas.js", "src/util/Tween.js", "src/util/Transform.js", "src/Animation.js", "src/Node.js", "src/Container.js", "src/Stage.js", "src/Layer.js", "src/Group.js", "src/Shape.js", "src/shapes/Rect.js", "src/shapes/Ellipse.js", "src/shapes/Image.js", "src/shapes/Polygon.js", "src/shapes/Text.js", "src/shapes/Line.js", "src/shapes/Sprite.js", "src/shapes/Star.js", "src/shapes/RegularPolygon.js", "src/shapes/Path.js", "src/shapes/TextPath.js" ] diff --git a/dist/kinetic-core.js b/dist/kinetic-core.js index 2fad135a..2f36afbb 100644 --- a/dist/kinetic-core.js +++ b/dist/kinetic-core.js @@ -3,7 +3,7 @@ * http://www.kineticjs.com/ * Copyright 2012, Eric Rowell * Licensed under the MIT or GPL Version 2 licenses. - * Date: Aug 14 2012 + * Date: Aug 23 2012 * * Copyright (C) 2011 - 2012 by Eric Rowell * @@ -38,9 +38,13 @@ Kinetic.Filters = {}; Kinetic.Plugins = {}; Kinetic.Global = { BUBBLE_WHITELIST: ['mousedown', 'mousemove', 'mouseup', 'mouseover', 'mouseout', 'click', 'dblclick', 'touchstart', 'touchmove', 'touchend', 'tap', 'dbltap', 'dragstart', 'dragmove', 'dragend'], + BUFFER_WHITELIST: ['fill', 'stroke', 'textFill', 'textStroke'], + BUFFER_BLACKLIST: ['shadow'], stages: [], idCounter: 0, tempNodes: {}, + //shapes hash. rgb keys and shape values + shapes: {}, maxDragTimeInterval: 20, drag: { moving: false, @@ -55,6 +59,13 @@ Kinetic.Global = { console.warn('Kinetic warning: ' + str); } }, + extend: function(c1, c2) { + for(var key in c2.prototype) { + if(!( key in c1.prototype)) { + c1.prototype[key] = c2.prototype[key]; + } + } + }, _pullNodes: function(stage) { var tempNodes = this.tempNodes; for(var key in tempNodes) { @@ -177,8 +188,8 @@ Kinetic.Transition.prototype = { } }; -Kinetic.Filters.Grayscale = function() { - var data = this.imageData.data; +Kinetic.Filters.Grayscale = function(imageData) { + var data = imageData.data; for(var i = 0; i < data.length; i += 4) { var brightness = 0.34 * data[i] + 0.5 * data[i + 1] + 0.16 * data[i + 2]; // red @@ -443,6 +454,23 @@ Kinetic.Type = { else { callback(null); } + }, + _rgbToHex: function(r, g, b) { + return ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); + }, + _hexToRgb: function(hex) { + var bigint = parseInt(hex, 16); + return { + r: (bigint >> 16) & 255, + g: (bigint >> 8) & 255, + b: bigint & 255 + }; + }, + _getRandomColorKey: function() { + var r = Math.round(Math.random() * 255); + var g = Math.round(Math.random() * 255); + var b = Math.round(Math.random() * 255); + return this._rgbToHex(r, g, b); } }; @@ -540,22 +568,6 @@ Kinetic.Canvas.prototype = { */ strip: function() { var context = this.context; - context.stroke = function() { - }; - context.fill = function() { - }; - context.fillRect = function(x, y, width, height) { - context.rect(x, y, width, height); - }; - context.strokeRect = function(x, y, width, height) { - context.rect(x, y, width, height); - }; - context.drawImage = function() { - }; - context.fillText = function() { - }; - context.strokeText = function() { - }; }, /** * toDataURL @@ -572,68 +584,6 @@ Kinetic.Canvas.prototype = { } }; -/////////////////////////////////////////////////////////////////////// -// Class -/////////////////////////////////////////////////////////////////////// -/* Simple JavaScript Inheritance -* By John Resig http://ejohn.org/ -* MIT Licensed. -*/ -// Inspired by base2 and Prototype -(function() { - var initializing = false; - // The base Class implementation (does nothing) - Kinetic.Class = function() { - }; - // Create a new Class that inherits from this class - Kinetic.Class.extend = function(prop) { - var _super = this.prototype; - - // Instantiate a base class (but only create the instance, - // don't run the init constructor) - initializing = true; - var prototype = new this(); - initializing = false; - - // Copy the properties over onto the new prototype - for(var name in prop) { - // Check if we're overwriting an existing function - prototype[name] = typeof prop[name] == "function" && typeof _super[name] == "function" ? (function(name, fn) { - return function() { - var tmp = this._super; - - // Add a new ._super() method that is the same method - // but on the super-class - this._super = _super[name]; - - // The method only need to be bound temporarily, so we - // remove it when we're done executing - var ret = fn.apply(this, arguments); - this._super = tmp; - - return ret; - }; - })(name, prop[name]) : prop[name]; - } - - // The dummy class constructor - function Class() { - // All construction is actually done in the init method - if(!initializing && this.init) - this.init.apply(this, arguments); - } - // Populate our constructed prototype object - Class.prototype = prototype; - - // Enforce the constructor to be what we expect - Class.prototype.constructor = Class; - - // And make this class extendable - Class.extend = arguments.callee; - - return Class; - }; -})(); /////////////////////////////////////////////////////////////////////// // Tween /////////////////////////////////////////////////////////////////////// @@ -1228,7 +1178,7 @@ requestAnimFrame = (function(callback) { * @param {Boolean} [config.listening] whether or not the node is listening for events * @param {String} [config.id] unique id * @param {String} [config.name] non-unique name - * @param {Number} [config.alpha] determines node opacity. Can be any number between 0 and 1 + * @param {Number} [config.opacity] determines node opacity. Can be any number between 0 and 1 * @param {Object} [config.scale] * @param {Number} [config.scale.x] * @param {Number} [config.scale.y] @@ -1247,13 +1197,17 @@ requestAnimFrame = (function(callback) { * @param {Number} [config.dragBounds.left] * @param {Function} [config.dragBoundFunc] dragBoundFunc(pos, evt) should return new position */ -Kinetic.Node = Kinetic.Class.extend({ - init: function(config) { +Kinetic.Node = function(config) { + this._nodeInit(config); +}; + +Kinetic.Node.prototype = { + _nodeInit: function(config) { this.defaultNodeAttrs = { visible: true, listening: true, name: undefined, - alpha: 1, + opacity: 1, x: 0, y: 0, scale: { @@ -1274,7 +1228,7 @@ Kinetic.Node = Kinetic.Class.extend({ this.eventListeners = {}; this.transAnim = new Kinetic.Animation(); this.setAttrs(config); - + // bind events this.on('draggableChange.kinetic', function() { this._onDraggableChange(); @@ -1777,19 +1731,19 @@ Kinetic.Node = Kinetic.Class.extend({ this.parent._setChildrenIndices(); }, /** - * get absolute alpha - * @name getAbsoluteAlpha + * get absolute opacity + * @name getAbsoluteOpacity * @methodOf Kinetic.Node.prototype */ - getAbsoluteAlpha: function() { - var absAlpha = 1; + getAbsoluteOpacity: function() { + var absOpacity = 1; var node = this; // traverse upwards while(node.nodeType !== 'Stage') { - absAlpha *= node.attrs.alpha; + absOpacity *= node.attrs.opacity; node = node.parent; } - return absAlpha; + return absOpacity; }, /** * determine if node is currently in drag and drop mode @@ -1798,7 +1752,7 @@ Kinetic.Node = Kinetic.Class.extend({ */ isDragging: function() { var go = Kinetic.Global; - return go.drag.node !== undefined && go.drag.node._id === this._id && go.drag.moving; + return go.drag.node && go.drag.node._id === this._id && go.drag.moving; }, /** * move node to another container @@ -1866,7 +1820,7 @@ Kinetic.Node = Kinetic.Class.extend({ }, /** * transition node to another state. Any property that can accept a real - * number can be transitioned, including x, y, rotation, alpha, strokeWidth, + * number can be transitioned, including x, y, rotation, opacity, strokeWidth, * radius, scale.x, scale.y, offset.x, offset.y, etc. * @name transitionTo * @methodOf Kinetic.Node.prototype @@ -1995,48 +1949,6 @@ Kinetic.Node = Kinetic.Class.extend({ node.setAttrs(obj); return node; }, - /** - * save image data - * @name saveImageData - * @methodOf Kinetic.Node.prototype - */ - saveImageData: function(width, height) { - try { - var canvas; - if(width && height) { - canvas = new Kinetic.Canvas(width, height); - } - else { - var stage = this.getStage(); - canvas = stage.bufferCanvas; - } - - var context = canvas.getContext(); - canvas.clear(); - this._draw(canvas); - var imageData = context.getImageData(0, 0, canvas.getWidth(), canvas.getHeight()); - this.imageData = imageData; - } - catch(e) { - Kinetic.Global.warn('Image data could not saved because canvas is dirty.'); - } - }, - /** - * clear image data - * @name clearImageData - * @methodOf Kinetic.Node.prototype - */ - clearImageData: function() { - delete this.imageData; - }, - /** - * get image data - * @name getImageData - * @methodOf Kinetic.Node.prototype - */ - getImageData: function() { - return this.imageData; - }, /** * Creates a composite data URL. If MIME type is not * specified, then "image/png" will result. For "image/jpeg", specify a quality @@ -2123,11 +2035,6 @@ Kinetic.Node = Kinetic.Class.extend({ this.attrs[key] = trans[key]; } }, - _setImageData: function(imageData) { - if(imageData && imageData.data) { - this.imageData = imageData; - } - }, _fireBeforeChangeEvent: function(attr, oldVal, newVal) { this._handleEvent('before' + attr.toUpperCase() + 'Change', { oldVal: oldVal, @@ -2179,7 +2086,7 @@ Kinetic.Node = Kinetic.Class.extend({ else { stage.dragAnim.node = this.getLayer(); } - stage.dragAnim.start(); + stage.dragAnim.start(); } }, _onDraggableChange: function() { @@ -2212,25 +2119,18 @@ Kinetic.Node = Kinetic.Class.extend({ /** * handle node event */ - _handleEvent: function(eventType, evt) { + _handleEvent: function(eventType, evt, compareShape) { if(this.nodeType === 'Shape') { evt.shape = this; } - var stage = this.getStage(); - var mover = stage ? stage.mouseoverShape : null; - var mout = stage ? stage.mouseoutShape : null; var el = this.eventListeners; var okayToRun = true; - /* - * determine if event handler should be skipped by comparing - * parent nodes - */ - if(eventType === 'mouseover' && mout && mout._id === this._id) { + if(eventType === 'mouseover' && compareShape && this._id === compareShape._id) { okayToRun = false; } - else if(eventType === 'mouseout' && mover && mover._id === this._id) { + else if(eventType === 'mouseout' && compareShape && this._id === compareShape._id) { okayToRun = false; } @@ -2242,18 +2142,38 @@ Kinetic.Node = Kinetic.Class.extend({ } } - if(stage && mover && mout) { - stage.mouseoverShape = mover.parent; - stage.mouseoutShape = mout.parent; - } - // simulate event bubbling if(Kinetic.Global.BUBBLE_WHITELIST.indexOf(eventType) >= 0 && !evt.cancelBubble && this.parent) { - this._handleEvent.call(this.parent, eventType, evt); + if(compareShape && compareShape.parent) { + this._handleEvent.call(this.parent, eventType, evt, compareShape.parent); + } + else { + this._handleEvent.call(this.parent, eventType, evt); + } } } - } -}); + }, + _draw: function(canvas) { + if(this.isVisible() && (!canvas || canvas.name !== 'buffer' || this.getListening())) { + if(this.__draw) { + this.__draw(canvas); + } + + var children = this.children; + if(children) { + for(var n = 0; n < children.length; n++) { + var child = children[n]; + if(child.draw) { + child.draw(canvas); + } + else { + child._draw(canvas); + } + } + } + } + }, +}; // add getter and setter methods Kinetic.Node.addSetters = function(constructor, arr) { @@ -2295,7 +2215,7 @@ Kinetic.Node._addGetter = function(constructor, attr) { }; }; // add getters setters -Kinetic.Node.addGettersSetters(Kinetic.Node, ['x', 'y', 'scale', 'detectionType', 'rotation', 'alpha', 'name', 'id', 'offset', 'draggable', 'dragConstraint', 'dragBounds', 'dragBoundFunc', 'listening']); +Kinetic.Node.addGettersSetters(Kinetic.Node, ['x', 'y', 'scale', 'rotation', 'opacity', 'name', 'id', 'offset', 'draggable', 'dragConstraint', 'dragBounds', 'dragBoundFunc', 'listening']); Kinetic.Node.addSetters(Kinetic.Node, ['rotationDeg']); /** @@ -2312,13 +2232,6 @@ Kinetic.Node.addSetters(Kinetic.Node, ['rotationDeg']); * @param {Number} y */ -/** - * set detection type - * @name setDetectionType - * @methodOf Kinetic.Node.prototype - * @param {String} type can be path or pixel - */ - /** * set node rotation in radians * @name setRotation @@ -2327,12 +2240,12 @@ Kinetic.Node.addSetters(Kinetic.Node, ['rotationDeg']); */ /** - * set alpha. Alpha values range from 0 to 1. - * A node with an alpha of 0 is fully transparent, and a node - * with an alpha of 1 is fully opaque - * @name setAlpha + * 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 setOpacity * @methodOf Kinetic.Node.prototype - * @param {Object} alpha + * @param {Object} opacity */ /** @@ -2408,12 +2321,6 @@ Kinetic.Node.addSetters(Kinetic.Node, ['rotationDeg']); * @methodOf Kinetic.Node.prototype */ -/** - * get detection type. Can be path or pixel - * @name getDetectionType - * @methodOf Kinetic.Node.prototype - */ - /** * get rotation in radians * @name getRotation @@ -2421,8 +2328,8 @@ Kinetic.Node.addSetters(Kinetic.Node, ['rotationDeg']); */ /** - * get alpha. - * @name getAlpha + * get opacity. + * @name getOpacity * @methodOf Kinetic.Node.prototype */ @@ -2499,10 +2406,14 @@ Kinetic.Node.addSetters(Kinetic.Node, ['rotationDeg']); * @param {Number} [config.dragBounds.bottom] * @param {Number} [config.dragBounds.left] */ -Kinetic.Container = Kinetic.Node.extend({ - init: function(config) { +Kinetic.Container = function(config) { + this._containerInit(config); +}; + +Kinetic.Container.prototype = { + _containerInit: function(config) { this.children = []; - this._super(config); + Kinetic.Node.call(this, config); }, /** * get children @@ -2695,24 +2606,6 @@ Kinetic.Container = Kinetic.Node.extend({ return arr; }, - /** - * draw children - */ - _drawChildren: function(canvas) { - var stage = this.getStage(); - var children = this.children; - for(var n = 0; n < children.length; n++) { - var child = children[n]; - if(child.nodeType === 'Shape') { - if(child.isVisible() && stage.isVisible()) { - child._draw(canvas); - } - } - else { - child.draw(canvas); - } - } - }, /** * set children indices */ @@ -2721,7 +2614,8 @@ Kinetic.Container = Kinetic.Node.extend({ this.children[n].index = n; } } -}); +}; +Kinetic.Global.extend(Kinetic.Container, Kinetic.Node); /////////////////////////////////////////////////////////////////////// // Stage @@ -2740,7 +2634,7 @@ Kinetic.Container = Kinetic.Node.extend({ * @param {Boolean} [config.listening] whether or not the node is listening for events * @param {String} [config.id] unique id * @param {String} [config.name] non-unique name - * @param {Number} [config.alpha] determines node opacity. Can be any number between 0 and 1 + * @param {Number} [config.opacity] determines node opacity. Can be any number between 0 and 1 * @param {Object} [config.scale] * @param {Number} [config.scale.x] * @param {Number} [config.scale.y] @@ -2758,8 +2652,12 @@ Kinetic.Container = Kinetic.Node.extend({ * @param {Number} [config.dragBounds.bottom] * @param {Number} [config.dragBounds.left] */ -Kinetic.Stage = Kinetic.Container.extend({ - init: function(config) { +Kinetic.Stage = function(config) { + this._initStage(config); +}; + +Kinetic.Stage.prototype = { + _initStage: function(config) { this.setDefaultAttrs({ width: 400, height: 200 @@ -2774,7 +2672,7 @@ Kinetic.Stage = Kinetic.Container.extend({ } // call super constructor - this._super(config); + Kinetic.Container.call(this, config); this._setStageDefaultProperties(); this._id = Kinetic.Global.idCounter++; @@ -2800,8 +2698,8 @@ Kinetic.Stage = Kinetic.Container.extend({ * @name draw * @methodOf Kinetic.Stage.prototype */ - draw: function(canvas) { - this._draw(canvas); + draw: function() { + this._draw(); }, /** * set stage size @@ -3069,6 +2967,44 @@ Kinetic.Stage = Kinetic.Container.extend({ } }); }, + /** + * get intersection object that contains shape and pixel data + * @name getIntersection + * @methodOf Kinetic.Stage.prototype + * @param {Object} pos point object + */ + getIntersection: function(pos) { + var shape; + var layers = this.getChildren(); + + /* + * traverse through layers from top to bottom and look + * for hit detection + */ + for(var n = layers.length - 1; n >= 0; n--) { + var layer = layers[n]; + var p = layer.bufferCanvas.context.getImageData(pos.x, pos.y, 1, 1).data; + // this indicates that a buffer pixel may have been found + if(p[3] === 255) { + var colorKey = Kinetic.Type._rgbToHex(p[0], p[1], p[2]); + shape = Kinetic.Global.shapes[colorKey]; + var isDragging = Kinetic.Global.drag.moving; + + return { + shape: shape, + pixel: p + }; + } + // if no shape mapped to that pixel, return pixel array + else if(p[0] > 0 || p[1] > 0 || p[2] > 0 || p[3] > 0) { + return { + pixel: p + }; + } + } + + return null; + }, _resizeDOM: function() { var width = this.attrs.width; var height = this.attrs.height; @@ -3077,15 +3013,13 @@ Kinetic.Stage = Kinetic.Container.extend({ this.content.style.width = width + 'px'; this.content.style.height = height + 'px'; - // set buffer canvas and path canvas sizes this.bufferCanvas.setSize(width, height); - this.pathCanvas.setSize(width, height); - // set user defined layer dimensions var layers = this.children; for(var n = 0; n < layers.length; n++) { var layer = layers[n]; layer.getCanvas().setSize(width, height); + layer.bufferCanvas.setSize(width, height); layer.draw(); } }, @@ -3110,234 +3044,18 @@ Kinetic.Stage = Kinetic.Container.extend({ */ _add: function(layer) { layer.canvas.setSize(this.attrs.width, this.attrs.height); + layer.bufferCanvas.setSize(this.attrs.width, this.attrs.height); // draw layer and append canvas to container layer.draw(); this.content.appendChild(layer.canvas.element); - - /* - * set layer last draw time to zero - * so that throttling doesn't take into account - * the layer draws associated with adding a node - */ - layer.lastDrawTime = 0; }, - /** - * detect event - * @param {Shape} shape - */ - _detectEvent: function(shape, evt) { - var isDragging = Kinetic.Global.drag.moving; - var go = Kinetic.Global; - var pos = this.getUserPosition(); - var el = shape.eventListeners; - var that = this; - - if(this.targetShape && shape._id === this.targetShape._id) { - this.targetFound = true; - } - - if(shape.isVisible() && pos !== undefined && shape.intersects(pos)) { - // handle onmousedown - if(!isDragging && this.mouseDown) { - this.mouseDown = false; - this.clickStart = true; - shape._handleEvent('mousedown', evt); - return true; - } - // handle onmouseup & onclick - else if(this.mouseUp) { - this.mouseUp = false; - shape._handleEvent('mouseup', evt); - - // detect if click or double click occurred - if(this.clickStart) { - /* - * if dragging and dropping, don't fire click or dbl click - * event - */ - if((!go.drag.moving) || !go.drag.node) { - shape._handleEvent('click', evt); - - if(this.inDoubleClickWindow) { - shape._handleEvent('dblclick', evt); - } - this.inDoubleClickWindow = true; - setTimeout(function() { - that.inDoubleClickWindow = false; - }, this.dblClickWindow); - } - } - return true; - } - - // handle touchstart - else if(!isDragging && this.touchStart && !this.touchMove) { - this.touchStart = false; - this.tapStart = true; - shape._handleEvent('touchstart', evt); - return true; - } - // handle touchend & tap - else if(this.touchEnd) { - this.touchEnd = false; - shape._handleEvent('touchend', evt); - - // detect if tap or double tap occurred - if(this.tapStart) { - /* - * if dragging and dropping, don't fire tap or dbltap - * event - */ - if((!go.drag.moving) || !go.drag.node) { - shape._handleEvent('tap', evt); - - if(this.inDoubleClickWindow) { - shape._handleEvent('dbltap', evt); - } - this.inDoubleClickWindow = true; - setTimeout(function() { - that.inDoubleClickWindow = false; - }, this.dblClickWindow); - } - } - return true; - } - else if(!isDragging && this.touchMove) { - shape._handleEvent('touchmove', evt); - return true; - } - /* - * NOTE: these event handlers require target shape - * handling - */ - // handle onmouseover - else if(!isDragging && this._isNewTarget(shape, evt)) { - /* - * check to see if there are stored mouseout events first. - * if there are, run those before running the onmouseover - * events - */ - if(this.mouseoutShape) { - this.mouseoverShape = shape; - this.mouseoutShape._handleEvent('mouseout', evt); - this.mouseoverShape = undefined; - } - - shape._handleEvent('mouseover', evt); - this._setTarget(shape); - return true; - } - // handle mousemove and touchmove - else { - if(!isDragging && this.mouseMove) { - shape._handleEvent('mousemove', evt); - return true; - } - } - - } - // handle mouseout condition - else if(!isDragging && this.targetShape && this.targetShape._id === shape._id) { - this._setTarget(undefined); - this.mouseoutShape = shape; - return true; - } - - return false; - }, - /** - * set new target - */ - _setTarget: function(shape) { - this.targetShape = shape; - this.targetFound = true; - }, - /** - * check if shape should be a new target - */ - _isNewTarget: function(shape, evt) { - if(!this.targetShape || (!this.targetFound && shape._id !== this.targetShape._id)) { - /* - * check if old target has an onmouseout event listener - */ - if(this.targetShape) { - var oldEl = this.targetShape.eventListeners; - if(oldEl) { - this.mouseoutShape = this.targetShape; - } - } - return true; - } - else { - return false; - } - }, - /** - * traverse container children - * @param {Container} obj - */ - _traverseChildren: function(obj, evt) { - var children = obj.children; - // propapgate backwards through children - for(var i = children.length - 1; i >= 0; i--) { - var child = children[i]; - if(child.getListening()) { - if(child.nodeType === 'Shape') { - var exit = this._detectEvent(child, evt); - if(exit) { - return true; - } - } - else { - var exit = this._traverseChildren(child, evt); - if(exit) { - return true; - } - } - } - } - - return false; - }, - /** - * handle incoming event - * @param {Event} evt - */ - _handleStageEvent: function(evt) { - var go = Kinetic.Global; + _setUserPosition: function(evt) { if(!evt) { evt = window.event; } - this._setMousePosition(evt); this._setTouchPosition(evt); - this.pathCanvas.clear(); - - /* - * loop through layers. If at any point an event - * is triggered, break out - */ - this.targetFound = false; - var shapeDetected = false; - for(var n = this.children.length - 1; n >= 0; n--) { - var layer = this.children[n]; - if(layer.isVisible() && n >= 0 && layer.getListening()) { - if(this._traverseChildren(layer, evt)) { - shapeDetected = true; - break; - } - } - } - - /* - * if no shape was detected and a mouseout shape has been stored, - * then run the onmouseout event handlers - */ - if(!shapeDetected && this.mouseoutShape) { - this.mouseoutShape._handleEvent('mouseout', evt); - this.mouseoutShape = undefined; - } }, /** * begin listening for events by adding event handlers @@ -3346,8 +3064,7 @@ Kinetic.Stage = Kinetic.Container.extend({ _bindContentEvents: function() { var go = Kinetic.Global; var that = this; - - var events = ['mousedown', 'mousemove', 'mouseup', 'mouseover', 'mouseout', 'touchstart', 'touchmove', 'touchend']; + var events = ['mousedown', 'mousemove', 'mouseup', 'mouseout', 'touchstart', 'touchmove', 'touchend']; for(var n = 0; n < events.length; n++) { var pubEvent = events[n]; @@ -3360,15 +3077,14 @@ Kinetic.Stage = Kinetic.Container.extend({ }()); } }, - _mouseover: function(evt) { - this._handleStageEvent(evt); - }, _mouseout: function(evt) { + this._setUserPosition(evt); + var go = Kinetic.Global; // if there's a current target shape, run mouseout handlers var targetShape = this.targetShape; - if(targetShape) { + if(targetShape && !go.drag.moving) { targetShape._handleEvent('mouseout', evt); - this.targetShape = undefined; + this.targetShape = null; } this.mousePos = undefined; @@ -3376,19 +3092,45 @@ Kinetic.Stage = Kinetic.Container.extend({ this._endDrag(evt); }, _mousemove: function(evt) { - this.mouseDown = false; - this.mouseUp = false; - this.mouseMove = true; - this._handleStageEvent(evt); + this._setUserPosition(evt); + var go = Kinetic.Global; + var obj = this.getIntersection(this.getUserPosition()); + + if(obj) { + var shape = obj.shape; + if(shape) { + if(!go.drag.moving && obj.pixel[3] === 255 && (!this.targetShape || this.targetShape._id !== shape._id)) { + if(this.targetShape) { + this.targetShape._handleEvent('mouseout', evt, shape); + } + shape._handleEvent('mouseover', evt, this.targetShape); + this.targetShape = shape; + } + else { + shape._handleEvent('mousemove', evt); + } + } + } + /* + * if no shape was detected, clear target shape and try + * to run mouseout from previous target shape + */ + else if(this.targetShape && !go.drag.moving) { + this.targetShape._handleEvent('mouseout', evt); + this.targetShape = null; + } // start drag and drop this._startDrag(evt); }, _mousedown: function(evt) { - this.mouseDown = true; - this.mouseUp = false; - this.mouseMove = false; - this._handleStageEvent(evt); + this._setUserPosition(evt); + var obj = this.getIntersection(this.getUserPosition()); + if(obj && obj.shape) { + var shape = obj.shape; + this.clickStart = true; + shape._handleEvent('mousedown', evt); + } //init stage drag and drop if(this.attrs.draggable) { @@ -3396,21 +3138,49 @@ Kinetic.Stage = Kinetic.Container.extend({ } }, _mouseup: function(evt) { - this.mouseDown = false; - this.mouseUp = true; - this.mouseMove = false; - this._handleStageEvent(evt); + this._setUserPosition(evt); + var go = Kinetic.Global; + var obj = this.getIntersection(this.getUserPosition()); + var that = this; + if(obj && obj.shape) { + var shape = obj.shape; + shape._handleEvent('mouseup', evt); + + // detect if click or double click occurred + if(this.clickStart) { + /* + * if dragging and dropping, don't fire click or dbl click + * event + */ + if((!go.drag.moving) || !go.drag.node) { + shape._handleEvent('click', evt); + + if(this.inDoubleClickWindow) { + shape._handleEvent('dblclick', evt); + } + this.inDoubleClickWindow = true; + setTimeout(function() { + that.inDoubleClickWindow = false; + }, this.dblClickWindow); + } + } + } this.clickStart = false; // end drag and drop this._endDrag(evt); }, _touchstart: function(evt) { + this._setUserPosition(evt); evt.preventDefault(); - this.touchStart = true; - this.touchEnd = false; - this.touchMove = false; - this._handleStageEvent(evt); + var obj = this.getIntersection(this.getUserPosition()); + + if(obj && obj.shape) { + var shape = obj.shape; + this.tapStart = true; + shape._handleEvent('touchstart', evt); + } + /* * init stage drag and drop */ @@ -3419,20 +3189,47 @@ Kinetic.Stage = Kinetic.Container.extend({ } }, _touchend: function(evt) { - this.touchStart = false; - this.touchEnd = true; - this.touchMove = false; - this._handleStageEvent(evt); + this._setUserPosition(evt); + var go = Kinetic.Global; + var obj = this.getIntersection(this.getUserPosition()); + var that = this; + if(obj && obj.shape) { + var shape = obj.shape; + shape._handleEvent('touchend', evt); + + // detect if tap or double tap occurred + if(this.tapStart) { + /* + * if dragging and dropping, don't fire tap or dbltap + * event + */ + if((!go.drag.moving) || !go.drag.node) { + shape._handleEvent('tap', evt); + + if(this.inDoubleClickWindow) { + shape._handleEvent('dbltap', evt); + } + this.inDoubleClickWindow = true; + setTimeout(function() { + that.inDoubleClickWindow = false; + }, this.dblClickWindow); + } + } + } + this.tapStart = false; // end drag and drop this._endDrag(evt); }, _touchmove: function(evt) { + this._setUserPosition(evt); evt.preventDefault(); - this.touchEnd = false; - this.touchMove = true; - this._handleStageEvent(evt); + var obj = this.getIntersection(this.getUserPosition()); + if(obj && obj.shape) { + var shape = obj.shape; + shape._handleEvent('touchmove', evt); + } // start drag and drop this._startDrag(evt); @@ -3484,13 +3281,13 @@ Kinetic.Stage = Kinetic.Container.extend({ var go = Kinetic.Global; var node = go.drag.node; if(node) { - if (node.nodeType === 'Stage') { - node.draw(); - } - else { - node.getLayer().draw(); - } - + if(node.nodeType === 'Stage') { + node.draw(); + } + else { + node.getLayer().draw(); + } + // handle dragend if(go.drag.moving) { go.drag.moving = false; @@ -3578,11 +3375,7 @@ Kinetic.Stage = Kinetic.Container.extend({ width: this.attrs.width, height: this.attrs.height }); - this.pathCanvas = new Kinetic.Canvas({ - width: this.attrs.width, - height: this.attrs.height - }); - this.pathCanvas.strip(); + this._resizeDOM(); }, _addId: function(node) { @@ -3638,35 +3431,18 @@ Kinetic.Stage = Kinetic.Container.extend({ _setStageDefaultProperties: function() { this.nodeType = 'Stage'; this.dblClickWindow = 400; - this.targetShape = undefined; - this.targetFound = false; - this.mouseoverShape = undefined; - this.mouseoutShape = undefined; - - // desktop flags + this.targetShape = null; this.mousePos = undefined; - this.mouseDown = false; - this.mouseUp = false; - this.mouseMove = false; this.clickStart = false; - - // mobile flags this.touchPos = undefined; - this.touchStart = false; - this.touchEnd = false; - this.touchMove = false; this.tapStart = false; this.ids = {}; this.names = {}; - //shapes hash. rgb keys and shape values - this.shapes = {}; - this.dragAnim = new Kinetic.Animation(); - }, - _draw: function(canvas) { - this._drawChildren(canvas); + this.dragAnim = new Kinetic.Animation(); } -}); +}; +Kinetic.Global.extend(Kinetic.Stage, Kinetic.Container); // add getters and setters Kinetic.Node.addGettersSetters(Kinetic.Stage, ['width', 'height']); @@ -3713,7 +3489,7 @@ Kinetic.Node.addGettersSetters(Kinetic.Stage, ['width', 'height']); * @param {Boolean} [config.listening] whether or not the node is listening for events * @param {String} [config.id] unique id * @param {String} [config.name] non-unique name - * @param {Number} [config.alpha] determines node opacity. Can be any number between 0 and 1 + * @param {Number} [config.opacity] determines node opacity. Can be any number between 0 and 1 * @param {Object} [config.scale] * @param {Number} [config.scale.x] * @param {Number} [config.scale.y] @@ -3731,22 +3507,26 @@ Kinetic.Node.addGettersSetters(Kinetic.Stage, ['width', 'height']); * @param {Number} [config.dragBounds.bottom] * @param {Number} [config.dragBounds.left] */ -Kinetic.Layer = Kinetic.Container.extend({ - init: function(config) { +Kinetic.Layer = function(config) { + this._initLayer(config); +}; + +Kinetic.Layer.prototype = { + _initLayer: function(config) { this.setDefaultAttrs({ clearBeforeDraw: true }); this.nodeType = 'Layer'; - this.lastDrawTime = 0; this.beforeDrawFunc = undefined; this.afterDrawFunc = undefined; - this.canvas = new Kinetic.Canvas(); this.canvas.getElement().style.position = 'absolute'; + this.bufferCanvas = new Kinetic.Canvas(); + this.bufferCanvas.name = 'buffer'; // call super constructor - this._super(config); + Kinetic.Container.call(this, config); }, /** * draw children nodes. this includes any groups @@ -3755,7 +3535,41 @@ Kinetic.Layer = Kinetic.Container.extend({ * @methodOf Kinetic.Layer.prototype */ draw: function(canvas) { - this._draw(canvas); + // before draw handler + if(this.beforeDrawFunc !== undefined) { + this.beforeDrawFunc.call(this); + } + + if(canvas) { + this._draw(canvas); + } + else { + this._draw(this.getCanvas()); + this._draw(this.bufferCanvas); + } + + // after draw handler + if(this.afterDrawFunc !== undefined) { + this.afterDrawFunc.call(this); + } + }, + /** + * draw children nodes on buffer. this includes any groups + * or shapes + * @name drawBuffer + * @methodOf Kinetic.Layer.prototype + */ + drawBuffer: function() { + this.draw(this.bufferCanvas); + }, + /** + * draw children nodes on scene. this includes any groups + * or shapes + * @name drawScene + * @methodOf Kinetic.Layer.prototype + */ + drawScene: function() { + this.draw(this.getCanvas()); }, /** * set before draw handler @@ -3830,46 +3644,13 @@ Kinetic.Layer = Kinetic.Container.extend({ } return canvas.toDataURL(mimeType, quality); }, - /** - * private draw children - */ - _draw: function(canvas) { - /* - * if canvas is not defined, then use the canvas - * tied to the layer - */ - if(!canvas) { - canvas = this.getCanvas(); - } - - var time = new Date().getTime(); - this.lastDrawTime = time; - - // before draw handler - if(this.beforeDrawFunc !== undefined) { - this.beforeDrawFunc.call(this); - } - + __draw: function(canvas) { if(this.attrs.clearBeforeDraw) { canvas.clear(); } - - if(this.isVisible()) { - // draw custom func - if(this.attrs.drawFunc !== undefined) { - this.attrs.drawFunc.call(this); - } - - // draw children - this._drawChildren(canvas); - } - - // after draw handler - if(this.afterDrawFunc !== undefined) { - this.afterDrawFunc.call(this); - } } -}); +}; +Kinetic.Global.extend(Kinetic.Layer, Kinetic.Container); // add getters and setters Kinetic.Node.addGettersSetters(Kinetic.Layer, ['clearBeforeDraw']); @@ -3902,7 +3683,7 @@ Kinetic.Node.addGettersSetters(Kinetic.Layer, ['clearBeforeDraw']); * @param {Boolean} [config.listening] whether or not the node is listening for events * @param {String} [config.id] unique id * @param {String} [config.name] non-unique name - * @param {Number} [config.alpha] determines node opacity. Can be any number between 0 and 1 + * @param {Number} [config.opacity] determines node opacity. Can be any number between 0 and 1 * @param {Object} [config.scale] * @param {Number} [config.scale.x] * @param {Number} [config.scale.y] @@ -3920,22 +3701,19 @@ Kinetic.Node.addGettersSetters(Kinetic.Layer, ['clearBeforeDraw']); * @param {Number} [config.dragBounds.bottom] * @param {Number} [config.dragBounds.left] */ -Kinetic.Group = Kinetic.Container.extend({ - init: function(config) { +Kinetic.Group = function(config) { + this._initGroup(config); +}; + +Kinetic.Group.prototype = { + _initGroup: function(config) { this.nodeType = 'Group'; // call super constructor - this._super(config); - }, - draw: function(canvas) { - this._draw(canvas); - }, - _draw: function(canvas) { - if(this.attrs.visible) { - this._drawChildren(canvas); - } + Kinetic.Container.call(this, config); } -}); +}; +Kinetic.Global.extend(Kinetic.Group, Kinetic.Container); /////////////////////////////////////////////////////////////////////// // Shape @@ -3972,17 +3750,15 @@ Kinetic.Group = Kinetic.Container.extend({ * @config {Obect} [config.shadow.blur.offset] * @config {Number} [config.shadow.blur.offset.x] * @config {Number} [config.shadow.blur.offset.y] - * @config {Number} [config.shadow.alpha] shadow alpha. Can be any real number + * @config {Number} [config.shadow.opacity] shadow opacity. Can be any real number * between 0 and 1 - * @config {String} [config.detectionType] shape detection type. Can be path or pixel. - * The default is path because it performs better * @param {Number} [config.x] * @param {Number} [config.y] * @param {Boolean} [config.visible] * @param {Boolean} [config.listening] whether or not the node is listening for events * @param {String} [config.id] unique id * @param {String} [config.name] non-unique name - * @param {Number} [config.alpha] determines node opacity. Can be any number between 0 and 1 + * @param {Number} [config.opacity] determines node opacity. Can be any number between 0 and 1 * @param {Object} [config.scale] * @param {Number} [config.scale.x] * @param {Number} [config.scale.y] @@ -4000,17 +3776,29 @@ Kinetic.Group = Kinetic.Container.extend({ * @param {Number} [config.dragBounds.bottom] * @param {Number} [config.dragBounds.left] */ -Kinetic.Shape = Kinetic.Node.extend({ - init: function(config) { - this.setDefaultAttrs({ - detectionType: 'path' - }); +Kinetic.Shape = function(config) { + this._initShape(config); +}; +Kinetic.Shape.prototype = { + _initShape: function(config) { this.nodeType = 'Shape'; this.appliedShadow = false; + // set colorKey + var shapes = Kinetic.Global.shapes; + var key; + while(true) { + key = Kinetic.Type._getRandomColorKey(); + if(key && !( key in shapes)) { + break; + } + } + this.colorKey = key; + shapes[key] = this; + // call super constructor - this._super(config); + Kinetic.Node.call(this, config); }, /** * get canvas context tied to the layer @@ -4226,7 +4014,7 @@ Kinetic.Shape = Kinetic.Node.extend({ _applyShadow: function(context) { var s = this.attrs.shadow; if(s) { - var aa = this.getAbsoluteAlpha(); + var aa = this.getAbsoluteOpacity(); // defaults var color = s.color ? s.color : 'black'; var blur = s.blur ? s.blur : 5; @@ -4235,8 +4023,8 @@ Kinetic.Shape = Kinetic.Node.extend({ y: 0 }; - if(s.alpha) { - context.globalAlpha = s.alpha * aa; + if(s.opacity) { + context.globalAlpha = s.opacity * aa; } context.shadowColor = color; context.shadowBlur = blur; @@ -4258,28 +4046,13 @@ Kinetic.Shape = Kinetic.Node.extend({ intersects: function() { var pos = Kinetic.Type._getXY(Array.prototype.slice.call(arguments)); var stage = this.getStage(); - - // path detection - if(this.attrs.detectionType === 'path') { - var pathCanvas = stage.pathCanvas; - var pathCanvasContext = pathCanvas.getContext(); - - this._draw(pathCanvas); - - return pathCanvasContext.isPointInPath(pos.x, pos.y); - } - - // pixel detection - if(this.imageData) { - var w = stage.attrs.width; - var alpha = this.imageData.data[((w * pos.y) + pos.x) * 4 + 3]; - return (alpha); - } - - // default - return false; + var bufferCanvas = stage.bufferCanvas; + bufferCanvas.clear(); + this._draw(bufferCanvas); + var obj = stage.getIntersection(pos); + return !!(obj && obj.pixel[3] > 0); }, - _draw: function(canvas) { + __draw: function(canvas) { if(this.attrs.drawFunc) { var stage = this.getStage(); var context = canvas.getContext(); @@ -4301,21 +4074,70 @@ Kinetic.Shape = Kinetic.Node.extend({ } /* - * pre styles include alpha, linejoin + * pre styles include opacity, linejoin */ - var absAlpha = this.getAbsoluteAlpha(); - if(absAlpha !== 1) { - context.globalAlpha = absAlpha; + var absOpacity = this.getAbsoluteOpacity(); + if(absOpacity !== 1) { + context.globalAlpha = absOpacity; } this.applyLineJoin(context); // draw the shape this.appliedShadow = false; + + var wl = Kinetic.Global.BUFFER_WHITELIST; + var bl = Kinetic.Global.BUFFER_BLACKLIST; + var attrs = {}; + + if(canvas.name === 'buffer') { + for(var n = 0; n < wl.length; n++) { + var key = wl[n]; + attrs[key] = this.attrs[key]; + if(this.attrs[key] || (key === 'fill' && !this.attrs.stroke && !('image' in this.attrs))) { + this.attrs[key] = '#' + this.colorKey; + } + } + + for(var n = 0; n < bl.length; n++) { + var key = bl[n]; + attrs[key] = this.attrs[key]; + this.attrs[key] = ''; + } + + // image is a special case + if('image' in this.attrs) { + attrs.image = this.attrs.image; + + if(this.imageBuffer) { + this.attrs.image = this.imageBuffer; + } + else { + this.attrs.image = null; + this.attrs.fill = '#' + this.colorKey; + } + } + + context.globalAlpha = 1; + } + this.attrs.drawFunc.call(this, canvas.getContext()); + + if(canvas.name === 'buffer') { + var bothLists = wl.concat(bl); + for(var n = 0; n < bothLists.length; n++) { + var key = bothLists[n]; + this.attrs[key] = attrs[key]; + } + + // image is a special case + this.attrs.image = attrs.image; + } + context.restore(); } } -}); +}; +Kinetic.Global.extend(Kinetic.Shape, Kinetic.Node); // add getters and setters Kinetic.Node.addGettersSetters(Kinetic.Shape, ['fill', 'stroke', 'lineJoin', 'strokeWidth', 'shadow', 'drawFunc', 'filter']); @@ -4408,8 +4230,11 @@ Kinetic.Node.addGettersSetters(Kinetic.Shape, ['fill', 'stroke', 'lineJoin', 'st * @augments Kinetic.Shape * @param {Object} config */ -Kinetic.Rect = Kinetic.Shape.extend({ - init: function(config) { +Kinetic.Rect = function(config) { + this._initRect(config); +} +Kinetic.Rect.prototype = { + _initRect: function(config) { this.setDefaultAttrs({ width: 0, height: 0, @@ -4417,8 +4242,8 @@ Kinetic.Rect = Kinetic.Shape.extend({ }); this.shapeType = "Rect"; config.drawFunc = this.drawFunc; - // call super constructor - this._super(config); + + Kinetic.Shape.call(this, config); }, drawFunc: function(context) { context.beginPath(); @@ -4463,7 +4288,8 @@ Kinetic.Rect = Kinetic.Shape.extend({ height: this.attrs.height }; } -}); +}; +Kinetic.Global.extend(Kinetic.Rect, Kinetic.Shape); // add getters setters Kinetic.Node.addGettersSetters(Kinetic.Rect, ['width', 'height', 'cornerRadius']); @@ -4515,8 +4341,12 @@ Kinetic.Node.addGettersSetters(Kinetic.Rect, ['width', 'height', 'cornerRadius'] * @augments Kinetic.Shape * @param {Object} config */ -Kinetic.Ellipse = Kinetic.Shape.extend({ - init: function(config) { +Kinetic.Ellipse = function(config) { + this._initEllipse(config); +}; + +Kinetic.Ellipse.prototype = { + _initEllipse: function(config) { this.setDefaultAttrs({ radius: { x: 0, @@ -4528,7 +4358,7 @@ Kinetic.Ellipse = Kinetic.Shape.extend({ config.drawFunc = this.drawFunc; // call super constructor - this._super(config); + Kinetic.Shape.call(this, config); this._convertRadius(); var that = this; this.on('radiusChange.kinetic', function() { @@ -4565,7 +4395,8 @@ Kinetic.Ellipse = Kinetic.Shape.extend({ */ this.attrs.radius = type._getXY(radius); } -}); +}; +Kinetic.Global.extend(Kinetic.Ellipse, Kinetic.Shape); // Circle backwards compatibility Kinetic.Circle = Kinetic.Ellipse; @@ -4604,24 +4435,35 @@ Kinetic.Node.addGettersSetters(Kinetic.Ellipse, ['radius']); * @param {Number} [config.height] * @param {Object} [config.crop] */ -Kinetic.Image = Kinetic.Shape.extend({ - init: function(config) { +Kinetic.Image = function(config) { + this._initImage(config); +}; + +Kinetic.Image.prototype = { + _initImage: function(config) { this.shapeType = "Image"; config.drawFunc = this.drawFunc; // call super constructor - this._super(config); + Kinetic.Shape.call(this, config); + + var that = this; + this.on('imageChange', function(evt) { + that._syncSize(); + }); + + this._syncSize(); }, drawFunc: function(context) { + var width = this.getWidth(); + var height = this.getHeight(); + + context.beginPath(); + context.rect(0, 0, width, height); + context.closePath(); + this.fill(context); + this.stroke(context); + if(this.attrs.image) { - var width = this.getWidth(); - var height = this.getHeight(); - - context.beginPath(); - context.rect(0, 0, width, height); - context.closePath(); - this.fill(context); - this.stroke(context); - // if cropping if(this.attrs.crop && this.attrs.crop.width && this.attrs.crop.height) { var cropX = this.attrs.crop.x ? this.attrs.crop.x : 0; @@ -4656,34 +4498,6 @@ Kinetic.Image = Kinetic.Shape.extend({ height: this.attrs.height }; }, - /** - * get width - * @name getWidth - * @methodOf Kinetic.Image.prototype - */ - getWidth: function() { - if(this.attrs.width) { - return this.attrs.width; - } - if(this.attrs.image) { - return this.attrs.image.width; - } - return 0; - }, - /** - * get height - * @name getHeight - * @methodOf Kinetic.Image.prototype - */ - getHeight: function() { - if(this.attrs.height) { - return this.attrs.height; - } - if(this.attrs.image) { - return this.attrs.image.height; - } - return 0; - }, /** * apply filter * @name applyFilter @@ -4694,14 +4508,14 @@ Kinetic.Image = Kinetic.Shape.extend({ * filter has been applied */ applyFilter: function(config) { - try { - var trans = this._clearTransform(); - this.saveImageData(this.getWidth(), this.getHeight()); - this._setTransform(trans); - - config.filter.call(this, config); + var canvas = new Kinetic.Canvas(this.attrs.image.width, this.attrs.image.height); + var context = canvas.getContext(); + context.drawImage(this.attrs.image, 0, 0); + try { + var imageData = context.getImageData(0, 0, canvas.getWidth(), canvas.getHeight()); + config.filter(imageData, config); var that = this; - Kinetic.Type._getImage(this.getImageData(), function(imageObj) { + Kinetic.Type._getImage(imageData, function(imageObj) { that.setImage(imageObj); if(config.callback) { @@ -4712,12 +4526,70 @@ Kinetic.Image = Kinetic.Shape.extend({ catch(e) { Kinetic.Global.warn('Unable to apply filter.'); } + }, + /** + * create image buffer which enables more accurate hit detection mapping of the image + * by avoiding event detections for transparent pixels + * @name createImageBuffer + * @methodOf Kinetic.Image.prototype + * @param {Function} [callback] callback function to be called once + * the buffer image has been created and set + */ + createImageBuffer: function(callback) { + var canvas = new Kinetic.Canvas(this.attrs.width, this.attrs.height); + var context = canvas.getContext(); + context.drawImage(this.attrs.image, 0, 0); + try { + var imageData = context.getImageData(0, 0, canvas.getWidth(), canvas.getHeight()); + var data = imageData.data; + var rgbColorKey = Kinetic.Type._hexToRgb(this.colorKey); + // replace non transparent pixels with color key + for(var i = 0, n = data.length; i < n; i += 4) { + data[i] = rgbColorKey.r; + data[i + 1] = rgbColorKey.g; + data[i + 2] = rgbColorKey.b; + // i+3 is alpha (the fourth element) + } + + var that = this; + Kinetic.Type._getImage(imageData, function(imageObj) { + that.imageBuffer = imageObj; + if(callback) { + callback(); + } + }); + } + catch(e) { + Kinetic.Global.warn('Unable to create image buffer.'); + } + }, + /** + * clear buffer image + * @name clearImageBuffer + * @methodOf Kinetic.Image.prototype + */ + clearImageBuffer: function() { + delete this.imageBuffer; + }, + _syncSize: function() { + if(this.attrs.image) { + if(!this.attrs.width) { + this.setAttrs({ + width: this.attrs.image.width + }); + } + if(!this.attrs.height) { + this.setAttrs({ + height: this.attrs.image.height + }); + } + } } -}); +}; +Kinetic.Global.extend(Kinetic.Image, Kinetic.Shape); // add getters setters -Kinetic.Node.addGettersSetters(Kinetic.Image, ['image', 'crop', 'filter']); -Kinetic.Node.addSetters(Kinetic.Image, ['width', 'height']); +Kinetic.Node.addGettersSetters(Kinetic.Image, ['image', 'crop', 'filter', 'width', 'height']); /** * set width @@ -4771,6 +4643,18 @@ Kinetic.Node.addSetters(Kinetic.Image, ['width', 'height']); * @name getFilter * @methodOf Kinetic.Image.prototype */ + +/** + * get width + * @name getWidth + * @methodOf Kinetic.Image.prototype + */ + +/** + * get height + * @name getHeight + * @methodOf Kinetic.Image.prototype + */ /////////////////////////////////////////////////////////////////////// // Polygon /////////////////////////////////////////////////////////////////////// @@ -4780,8 +4664,12 @@ Kinetic.Node.addSetters(Kinetic.Image, ['width', 'height']); * @augments Kinetic.Shape * @param {Object} config */ -Kinetic.Polygon = Kinetic.Shape.extend({ - init: function(config) { +Kinetic.Polygon = function(config) { + this._initPolygon(config); +}; + +Kinetic.Polygon.prototype = { + _initPolygon: function(config) { this.setDefaultAttrs({ points: [] }); @@ -4789,7 +4677,7 @@ Kinetic.Polygon = Kinetic.Shape.extend({ this.shapeType = "Polygon"; config.drawFunc = this.drawFunc; // call super constructor - this._super(config); + Kinetic.Shape.call(this, config); }, drawFunc: function(context) { context.beginPath(); @@ -4801,7 +4689,8 @@ Kinetic.Polygon = Kinetic.Shape.extend({ this.fill(context); this.stroke(context); } -}); +}; +Kinetic.Global.extend(Kinetic.Polygon, Kinetic.Shape); // add getters setters Kinetic.Node.addGettersSetters(Kinetic.Polygon, ['points']); @@ -4828,8 +4717,12 @@ Kinetic.Node.addGettersSetters(Kinetic.Polygon, ['points']); * @augments Kinetic.Shape * @param {Object} config */ -Kinetic.Text = Kinetic.Shape.extend({ - init: function(config) { +Kinetic.Text = function(config) { + this._initText(config); +}; + +Kinetic.Text.prototype = { + _initText: function(config) { this.setDefaultAttrs({ fontFamily: 'Calibri', text: '', @@ -4850,7 +4743,7 @@ Kinetic.Text = Kinetic.Shape.extend({ config.drawFunc = this.drawFunc; // call super constructor - this._super(config); + Kinetic.Shape.call(this, config); // update text data for certain attr changes var attrs = ['fontFamily', 'fontSize', 'fontStyle', 'padding', 'align', 'lineHeight', 'text', 'width', 'height']; @@ -5033,7 +4926,9 @@ Kinetic.Text = Kinetic.Shape.extend({ } this.textArr = arr; } -}); +}; +Kinetic.Global.extend(Kinetic.Text, Kinetic.Shape); + // add getters setters Kinetic.Node.addGettersSetters(Kinetic.Text, ['fontFamily', 'fontSize', 'fontStyle', 'textFill', 'textStroke', 'textStrokeWidth', 'padding', 'align', 'lineHeight', 'text', 'width', 'height', 'cornerRadius', 'fill', 'stroke', 'strokeWidth', 'shadow']); @@ -5214,8 +5109,12 @@ Kinetic.Node.addGettersSetters(Kinetic.Text, ['fontFamily', 'fontSize', 'fontSty * @augments Kinetic.Shape * @param {Object} config */ -Kinetic.Line = Kinetic.Shape.extend({ - init: function(config) { +Kinetic.Line = function(config) { + this._initLine(config); +}; + +Kinetic.Line.prototype = { + _initLine: function(config) { this.setDefaultAttrs({ points: [], lineCap: 'butt', @@ -5226,7 +5125,7 @@ Kinetic.Line = Kinetic.Shape.extend({ this.shapeType = "Line"; config.drawFunc = this.drawFunc; // call super constructor - this._super(config); + Kinetic.Shape.call(this, config); }, drawFunc: function(context) { var lastPos = {}; @@ -5301,7 +5200,8 @@ Kinetic.Line = Kinetic.Shape.extend({ context.moveTo(x2, y2); } -}); +}; +Kinetic.Global.extend(Kinetic.Line, Kinetic.Shape); // add getters setters Kinetic.Node.addGettersSetters(Kinetic.Line, ['dashArray', 'lineCap', 'points']); @@ -5360,8 +5260,12 @@ Kinetic.Node.addGettersSetters(Kinetic.Line, ['dashArray', 'lineCap', 'points']) * @augments Kinetic.Shape * @param {Object} config */ -Kinetic.Sprite = Kinetic.Shape.extend({ - init: function(config) { +Kinetic.Sprite = function(config) { + this._initSprite(config); +}; + +Kinetic.Sprite.prototype = { + _initSprite: function(config) { this.setDefaultAttrs({ index: 0, frameRate: 17 @@ -5369,7 +5273,7 @@ Kinetic.Sprite = Kinetic.Shape.extend({ config.drawFunc = this.drawFunc; // call super constructor - this._super(config); + Kinetic.Shape.call(this, config); this.anim = new Kinetic.Animation(); var that = this; this.on('animationChange.kinetic', function() { @@ -5378,10 +5282,17 @@ Kinetic.Sprite = Kinetic.Shape.extend({ }); }, drawFunc: function(context) { + var anim = this.attrs.animation; + var index = this.attrs.index; + var f = this.attrs.animations[anim][index]; + + context.beginPath(); + context.rect(0, 0, f.width, f.height); + context.closePath(); + this.fill(context); + this.stroke(context); + if(this.attrs.image) { - var anim = this.attrs.animation; - var index = this.attrs.index; - var f = this.attrs.animations[anim][index]; context.beginPath(); context.rect(0, 0, f.width, f.height); @@ -5447,7 +5358,8 @@ Kinetic.Sprite = Kinetic.Shape.extend({ this.attrs.index = 0; } } -}); +}; +Kinetic.Global.extend(Kinetic.Sprite, Kinetic.Shape); // add getters setters Kinetic.Node.addGettersSetters(Kinetic.Sprite, ['animation', 'animations', 'index']); @@ -5499,8 +5411,12 @@ Kinetic.Node.addGettersSetters(Kinetic.Sprite, ['animation', 'animations', 'inde * @augments Kinetic.Shape * @param {Object} config */ -Kinetic.Star = Kinetic.Shape.extend({ - init: function(config) { +Kinetic.Star = function(config) { + this._initStar(config); +}; + +Kinetic.Star.prototype = { + _initStar: function(config) { this.setDefaultAttrs({ numPoints: 0, innerRadius: 0, @@ -5510,7 +5426,7 @@ Kinetic.Star = Kinetic.Shape.extend({ this.shapeType = "Star"; config.drawFunc = this.drawFunc; // call super constructor - this._super(config); + Kinetic.Shape.call(this, config); }, drawFunc: function(context) { context.beginPath(); @@ -5527,7 +5443,8 @@ Kinetic.Star = Kinetic.Shape.extend({ this.fill(context); this.stroke(context); } -}); +}; +Kinetic.Global.extend(Kinetic.Star, Kinetic.Shape); // add getters setters Kinetic.Node.addGettersSetters(Kinetic.Star, ['numPoints', 'innerRadius', 'outerRadius']); @@ -5579,8 +5496,12 @@ Kinetic.Node.addGettersSetters(Kinetic.Star, ['numPoints', 'innerRadius', 'outer * @augments Kinetic.Shape * @param {Object} config */ -Kinetic.RegularPolygon = Kinetic.Shape.extend({ - init: function(config) { +Kinetic.RegularPolygon = function(config) { + this._initRegularPolygon(config); +}; + +Kinetic.RegularPolygon.prototype = { + _initRegularPolygon: function(config) { this.setDefaultAttrs({ radius: 0, sides: 0 @@ -5589,7 +5510,7 @@ Kinetic.RegularPolygon = Kinetic.Shape.extend({ this.shapeType = "RegularPolygon"; config.drawFunc = this.drawFunc; // call super constructor - this._super(config); + Kinetic.Shape.call(this, config); }, drawFunc: function(context) { context.beginPath(); @@ -5604,7 +5525,8 @@ Kinetic.RegularPolygon = Kinetic.Shape.extend({ this.fill(context); this.stroke(context); } -}); +}; +Kinetic.Global.extend(Kinetic.RegularPolygon, Kinetic.Shape); // add getters setters Kinetic.Node.addGettersSetters(Kinetic.RegularPolygon, ['radius', 'sides']); @@ -5643,15 +5565,19 @@ Kinetic.Node.addGettersSetters(Kinetic.RegularPolygon, ['radius', 'sides']); * @augments Kinetic.Shape * @param {Object} config */ -Kinetic.Path = Kinetic.Shape.extend({ - init: function(config) { +Kinetic.Path = function(config) { + this._initPath(config); +}; + +Kinetic.Path.prototype = { + _initPath: function(config) { this.shapeType = "Path"; this.dataArray = []; var that = this; config.drawFunc = this.drawFunc; // call super constructor - this._super(config); + Kinetic.Shape.call(this, config); this.dataArray = Kinetic.Path.parsePathData(this.attrs.data); this.on('dataChange', function() { that.dataArray = Kinetic.Path.parsePathData(that.attrs.data); @@ -5701,7 +5627,8 @@ Kinetic.Path = Kinetic.Shape.extend({ this.fill(context); this.stroke(context); } -}); +}; +Kinetic.Global.extend(Kinetic.Path, Kinetic.Shape); /* * Utility methods written by jfollas to @@ -6198,8 +6125,12 @@ Kinetic.Node.addGettersSetters(Kinetic.Path, ['data']); * @augments Kinetic.Shape * @param {Object} config */ -Kinetic.TextPath = Kinetic.Shape.extend({ - init: function(config) { +Kinetic.TextPath = function(config) { + this._initTextPath(config); +}; + +Kinetic.TextPath.prototype = { + _initTextPath: function(config) { this.setDefaultAttrs({ fontFamily: 'Calibri', fontSize: 12, @@ -6215,7 +6146,7 @@ Kinetic.TextPath = Kinetic.Shape.extend({ config.drawFunc = this.drawFunc; // call super constructor - this._super(config); + Kinetic.Shape.call(this, config); this.dataArray = Kinetic.Path.parsePathData(this.attrs.data); this.on('dataChange', function() { that.dataArray = Kinetic.Path.parsePathData(this.attrs.data); @@ -6469,7 +6400,8 @@ Kinetic.TextPath = Kinetic.Shape.extend({ p0 = p1; } } -}); +}; +Kinetic.Global.extend(Kinetic.TextPath, Kinetic.Shape); // add setters and getters Kinetic.Node.addGettersSetters(Kinetic.TextPath, ['fontFamily', 'fontSize', 'fontStyle', 'textFill', 'textStroke', 'textStrokeWidth', 'text']); diff --git a/dist/kinetic-core.min.js b/dist/kinetic-core.min.js index b721c3ac..48b7a4ac 100644 --- a/dist/kinetic-core.min.js +++ b/dist/kinetic-core.min.js @@ -3,7 +3,7 @@ * http://www.kineticjs.com/ * Copyright 2012, Eric Rowell * Licensed under the MIT or GPL Version 2 licenses. - * Date: Aug 14 2012 + * Date: Aug 23 2012 * * Copyright (C) 2011 - 2012 by Eric Rowell * @@ -25,5 +25,5 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -var Kinetic={};Kinetic.Filters={},Kinetic.Plugins={},Kinetic.Global={BUBBLE_WHITELIST:["mousedown","mousemove","mouseup","mouseover","mouseout","click","dblclick","touchstart","touchmove","touchend","tap","dbltap","dragstart","dragmove","dragend"],stages:[],idCounter:0,tempNodes:{},maxDragTimeInterval:20,drag:{moving:!1,offset:{x:0,y:0},lastDrawTime:0},warn:function(e){console&&console.warn&&console.warn("Kinetic warning: "+e)},_pullNodes:function(e){var t=this.tempNodes;for(var n in t){var r=t[n];r.getStage()!==undefined&&r.getStage()._id===e._id&&(e._addId(r),e._addName(r),this._removeTempNode(r))}},_addTempNode:function(e){this.tempNodes[e._id]=e},_removeTempNode:function(e){delete this.tempNodes[e._id]}},Kinetic.Transition=function(e,t){function r(e,t,i,s){for(var o in e)o!=="duration"&&o!=="easing"&&o!=="callback"&&(Kinetic.Type._isObject(e[o])?(i[o]={},r(e[o],t[o],i[o],s)):n._add(n._getTween(t,o,e[o],i,s)))}this.node=e,this.config=t,this.tweens=[];var n=this,i={};r(t,e.attrs,i,i);var s=0;for(var o=0;o=n.tweens.length&&n.onFinished()}}},Kinetic.Transition.prototype={start:function(){for(var e=0;e0},_getXY:function(e){if(this._isNumber(e))return{x:e,y:e};if(this._isArray(e)){if(e.length===1){var t=e[0];if(this._isNumber(t))return{x:t,y:t};if(this._isArray(t))return{x:t[0],y:t[1]};if(this._isObject(t))return t}else if(e.length>=2)return{x:e[0],y:e[1]}}else if(this._isObject(e))return e;return{x:0,y:0}},_getSize:function(e){if(this._isNumber(e))return{width:e,height:e};if(this._isArray(e))if(e.length===1){var t=e[0];if(this._isNumber(t))return{width:t,height:t};if(this._isArray(t)){if(t.length>=4)return{width:t[2],height:t[3]};if(t.length>=2)return{width:t[0],height:t[1]}}else if(this._isObject(t))return t}else{if(e.length>=4)return{width:e[2],height:e[3]};if(e.length>=2)return{width:e[0],height:e[1]}}else if(this._isObject(e))return e;return{width:0,height:0}},_getPoints:function(e){if(e===undefined)return[];if(this._isObject(e[0]))return e;var t=[];for(var n=0;nthis.getDuration()?this.looping?(this.rewind(e-this._duration),this.update(),this.broadcastMessage("onLooped",{target:this,type:"onLooped"})):(this._time=this._duration,this.update(),this.stop(),this.broadcastMessage("onFinished",{target:this,type:"onFinished"})):e<0?(this.rewind(),this.update()):(this._time=e,this.update())},getTime:function(){return this._time},setDuration:function(e){this._duration=e===null||e<=0?1e5:e},getDuration:function(){return this._duration},setPosition:function(e){this.prevPos=this._pos,this.propFunc(e),this._pos=e,this.broadcastMessage("onChanged",{target:this,type:"onChanged"})},getPosition:function(e){return e===undefined&&(e=this._time),this.func(e,this.begin,this._change,this._duration)},setFinish:function(e){this._change=e-this.begin},getFinish:function(){return this.begin+this._change},start:function(){this.rewind(),this.startEnterFrame(),this.broadcastMessage("onStarted",{target:this,type:"onStarted"})},rewind:function(e){this.stop(),this._time=e===undefined?0:e,this.fixTime(),this.update()},fforward:function(){this._time=this._duration,this.fixTime(),this.update()},update:function(){this.setPosition(this.getPosition(this._time))},startEnterFrame:function(){this.stopEnterFrame(),this.isPlaying=!0,this.onEnterFrame()},onEnterFrame:function(){this.isPlaying&&this.nextFrame()},nextFrame:function(){this.setTime((this.getTimer()-this._startTime)/1e3)},stop:function(){this.stopEnterFrame(),this.broadcastMessage("onStopped",{target:this,type:"onStopped"})},stopEnterFrame:function(){this.isPlaying=!1},continueTo:function(e,t){this.begin=this._pos,this.setFinish(e),this._duration!==undefined&&this.setDuration(t),this.start()},resume:function(){this.fixTime(),this.startEnterFrame(),this.broadcastMessage("onResumed",{target:this,type:"onResumed"})},yoyo:function(){this.continueTo(this.begin,this._time)},addListener:function(e){return this.removeListener(e),this._listeners.push(e)},removeListener:function(e){var t=this._listeners,n=t.length;while(n--)if(t[n]==e)return t.splice(n,1),!0;return!1},broadcastMessage:function(){var e=[];for(var t=0;t0){this._runFrames();var e=this;requestAnimFrame(function(){e._animationLoop()})}else this.animRunning=!1},Kinetic.Animation._handleAnimation=function(){var e=this;this.animRunning||(this.animRunning=!0,e._animationLoop())},requestAnimFrame=function(e){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(e){window.setTimeout(e,1e3/60)}}(),Kinetic.Node=Kinetic.Class.extend({init:function(e){this.defaultNodeAttrs={visible:!0,listening:!0,name:undefined,alpha:1,x:0,y:0,scale:{x:1,y:1},rotation:0,offset:{x:0,y:0},dragConstraint:"none",dragBounds:{},draggable:!1},this.setDefaultAttrs(this.defaultNodeAttrs),this.eventListeners={},this.transAnim=new Kinetic.Animation,this.setAttrs(e),this.on("draggableChange.kinetic",function(){this._onDraggableChange()});var t=this;this.on("idChange.kinetic",function(e){var n=t.getStage();n&&(n._removeId(e.oldVal),n._addId(t))}),this.on("nameChange.kinetic",function(e){var n=t.getStage();n&&(n._removeName(e.oldVal,t._id),n._addName(t))}),this._onDraggableChange()},on:function(e,t){var n=e.split(" ");for(var r=0;r1?o[1]:"";this.eventListeners[u]||(this.eventListeners[u]=[]),this.eventListeners[u].push({name:a,handler:t})}},off:function(e){var t=e.split(" ");for(var n=0;n1){var u=s[1];for(var a=0;a0&&s[0].getLevel()<=e&&i(s)}var e=this.getLevel(),t=this.getStage(),n=this,r=0;return n.nodeType!=="Stage"&&i(n.getStage().getChildren()),r},getLevel:function(){var e=0,t=this.parent;while(t)e++,t=t.parent;return e},setPosition:function(){var e=Kinetic.Type._getXY(Array.prototype.slice.call(arguments));this.setAttrs(e)},getPosition:function(){return{x:this.attrs.x,y:this.attrs.y}},getAbsolutePosition:function(){var e=this.getAbsoluteTransform(),t=this.getOffset();return e.translate(t.x,t.y),e.getTranslation()},setAbsolutePosition:function(){var e=Kinetic.Type._getXY(Array.prototype.slice.call(arguments)),t=this._clearTransform();this.attrs.x=t.x,this.attrs.y=t.y,delete t.x,delete t.y;var n=this.getAbsoluteTransform();n.invert(),n.translate(e.x,e.y),e={x:this.attrs.x+n.getTranslation().x,y:this.attrs.y+n.getTranslation().y},this.setPosition(e.x,e.y),this._setTransform(t)},move:function(){var e=Kinetic.Type._getXY(Array.prototype.slice.call(arguments)),t=this.getX(),n=this.getY();e.x!==undefined&&(t+=e.x),e.y!==undefined&&(n+=e.y),this.setAttrs({x:t,y:n})},getRotationDeg:function(){return this.attrs.rotation*180/Math.PI},rotate:function(e){this.setAttrs({rotation:this.getRotation()+e})},rotateDeg:function(e){this.setAttrs({rotation:this.getRotation()+e*Math.PI/180})},moveToTop:function(){var e=this.index;this.parent.children.splice(e,1),this.parent.children.push(this),this.parent._setChildrenIndices()},moveUp:function(){var e=this.index;this.parent.children.splice(e,1),this.parent.children.splice(e+1,0,this),this.parent._setChildrenIndices()},moveDown:function(){var e=this.index;e>0&&(this.parent.children.splice(e,1),this.parent.children.splice(e-1,0,this),this.parent._setChildrenIndices())},moveToBottom:function(){var e=this.index;this.parent.children.splice(e,1),this.parent.children.unshift(this),this.parent._setChildrenIndices()},setZIndex:function(e){var t=this.index;this.parent.children.splice(t,1),this.parent.children.splice(e,0,this),this.parent._setChildrenIndices()},getAbsoluteAlpha:function(){var e=1,t=this;while(t.nodeType!=="Stage")e*=t.attrs.alpha,t=t.parent;return e},isDragging:function(){var e=Kinetic.Global;return e.drag.node!==undefined&&e.drag.node._id===this._id&&e.drag.moving},moveTo:function(e){var t=this.parent;t.children.splice(this.index,1),t._setChildrenIndices(),e.children.push(this),this.index=e.children.length-1,this.parent=e,e._setChildrenIndices()},getParent:function(){return this.parent},getLayer:function(){return this.nodeType==="Layer"?this:this.getParent().getLayer()},getStage:function(){return this.nodeType!=="Stage"&&this.getParent()?this.getParent().getStage():this.nodeType==="Stage"?this:undefined},simulate:function(e){this._handleEvent(e,{})},transitionTo:function(e){var t=this.nodeType==="Stage"?this:this.getLayer(),n=this,r=new Kinetic.Transition(this,e);return this.transAnim.func=function(){r._onEnterFrame()},this.transAnim.node=t,r.onFinished=function(){n.transAnim.stop(),n.transAnim.node.draw(),e.callback&&e.callback()},r.start(),this.transAnim.start(),r},getAbsoluteTransform:function(){var e=new Kinetic.Transform,t=[],n=this.parent;t.unshift(this);while(n)t.unshift(n),n=n.parent;for(var r=0;r=0&&!t.cancelBubble&&this.parent&&this._handleEvent.call(this.parent,e,t)}}}),Kinetic.Node.addSetters=function(e,t){for(var n=0;n0)this.remove(this.children[0])},add:function(e){e._id=Kinetic.Global.idCounter++,e.index=this.children.length,e.parent=this,this.children.push(e);var t=e.getStage();if(!t)Kinetic.Global._addTempNode(e);else{t._addId(e),t._addName(e);var n=Kinetic.Global;n._pullNodes(t)}return this._add!==undefined&&this._add(e),this},remove:function(e){if(e&&e.index!==undefined&&this.children[e.index]._id==e._id){var t=this.getStage();t&&(t._removeId(e.getId()),t._removeName(e.getName(),e._id)),Kinetic.Global._removeTempNode(e),this.children.splice(e.index,1),this._setChildrenIndices();while(e.children&&e.children.length>0)e.remove(e.children[0]);this._remove!==undefined&&this._remove(e)}return this},get:function(e){var t=this.getStage(),n,r=e.slice(1);if(e.charAt(0)==="#")n=t.ids[r]!==undefined?[t.ids[r]]:[];else{if(e.charAt(0)!==".")return e==="Shape"||e==="Group"||e==="Layer"?this._getNodes(e):!1;n=t.names[r]!==undefined?t.names[r]:[]}var i=[];for(var s=0;s=0;r--){var i=n[r];if(i.getListening())if(i.nodeType==="Shape"){var s=this._detectEvent(i,t);if(s)return!0}else{var s=this._traverseChildren(i,t);if(s)return!0}}return!1},_handleStageEvent:function(e){var t=Kinetic.Global;e||(e=window.event),this._setMousePosition(e),this._setTouchPosition(e),this.pathCanvas.clear(),this.targetFound=!1;var n=!1;for(var r=this.children.length-1;r>=0;r--){var i=this.children[r];if(i.isVisible()&&r>=0&&i.getListening()&&this._traverseChildren(i,e)){n=!0;break}}!n&&this.mouseoutShape&&(this.mouseoutShape._handleEvent("mouseout",e),this.mouseoutShape=undefined)},_bindContentEvents:function(){var e=Kinetic.Global,t=this,n=["mousedown","mousemove","mouseup","mouseover","mouseout","touchstart","touchmove","touchend"];for(var r=0;ro.right&&(f.x=o.right),o.top!==undefined&&f.yo.bottom&&(f.y=o.bottom),u==="horizontal"?f.y=a.y:u==="vertical"&&(f.x=a.x),r.setAbsolutePosition(f),n.drag.moving||(n.drag.moving=!0,n.drag.node._handleEvent("dragstart",e)),n.drag.node._handleEvent("dragmove",e)}},_buildDOM:function(){this.content=document.createElement("div"),this.content.style.position="relative",this.content.style.display="inline-block",this.content.className="kineticjs-content",this.attrs.container.appendChild(this.content),this.bufferCanvas=new Kinetic.Canvas({width:this.attrs.width,height:this.attrs.height}),this.pathCanvas=new Kinetic.Canvas({width:this.attrs.width,height:this.attrs.height}),this.pathCanvas.strip(),this._resizeDOM()},_addId:function(e){e.attrs.id!==undefined&&(this.ids[e.attrs.id]=e)},_removeId:function(e){e!==undefined&&delete this.ids[e]},_addName:function(e){var t=e.attrs.name;t!==undefined&&(this.names[t]===undefined&&(this.names[t]=[]),this.names[t].push(e))},_removeName:function(e,t){if(e!==undefined){var n=this.names[e];if(n!==undefined){for(var r=0;r0&&r&&(this.attrs.height==="auto"||i*(n+1)this.attrs.width-this.attrs.padding*2){if(s==0)break;var a=u.lastIndexOf(" "),f=u.lastIndexOf("-"),l=Math.max(a,f);if(l>=0){o=e.splice(0,1+l).join("");break}o=e.splice(0,s).join("");break}s++,s===e.length&&(o=e.splice(0,s).join(""))}this.textWidth=Math.max(this.textWidth,this._getTextSize(o).width),o!==undefined&&(t.push(o),r=!0),n++}this.textArr=t}}),Kinetic.Node.addGettersSetters(Kinetic.Text,["fontFamily","fontSize","fontStyle","textFill","textStroke","textStrokeWidth","padding","align","lineHeight","text","width","height","cornerRadius","fill","stroke","strokeWidth","shadow"]),Kinetic.Line=Kinetic.Shape.extend({init:function(e){this.setDefaultAttrs({points:[],lineCap:"butt",dashArray:[],detectionType:"pixel"}),this.shapeType="Line",e.drawFunc=this.drawFunc,this._super(e)},drawFunc:function(e){var t={};e.beginPath(),e.moveTo(this.attrs.points[0].x,this.attrs.points[0].y);for(var n=1;n0){var s=this.attrs.points[n-1].x,o=this.attrs.points[n-1].y;this._dashedLine(e,s,o,r,i,this.attrs.dashArray)}else e.lineTo(r,i)}!this.attrs.lineCap||(e.lineCap=this.attrs.lineCap),this.stroke(e)},_dashedLine:function(e,t,n,r,i,s){var o=s.length,u=r-t,a=i-n,f=u>a,l=f?a/u:u/a;l>9999?l=9999:l<-9999&&(l=-9999);var c=Math.sqrt(u*u+a*a),h=0,p=!0;while(c>=.1&&h<1e4){var d=s[h++%o];d===0&&(d=.001),d>c&&(d=c);var v=Math.sqrt(d*d/(1+l*l));f?(t+=u<0&&a<0?v*-1:v,n+=u<0&&a<0?l*v*-1:l*v):(t+=u<0&&a<0?l*v*-1:l*v,n+=u<0&&a<0?v*-1:v),e[p?"lineTo":"moveTo"](t,n),c-=d,p=!p}e.moveTo(r,i)}}),Kinetic.Node.addGettersSetters(Kinetic.Line,["dashArray","lineCap","points"]),Kinetic.Sprite=Kinetic.Shape.extend({init:function(e){this.setDefaultAttrs({index:0,frameRate:17}),e.drawFunc=this.drawFunc,this._super(e),this.anim=new Kinetic.Animation;var t=this;this.on("animationChange.kinetic",function(){t.setIndex(0)})},drawFunc:function(e){if(this.attrs.image){var t=this.attrs.animation,n=this.attrs.index,r=this.attrs.animations[t][n];e.beginPath(),e.rect(0,0,r.width,r.height),e.closePath(),this.drawImage(e,this.attrs.image,r.x,r.y,r.width,r.height,0,0,r.width,r.height)}},start:function(){var e=this,t=this.getLayer();this.anim.node=t,this.interval=setInterval(function(){var t=e.attrs.index;e._updateIndex(),e.afterFrameFunc&&t===e.afterFrameIndex&&e.afterFrameFunc()},1e3/this.attrs.frameRate),this.anim.start()},stop:function(){this.anim.stop(),clearInterval(this.interval)},afterFrame:function(e,t){this.afterFrameIndex=e,this.afterFrameFunc=t},_updateIndex:function(){var e=this.attrs.index,t=this.attrs.animation;ea?u:a,d=u>a?1:u/a,v=u>a?a/u:1;e.translate(s,o),e.rotate(c),e.scale(d,v),e.arc(0,0,p,f,f+l,1-h),e.scale(1/d,1/v),e.rotate(-c),e.translate(-s,-o);break;case"z":e.closePath()}}this.fill(e),this.stroke(e)}}),Kinetic.Path.getLineLength=function(e,t,n,r){return Math.sqrt((n-e)*(n-e)+(r-t)*(r-t))},Kinetic.Path.getPointOnLine=function(e,t,n,r,i,s,o){s===undefined&&(s=t),o===undefined&&(o=n);var u=(i-n)/(r-t+1e-8),a=Math.sqrt(e*e/(1+u*u)),f=u*a,l;if((o-n)/(s-t+1e-8)===u)l={x:s+a,y:o+f};else{var c,h,p=this.getLineLength(t,n,r,i);if(p<1e-8)return undefined;var d=(s-t)*(r-t)+(o-n)*(i-n);d/=p*p,c=t+d*(r-t),h=n+d*(i-n);var v=this.getLineLength(s,o,c,h),m=Math.sqrt(e*e-v*v);a=Math.sqrt(m*m/(1+u*u)),f=u*a,l={x:c+a,y:h+f}}return l},Kinetic.Path.getPointOnCubicBezier=function(e,t,n,r,i,s,o,u,a){function f(e){return e*e*e}function l(e){return 3*e*e*(1-e)}function c(e){return 3*e*(1-e)*(1-e)}function h(e){return(1-e)*(1-e)*(1-e)}var p=u*f(e)+s*l(e)+r*c(e)+t*h(e),d=a*f(e)+o*l(e)+i*c(e)+n*h(e);return{x:p,y:d}},Kinetic.Path.getPointOnQuadraticBezier=function(e,t,n,r,i,s,o){function u(e){return e*e}function a(e){return 2*e*(1-e)}function f(e){return(1-e)*(1-e)}var l=s*u(e)+r*a(e)+t*f(e),c=o*u(e)+i*a(e)+n*f(e);return{x:l,y:c}},Kinetic.Path.getPointOnEllipticalArc=function(e,t,n,r,i,s){var o=Math.cos(s),u=Math.sin(s),a={x:n*Math.cos(i),y:r*Math.sin(i)};return{x:e+(a.x*o-a.y*u),y:t+(a.x*u+a.y*o)}},Kinetic.Path.parsePathData=function(e){if(!e)return[];var t=e,n=["m","M","l","L","v","V","h","H","z","Z","c","C","q","Q","t","T","s","S","a","A"];t=t.replace(new RegExp(" ","g"),",");for(var r=0;r0&&l[0]===""&&l.shift();for(var c=0;c0){if(isNaN(l[0]))break;var h=null,p=[],d=o,v=u;switch(f){case"l":o+=l.shift(),u+=l.shift(),h="L",p.push(o,u);break;case"L":o=l.shift(),u=l.shift(),p.push(o,u);break;case"m":o+=l.shift(),u+=l.shift(),h="M",p.push(o,u),f="l";break;case"M":o=l.shift(),u=l.shift(),h="M",p.push(o,u),f="L";break;case"h":o+=l.shift(),h="L",p.push(o,u);break;case"H":o=l.shift(),h="L",p.push(o,u);break;case"v":u+=l.shift(),h="L",p.push(o,u);break;case"V":u=l.shift(),h="L",p.push(o,u);break;case"C":p.push(l.shift(),l.shift(),l.shift(),l.shift()),o=l.shift(),u=l.shift(),p.push(o,u);break;case"c":p.push(o+l.shift(),u+l.shift(),o+l.shift(),u+l.shift()),o+=l.shift(),u+=l.shift(),h="C",p.push(o,u);break;case"S":var m=o,g=u,y=s[s.length-1];y.command==="C"&&(m=o+(o-y.points[2]),g=u+(u-y.points[3])),p.push(m,g,l.shift(),l.shift()),o=l.shift(),u=l.shift(),h="C",p.push(o,u);break;case"s":var m=o,g=u,y=s[s.length-1];y.command==="C"&&(m=o+(o-y.points[2]),g=u+(u-y.points[3])),p.push(m,g,o+l.shift(),u+l.shift()),o+=l.shift(),u+=l.shift(),h="C",p.push(o,u);break;case"Q":p.push(l.shift(),l.shift()),o=l.shift(),u=l.shift(),p.push(o,u);break;case"q":p.push(o+l.shift(),u+l.shift()),o+=l.shift(),u+=l.shift(),h="Q",p.push(o,u);break;case"T":var m=o,g=u,y=s[s.length-1];y.command==="Q"&&(m=o+(o-y.points[0]),g=u+(u-y.points[1])),o=l.shift(),u=l.shift(),h="Q",p.push(m,g,o,u);break;case"t":var m=o,g=u,y=s[s.length-1];y.command==="Q"&&(m=o+(o-y.points[0]),g=u+(u-y.points[1])),o+=l.shift(),u+=l.shift(),h="Q",p.push(m,g,o,u);break;case"A":var b=l.shift(),w=l.shift(),E=l.shift(),S=l.shift(),x=l.shift(),T=o,N=u;o=l.shift(),u=l.shift(),h="A",p=this.convertEndpointToCenterParameterization(T,N,o,u,S,x,b,w,E);break;case"a":var b=l.shift(),w=l.shift(),E=l.shift(),S=l.shift(),x=l.shift(),T=o,N=u;o+=l.shift(),u+=l.shift(),h="A",p=this.convertEndpointToCenterParameterization(T,N,o,u,S,x,b,w,E)}s.push({command:h||f,points:p,start:{x:d,y:v},pathLength:this.calcLength(d,v,h||f,p)})}(f==="z"||f==="Z")&&s.push({command:"z",points:[],start:undefined,pathLength:0})}return s},Kinetic.Path.calcLength=function(e,n,r,i){var s,o,u,a=Kinetic.Path;switch(r){case"L":return a.getLineLength(e,n,i[0],i[1]);case"C":s=0,o=a.getPointOnCubicBezier(0,e,n,i[0],i[1],i[2],i[3],i[4],i[5]);for(t=.01;t<=1;t+=.01)u=a.getPointOnCubicBezier(t,e,n,i[0],i[1],i[2],i[3],i[4],i[5]),s+=a.getLineLength(o.x,o.y,u.x,u.y),o=u;return s;case"Q":s=0,o=a.getPointOnQuadraticBezier(0,e,n,i[0],i[1],i[2],i[3]);for(t=.01;t<=1;t+=.01)u=a.getPointOnQuadraticBezier(t,e,n,i[0],i[1],i[2],i[3]),s+=a.getLineLength(o.x,o.y,u.x,u.y),o=u;return s;case"A":s=0;var f=i[4],l=i[5],c=i[4]+l,h=Math.PI/180;Math.abs(f-c)c;t-=h)u=a.getPointOnEllipticalArc(i[0],i[1],i[2],i[3],t,0),s+=a.getLineLength(o.x,o.y,u.x,u.y),o=u;else for(t=f+h;t1&&(o*=Math.sqrt(h),u*=Math.sqrt(h));var p=Math.sqrt((o*o*u*u-o*o*c*c-u*u*l*l)/(o*o*c*c+u*u*l*l));i==s&&(p*=-1),isNaN(p)&&(p=0);var d=p*o*c/u,v=p*-u*l/o,m=(e+n)/2+Math.cos(f)*d-Math.sin(f)*v,g=(t+r)/2+Math.sin(f)*d+Math.cos(f)*v,y=function(e){return Math.sqrt(e[0]*e[0]+e[1]*e[1])},b=function(e,t){return(e[0]*t[0]+e[1]*t[1])/(y(e)*y(t))},w=function(e,t){return(e[0]*t[1]=1&&(T=0),s===0&&T>0&&(T-=2*Math.PI),s==1&&T<0&&(T+=2*Math.PI),[m,g,o,u,E,T,f,s]},Kinetic.Node.addGettersSetters(Kinetic.Path,["data"]),Kinetic.TextPath=Kinetic.Shape.extend({init:function(e){this.setDefaultAttrs({fontFamily:"Calibri",fontSize:12,fontStyle:"normal",detectionType:"path",text:""}),this.dummyCanvas=document.createElement("canvas"),this.shapeType="TextPath",this.dataArray=[];var t=this;e.drawFunc=this.drawFunc,this._super(e),this.dataArray=Kinetic.Path.parsePathData(this.attrs.data),this.on("dataChange",function(){t.dataArray=Kinetic.Path.parsePathData(this.attrs.data)});var n=["text","textStroke","textStrokeWidth"];for(var r=0;r0)return o=n,t[n];t[n].command=="M"&&(r={x:t[n].points[0],y:t[n].points[1]})}return{}},f=function(t,n){var o=e._getTextSize(t).width,f=0,l=0,c=!1;i=undefined;while(Math.abs(o-f)/o>.01&&l<25){l++;var h=f;while(s===undefined)s=a(),s&&h+s.pathLengtho?i=Kinetic.Path.getPointOnLine(o,r.x,r.y,s.points[0],s.points[1],r.x,r.y):s=undefined;break;case"A":var d=s.points[4],v=s.points[5],m=s.points[4]+v;u===0?u=d+1e-8:o>f?u+=Math.PI/180*v/Math.abs(v):u-=Math.PI/360*v/Math.abs(v),Math.abs(u)>Math.abs(m)&&(u=m,p=!0),i=Kinetic.Path.getPointOnEllipticalArc(s.points[0],s.points[1],s.points[2],s.points[3],u,s.points[6]);break;case"C":u===0?o>s.pathLength?u=1e-8:u=o/s.pathLength:o>f?u+=(o-f)/s.pathLength:u-=(f-o)/s.pathLength,u>1&&(u=1,p=!0),i=Kinetic.Path.getPointOnCubicBezier(u,s.start.x,s.start.y,s.points[0],s.points[1],s.points[2],s.points[3],s.points[4],s.points[5]);break;case"Q":u===0?u=o/s.pathLength:o>f?u+=(o-f)/s.pathLength:u-=(f-o)/s.pathLength,u>1&&(u=1,p=!0),i=Kinetic.Path.getPointOnQuadraticBezier(u,s.start.x,s.start.y,s.points[0],s.points[1],s.points[2],s.points[3])}i!==undefined&&(f=Kinetic.Path.getLineLength(r.x,r.y,i.x,i.y)),p&&(p=!1,s=undefined)}};for(var l=0;l=n.tweens.length&&n.onFinished()}}},Kinetic.Transition.prototype={start:function(){for(var e=0;e0},_getXY:function(e){if(this._isNumber(e))return{x:e,y:e};if(this._isArray(e)){if(e.length===1){var t=e[0];if(this._isNumber(t))return{x:t,y:t};if(this._isArray(t))return{x:t[0],y:t[1]};if(this._isObject(t))return t}else if(e.length>=2)return{x:e[0],y:e[1]}}else if(this._isObject(e))return e;return{x:0,y:0}},_getSize:function(e){if(this._isNumber(e))return{width:e,height:e};if(this._isArray(e))if(e.length===1){var t=e[0];if(this._isNumber(t))return{width:t,height:t};if(this._isArray(t)){if(t.length>=4)return{width:t[2],height:t[3]};if(t.length>=2)return{width:t[0],height:t[1]}}else if(this._isObject(t))return t}else{if(e.length>=4)return{width:e[2],height:e[3]};if(e.length>=2)return{width:e[0],height:e[1]}}else if(this._isObject(e))return e;return{width:0,height:0}},_getPoints:function(e){if(e===undefined)return[];if(this._isObject(e[0]))return e;var t=[];for(var n=0;n>16&255,g:t>>8&255,b:t&255}},_getRandomColorKey:function(){var e=Math.round(Math.random()*255),t=Math.round(Math.random()*255),n=Math.round(Math.random()*255);return this._rgbToHex(e,t,n)}},Kinetic.Canvas=function(e,t){this.element=document.createElement("canvas"),this.context=this.element.getContext("2d"),this.element.width=e,this.element.height=t},Kinetic.Canvas.prototype={clear:function(){var e=this.getContext(),t=this.getElement();e.clearRect(0,0,t.width,t.height)},getElement:function(){return this.element},getContext:function(){return this.context},setWidth:function(e){this.element.width=e},setHeight:function(e){this.element.height=e},getWidth:function(){return this.element.width},getHeight:function(){return this.element.height},setSize:function(e,t){this.setWidth(e),this.setHeight(t)},strip:function(){var e=this.context},toDataURL:function(e,t){try{return this.element.toDataURL(e,t)}catch(n){return this.element.toDataURL()}}},Kinetic.Tween=function(e,t,n,r,i,s){this._listeners=[],this.addListener(this),this.obj=e,this.propFunc=t,this.begin=r,this._pos=r,this.setDuration(s),this.isPlaying=!1,this._change=0,this.prevTime=0,this.prevPos=0,this.looping=!1,this._time=0,this._position=0,this._startTime=0,this._finish=0,this.name="",this.func=n,this.setFinish(i)},Kinetic.Tween.prototype={setTime:function(e){this.prevTime=this._time,e>this.getDuration()?this.looping?(this.rewind(e-this._duration),this.update(),this.broadcastMessage("onLooped",{target:this,type:"onLooped"})):(this._time=this._duration,this.update(),this.stop(),this.broadcastMessage("onFinished",{target:this,type:"onFinished"})):e<0?(this.rewind(),this.update()):(this._time=e,this.update())},getTime:function(){return this._time},setDuration:function(e){this._duration=e===null||e<=0?1e5:e},getDuration:function(){return this._duration},setPosition:function(e){this.prevPos=this._pos,this.propFunc(e),this._pos=e,this.broadcastMessage("onChanged",{target:this,type:"onChanged"})},getPosition:function(e){return e===undefined&&(e=this._time),this.func(e,this.begin,this._change,this._duration)},setFinish:function(e){this._change=e-this.begin},getFinish:function(){return this.begin+this._change},start:function(){this.rewind(),this.startEnterFrame(),this.broadcastMessage("onStarted",{target:this,type:"onStarted"})},rewind:function(e){this.stop(),this._time=e===undefined?0:e,this.fixTime(),this.update()},fforward:function(){this._time=this._duration,this.fixTime(),this.update()},update:function(){this.setPosition(this.getPosition(this._time))},startEnterFrame:function(){this.stopEnterFrame(),this.isPlaying=!0,this.onEnterFrame()},onEnterFrame:function(){this.isPlaying&&this.nextFrame()},nextFrame:function(){this.setTime((this.getTimer()-this._startTime)/1e3)},stop:function(){this.stopEnterFrame(),this.broadcastMessage("onStopped",{target:this,type:"onStopped"})},stopEnterFrame:function(){this.isPlaying=!1},continueTo:function(e,t){this.begin=this._pos,this.setFinish(e),this._duration!==undefined&&this.setDuration(t),this.start()},resume:function(){this.fixTime(),this.startEnterFrame(),this.broadcastMessage("onResumed",{target:this,type:"onResumed"})},yoyo:function(){this.continueTo(this.begin,this._time)},addListener:function(e){return this.removeListener(e),this._listeners.push(e)},removeListener:function(e){var t=this._listeners,n=t.length;while(n--)if(t[n]==e)return t.splice(n,1),!0;return!1},broadcastMessage:function(){var e=[];for(var t=0;t0){this._runFrames();var e=this;requestAnimFrame(function(){e._animationLoop()})}else this.animRunning=!1},Kinetic.Animation._handleAnimation=function(){var e=this;this.animRunning||(this.animRunning=!0,e._animationLoop())},requestAnimFrame=function(e){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(e){window.setTimeout(e,1e3/60)}}(),Kinetic.Node=function(e){this._nodeInit(e)},Kinetic.Node.prototype={_nodeInit:function(e){this.defaultNodeAttrs={visible:!0,listening:!0,name:undefined,opacity:1,x:0,y:0,scale:{x:1,y:1},rotation:0,offset:{x:0,y:0},dragConstraint:"none",dragBounds:{},draggable:!1},this.setDefaultAttrs(this.defaultNodeAttrs),this.eventListeners={},this.transAnim=new Kinetic.Animation,this.setAttrs(e),this.on("draggableChange.kinetic",function(){this._onDraggableChange()});var t=this;this.on("idChange.kinetic",function(e){var n=t.getStage();n&&(n._removeId(e.oldVal),n._addId(t))}),this.on("nameChange.kinetic",function(e){var n=t.getStage();n&&(n._removeName(e.oldVal,t._id),n._addName(t))}),this._onDraggableChange()},on:function(e,t){var n=e.split(" ");for(var r=0;r1?o[1]:"";this.eventListeners[u]||(this.eventListeners[u]=[]),this.eventListeners[u].push({name:a,handler:t})}},off:function(e){var t=e.split(" ");for(var n=0;n1){var u=s[1];for(var a=0;a0&&s[0].getLevel()<=e&&i(s)}var e=this.getLevel(),t=this.getStage(),n=this,r=0;return n.nodeType!=="Stage"&&i(n.getStage().getChildren()),r},getLevel:function(){var e=0,t=this.parent;while(t)e++,t=t.parent;return e},setPosition:function(){var e=Kinetic.Type._getXY(Array.prototype.slice.call(arguments));this.setAttrs(e)},getPosition:function(){return{x:this.attrs.x,y:this.attrs.y}},getAbsolutePosition:function(){var e=this.getAbsoluteTransform(),t=this.getOffset();return e.translate(t.x,t.y),e.getTranslation()},setAbsolutePosition:function(){var e=Kinetic.Type._getXY(Array.prototype.slice.call(arguments)),t=this._clearTransform();this.attrs.x=t.x,this.attrs.y=t.y,delete t.x,delete t.y;var n=this.getAbsoluteTransform();n.invert(),n.translate(e.x,e.y),e={x:this.attrs.x+n.getTranslation().x,y:this.attrs.y+n.getTranslation().y},this.setPosition(e.x,e.y),this._setTransform(t)},move:function(){var e=Kinetic.Type._getXY(Array.prototype.slice.call(arguments)),t=this.getX(),n=this.getY();e.x!==undefined&&(t+=e.x),e.y!==undefined&&(n+=e.y),this.setAttrs({x:t,y:n})},getRotationDeg:function(){return this.attrs.rotation*180/Math.PI},rotate:function(e){this.setAttrs({rotation:this.getRotation()+e})},rotateDeg:function(e){this.setAttrs({rotation:this.getRotation()+e*Math.PI/180})},moveToTop:function(){var e=this.index;this.parent.children.splice(e,1),this.parent.children.push(this),this.parent._setChildrenIndices()},moveUp:function(){var e=this.index;this.parent.children.splice(e,1),this.parent.children.splice(e+1,0,this),this.parent._setChildrenIndices()},moveDown:function(){var e=this.index;e>0&&(this.parent.children.splice(e,1),this.parent.children.splice(e-1,0,this),this.parent._setChildrenIndices())},moveToBottom:function(){var e=this.index;this.parent.children.splice(e,1),this.parent.children.unshift(this),this.parent._setChildrenIndices()},setZIndex:function(e){var t=this.index;this.parent.children.splice(t,1),this.parent.children.splice(e,0,this),this.parent._setChildrenIndices()},getAbsoluteOpacity:function(){var e=1,t=this;while(t.nodeType!=="Stage")e*=t.attrs.opacity,t=t.parent;return e},isDragging:function(){var e=Kinetic.Global;return e.drag.node&&e.drag.node._id===this._id&&e.drag.moving},moveTo:function(e){var t=this.parent;t.children.splice(this.index,1),t._setChildrenIndices(),e.children.push(this),this.index=e.children.length-1,this.parent=e,e._setChildrenIndices()},getParent:function(){return this.parent},getLayer:function(){return this.nodeType==="Layer"?this:this.getParent().getLayer()},getStage:function(){return this.nodeType!=="Stage"&&this.getParent()?this.getParent().getStage():this.nodeType==="Stage"?this:undefined},simulate:function(e){this._handleEvent(e,{})},transitionTo:function(e){var t=this.nodeType==="Stage"?this:this.getLayer(),n=this,r=new Kinetic.Transition(this,e);return this.transAnim.func=function(){r._onEnterFrame()},this.transAnim.node=t,r.onFinished=function(){n.transAnim.stop(),n.transAnim.node.draw(),e.callback&&e.callback()},r.start(),this.transAnim.start(),r},getAbsoluteTransform:function(){var e=new Kinetic.Transform,t=[],n=this.parent;t.unshift(this);while(n)t.unshift(n),n=n.parent;for(var r=0;r=0&&!t.cancelBubble&&this.parent&&(n&&n.parent?this._handleEvent.call(this.parent,e,t,n.parent):this._handleEvent.call(this.parent,e,t))}},_draw:function(e){if(this.isVisible()&&(!e||e.name!=="buffer"||this.getListening())){this.__draw&&this.__draw(e);var t=this.children;if(t)for(var n=0;n0)this.remove(this.children[0])},add:function(e){e._id=Kinetic.Global.idCounter++,e.index=this.children.length,e.parent=this,this.children.push(e);var t=e.getStage();if(!t)Kinetic.Global._addTempNode(e);else{t._addId(e),t._addName(e);var n=Kinetic.Global;n._pullNodes(t)}return this._add!==undefined&&this._add(e),this},remove:function(e){if(e&&e.index!==undefined&&this.children[e.index]._id==e._id){var t=this.getStage();t&&(t._removeId(e.getId()),t._removeName(e.getName(),e._id)),Kinetic.Global._removeTempNode(e),this.children.splice(e.index,1),this._setChildrenIndices();while(e.children&&e.children.length>0)e.remove(e.children[0]);this._remove!==undefined&&this._remove(e)}return this},get:function(e){var t=this.getStage(),n,r=e.slice(1);if(e.charAt(0)==="#")n=t.ids[r]!==undefined?[t.ids[r]]:[];else{if(e.charAt(0)!==".")return e==="Shape"||e==="Group"||e==="Layer"?this._getNodes(e):!1;n=t.names[r]!==undefined?t.names[r]:[]}var i=[];for(var s=0;s=0;r--){var i=n[r],s=i.bufferCanvas.context.getImageData(e.x,e.y,1,1).data;if(s[3]===255){var o=Kinetic.Type._rgbToHex(s[0],s[1],s[2]);t=Kinetic.Global.shapes[o];var u=Kinetic.Global.drag.moving;return{shape:t,pixel:s}}if(s[0]>0||s[1]>0||s[2]>0||s[3]>0)return{pixel:s}}return null},_resizeDOM:function(){var e=this.attrs.width,t=this.attrs.height;this.content.style.width=e+"px",this.content.style.height=t+"px",this.bufferCanvas.setSize(e,t);var n=this.children;for(var r=0;ro.right&&(f.x=o.right),o.top!==undefined&&f.yo.bottom&&(f.y=o.bottom),u==="horizontal"?f.y=a.y:u==="vertical"&&(f.x=a.x),r.setAbsolutePosition(f),n.drag.moving||(n.drag.moving=!0,n.drag.node._handleEvent("dragstart",e)),n.drag.node._handleEvent("dragmove",e)}},_buildDOM:function(){this.content=document.createElement("div"),this.content.style.position="relative",this.content.style.display="inline-block",this.content.className="kineticjs-content",this.attrs.container.appendChild(this.content),this.bufferCanvas=new Kinetic.Canvas({width:this.attrs.width,height:this.attrs.height}),this._resizeDOM()},_addId:function(e){e.attrs.id!==undefined&&(this.ids[e.attrs.id]=e)},_removeId:function(e){e!==undefined&&delete this.ids[e]},_addName:function(e){var t=e.attrs.name;t!==undefined&&(this.names[t]===undefined&&(this.names[t]=[]),this.names[t].push(e))},_removeName:function(e,t){if(e!==undefined){var n=this.names[e];if(n!==undefined){for(var r=0;r0)},__draw:function(e){if(this.attrs.drawFunc){var t=this.getStage(),n=e.getContext(),r=[],i=this.parent;r.unshift(this);while(i)r.unshift(i),i=i.parent;n.save();for(var s=0;s0&&r&&(this.attrs.height==="auto"||i*(n+1)this.attrs.width-this.attrs.padding*2){if(s==0)break;var a=u.lastIndexOf(" "),f=u.lastIndexOf("-"),l=Math.max(a,f);if(l>=0){o=e.splice(0,1+l).join("");break}o=e.splice(0,s).join("");break}s++,s===e.length&&(o=e.splice(0,s).join(""))}this.textWidth=Math.max(this.textWidth,this._getTextSize(o).width),o!==undefined&&(t.push(o),r=!0),n++}this.textArr=t}},Kinetic.Global.extend(Kinetic.Text,Kinetic.Shape),Kinetic.Node.addGettersSetters(Kinetic.Text,["fontFamily","fontSize","fontStyle","textFill","textStroke","textStrokeWidth","padding","align","lineHeight","text","width","height","cornerRadius","fill","stroke","strokeWidth","shadow"]),Kinetic.Line=function(e){this._initLine(e)},Kinetic.Line.prototype={_initLine:function(e){this.setDefaultAttrs({points:[],lineCap:"butt",dashArray:[],detectionType:"pixel"}),this.shapeType="Line",e.drawFunc=this.drawFunc,Kinetic.Shape.call(this,e)},drawFunc:function(e){var t={};e.beginPath(),e.moveTo(this.attrs.points[0].x,this.attrs.points[0].y);for(var n=1;n0){var s=this.attrs.points[n-1].x,o=this.attrs.points[n-1].y;this._dashedLine(e,s,o,r,i,this.attrs.dashArray)}else e.lineTo(r,i)}!this.attrs.lineCap||(e.lineCap=this.attrs.lineCap),this.stroke(e)},_dashedLine:function(e,t,n,r,i,s){var o=s.length,u=r-t,a=i-n,f=u>a,l=f?a/u:u/a;l>9999?l=9999:l<-9999&&(l=-9999);var c=Math.sqrt(u*u+a*a),h=0,p=!0;while(c>=.1&&h<1e4){var d=s[h++%o];d===0&&(d=.001),d>c&&(d=c);var v=Math.sqrt(d*d/(1+l*l));f?(t+=u<0&&a<0?v*-1:v,n+=u<0&&a<0?l*v*-1:l*v):(t+=u<0&&a<0?l*v*-1:l*v,n+=u<0&&a<0?v*-1:v),e[p?"lineTo":"moveTo"](t,n),c-=d,p=!p}e.moveTo(r,i)}},Kinetic.Global.extend(Kinetic.Line,Kinetic.Shape),Kinetic.Node.addGettersSetters(Kinetic.Line,["dashArray","lineCap","points"]),Kinetic.Sprite=function(e){this._initSprite(e)},Kinetic.Sprite.prototype={_initSprite:function(e){this.setDefaultAttrs({index:0,frameRate:17}),e.drawFunc=this.drawFunc,Kinetic.Shape.call(this,e),this.anim=new Kinetic.Animation;var t=this;this.on("animationChange.kinetic",function(){t.setIndex(0)})},drawFunc:function(e){var t=this.attrs.animation,n=this.attrs.index,r=this.attrs.animations[t][n];e.beginPath(),e.rect(0,0,r.width,r.height),e.closePath(),this.fill(e),this.stroke(e),this.attrs.image&&(e.beginPath(),e.rect(0,0,r.width,r.height),e.closePath(),this.drawImage(e,this.attrs.image,r.x,r.y,r.width,r.height,0,0,r.width,r.height))},start:function(){var e=this,t=this.getLayer();this.anim.node=t,this.interval=setInterval(function(){var t=e.attrs.index;e._updateIndex(),e.afterFrameFunc&&t===e.afterFrameIndex&&e.afterFrameFunc()},1e3/this.attrs.frameRate),this.anim.start()},stop:function(){this.anim.stop(),clearInterval(this.interval)},afterFrame:function(e,t){this.afterFrameIndex=e,this.afterFrameFunc=t},_updateIndex:function(){var e=this.attrs.index,t=this.attrs.animation;ea?u:a,d=u>a?1:u/a,v=u>a?a/u:1;e.translate(s,o),e.rotate(c),e.scale(d,v),e.arc(0,0,p,f,f+l,1-h),e.scale(1/d,1/v),e.rotate(-c),e.translate(-s,-o);break;case"z":e.closePath()}}this.fill(e),this.stroke(e)}},Kinetic.Global.extend(Kinetic.Path,Kinetic.Shape),Kinetic.Path.getLineLength=function(e,t,n,r){return Math.sqrt((n-e)*(n-e)+(r-t)*(r-t))},Kinetic.Path.getPointOnLine=function(e,t,n,r,i,s,o){s===undefined&&(s=t),o===undefined&&(o=n);var u=(i-n)/(r-t+1e-8),a=Math.sqrt(e*e/(1+u*u)),f=u*a,l;if((o-n)/(s-t+1e-8)===u)l={x:s+a,y:o+f};else{var c,h,p=this.getLineLength(t,n,r,i);if(p<1e-8)return undefined;var d=(s-t)*(r-t)+(o-n)*(i-n);d/=p*p,c=t+d*(r-t),h=n+d*(i-n);var v=this.getLineLength(s,o,c,h),m=Math.sqrt(e*e-v*v);a=Math.sqrt(m*m/(1+u*u)),f=u*a,l={x:c+a,y:h+f}}return l},Kinetic.Path.getPointOnCubicBezier=function(e,t,n,r,i,s,o,u,a){function f(e){return e*e*e}function l(e){return 3*e*e*(1-e)}function c(e){return 3*e*(1-e)*(1-e)}function h(e){return(1-e)*(1-e)*(1-e)}var p=u*f(e)+s*l(e)+r*c(e)+t*h(e),d=a*f(e)+o*l(e)+i*c(e)+n*h(e);return{x:p,y:d}},Kinetic.Path.getPointOnQuadraticBezier=function(e,t,n,r,i,s,o){function u(e){return e*e}function a(e){return 2*e*(1-e)}function f(e){return(1-e)*(1-e)}var l=s*u(e)+r*a(e)+t*f(e),c=o*u(e)+i*a(e)+n*f(e);return{x:l,y:c}},Kinetic.Path.getPointOnEllipticalArc=function(e,t,n,r,i,s){var o=Math.cos(s),u=Math.sin(s),a={x:n*Math.cos(i),y:r*Math.sin(i)};return{x:e+(a.x*o-a.y*u),y:t+(a.x*u+a.y*o)}},Kinetic.Path.parsePathData=function(e){if(!e)return[];var t=e,n=["m","M","l","L","v","V","h","H","z","Z","c","C","q","Q","t","T","s","S","a","A"];t=t.replace(new RegExp(" ","g"),",");for(var r=0;r0&&l[0]===""&&l.shift();for(var c=0;c0){if(isNaN(l[0]))break;var h=null,p=[],d=o,v=u;switch(f){case"l":o+=l.shift(),u+=l.shift(),h="L",p.push(o,u);break;case"L":o=l.shift(),u=l.shift(),p.push(o,u);break;case"m":o+=l.shift(),u+=l.shift(),h="M",p.push(o,u),f="l";break;case"M":o=l.shift(),u=l.shift(),h="M",p.push(o,u),f="L";break;case"h":o+=l.shift(),h="L",p.push(o,u);break;case"H":o=l.shift(),h="L",p.push(o,u);break;case"v":u+=l.shift(),h="L",p.push(o,u);break;case"V":u=l.shift(),h="L",p.push(o,u);break;case"C":p.push(l.shift(),l.shift(),l.shift(),l.shift()),o=l.shift(),u=l.shift(),p.push(o,u);break;case"c":p.push(o+l.shift(),u+l.shift(),o+l.shift(),u+l.shift()),o+=l.shift(),u+=l.shift(),h="C",p.push(o,u);break;case"S":var m=o,g=u,y=s[s.length-1];y.command==="C"&&(m=o+(o-y.points[2]),g=u+(u-y.points[3])),p.push(m,g,l.shift(),l.shift()),o=l.shift(),u=l.shift(),h="C",p.push(o,u);break;case"s":var m=o,g=u,y=s[s.length-1];y.command==="C"&&(m=o+(o-y.points[2]),g=u+(u-y.points[3])),p.push(m,g,o+l.shift(),u+l.shift()),o+=l.shift(),u+=l.shift(),h="C",p.push(o,u);break;case"Q":p.push(l.shift(),l.shift()),o=l.shift(),u=l.shift(),p.push(o,u);break;case"q":p.push(o+l.shift(),u+l.shift()),o+=l.shift(),u+=l.shift(),h="Q",p.push(o,u);break;case"T":var m=o,g=u,y=s[s.length-1];y.command==="Q"&&(m=o+(o-y.points[0]),g=u+(u-y.points[1])),o=l.shift(),u=l.shift(),h="Q",p.push(m,g,o,u);break;case"t":var m=o,g=u,y=s[s.length-1];y.command==="Q"&&(m=o+(o-y.points[0]),g=u+(u-y.points[1])),o+=l.shift(),u+=l.shift(),h="Q",p.push(m,g,o,u);break;case"A":var b=l.shift(),w=l.shift(),E=l.shift(),S=l.shift(),x=l.shift(),T=o,N=u;o=l.shift(),u=l.shift(),h="A",p=this.convertEndpointToCenterParameterization(T,N,o,u,S,x,b,w,E);break;case"a":var b=l.shift(),w=l.shift(),E=l.shift(),S=l.shift(),x=l.shift(),T=o,N=u;o+=l.shift(),u+=l.shift(),h="A",p=this.convertEndpointToCenterParameterization(T,N,o,u,S,x,b,w,E)}s.push({command:h||f,points:p,start:{x:d,y:v},pathLength:this.calcLength(d,v,h||f,p)})}(f==="z"||f==="Z")&&s.push({command:"z",points:[],start:undefined,pathLength:0})}return s},Kinetic.Path.calcLength=function(e,n,r,i){var s,o,u,a=Kinetic.Path;switch(r){case"L":return a.getLineLength(e,n,i[0],i[1]);case"C":s=0,o=a.getPointOnCubicBezier(0,e,n,i[0],i[1],i[2],i[3],i[4],i[5]);for(t=.01;t<=1;t+=.01)u=a.getPointOnCubicBezier(t,e,n,i[0],i[1],i[2],i[3],i[4],i[5]),s+=a.getLineLength(o.x,o.y,u.x,u.y),o=u;return s;case"Q":s=0,o=a.getPointOnQuadraticBezier(0,e,n,i[0],i[1],i[2],i[3]);for(t=.01;t<=1;t+=.01)u=a.getPointOnQuadraticBezier(t,e,n,i[0],i[1],i[2],i[3]),s+=a.getLineLength(o.x,o.y,u.x,u.y),o=u;return s;case"A":s=0;var f=i[4],l=i[5],c=i[4]+l,h=Math.PI/180;Math.abs(f-c)c;t-=h)u=a.getPointOnEllipticalArc(i[0],i[1],i[2],i[3],t,0),s+=a.getLineLength(o.x,o.y,u.x,u.y),o=u;else for(t=f+h;t1&&(o*=Math.sqrt(h),u*=Math.sqrt(h));var p=Math.sqrt((o*o*u*u-o*o*c*c-u*u*l*l)/(o*o*c*c+u*u*l*l));i==s&&(p*=-1),isNaN(p)&&(p=0);var d=p*o*c/u,v=p*-u*l/o,m=(e+n)/2+Math.cos(f)*d-Math.sin(f)*v,g=(t+r)/2+Math.sin(f)*d+Math.cos(f)*v,y=function(e){return Math.sqrt(e[0]*e[0]+e[1]*e[1])},b=function(e,t){return(e[0]*t[0]+e[1]*t[1])/(y(e)*y(t))},w=function(e,t){return(e[0]*t[1]=1&&(T=0),s===0&&T>0&&(T-=2*Math.PI),s==1&&T<0&&(T+=2*Math.PI),[m,g,o,u,E,T,f,s]},Kinetic.Node.addGettersSetters(Kinetic.Path,["data"]),Kinetic.TextPath=function(e){this._initTextPath(e)},Kinetic.TextPath.prototype={_initTextPath:function(e){this.setDefaultAttrs({fontFamily:"Calibri",fontSize:12,fontStyle:"normal",detectionType:"path",text:""}),this.dummyCanvas=document.createElement("canvas"),this.shapeType="TextPath",this.dataArray=[];var t=this;e.drawFunc=this.drawFunc,Kinetic.Shape.call(this,e),this.dataArray=Kinetic.Path.parsePathData(this.attrs.data),this.on("dataChange",function(){t.dataArray=Kinetic.Path.parsePathData(this.attrs.data)});var n=["text","textStroke","textStrokeWidth"];for(var r=0;r0)return o=n,t[n];t[n].command=="M"&&(r={x:t[n].points[0],y:t[n].points[1]})}return{}},f=function(t,n){var o=e._getTextSize(t).width,f=0,l=0,c=!1;i=undefined;while(Math.abs(o-f)/o>.01&&l<25){l++;var h=f;while(s===undefined)s=a(),s&&h+s.pathLengtho?i=Kinetic.Path.getPointOnLine(o,r.x,r.y,s.points[0],s.points[1],r.x,r.y):s=undefined;break;case"A":var d=s.points[4],v=s.points[5],m=s.points[4]+v;u===0?u=d+1e-8:o>f?u+=Math.PI/180*v/Math.abs(v):u-=Math.PI/360*v/Math.abs(v),Math.abs(u)>Math.abs(m)&&(u=m,p=!0),i=Kinetic.Path.getPointOnEllipticalArc(s.points[0],s.points[1],s.points[2],s.points[3],u,s.points[6]);break;case"C":u===0?o>s.pathLength?u=1e-8:u=o/s.pathLength:o>f?u+=(o-f)/s.pathLength:u-=(f-o)/s.pathLength,u>1&&(u=1,p=!0),i=Kinetic.Path.getPointOnCubicBezier(u,s.start.x,s.start.y,s.points[0],s.points[1],s.points[2],s.points[3],s.points[4],s.points[5]);break;case"Q":u===0?u=o/s.pathLength:o>f?u+=(o-f)/s.pathLength:u-=(f-o)/s.pathLength,u>1&&(u=1,p=!0),i=Kinetic.Path.getPointOnQuadraticBezier(u,s.start.x,s.start.y,s.points[0],s.points[1],s.points[2],s.points[3])}i!==undefined&&(f=Kinetic.Path.getLineLength(r.x,r.y,i.x,i.y)),p&&(p=!1,s=undefined)}};for(var l=0;l= 0 && !evt.cancelBubble && this.parent) { - this._handleEvent.call(this.parent, eventType, evt); + if(compareShape && compareShape.parent) { + this._handleEvent.call(this.parent, eventType, evt, compareShape.parent); + } + else { + this._handleEvent.call(this.parent, eventType, evt); + } } } - } -}); + }, + _draw: function(canvas) { + if(this.isVisible() && (!canvas || canvas.name !== 'buffer' || this.getListening())) { + if(this.__draw) { + this.__draw(canvas); + } + + var children = this.children; + if(children) { + for(var n = 0; n < children.length; n++) { + var child = children[n]; + if(child.draw) { + child.draw(canvas); + } + else { + child._draw(canvas); + } + } + } + } + }, +}; // add getter and setter methods Kinetic.Node.addSetters = function(constructor, arr) { @@ -1080,7 +1050,7 @@ Kinetic.Node._addGetter = function(constructor, attr) { }; }; // add getters setters -Kinetic.Node.addGettersSetters(Kinetic.Node, ['x', 'y', 'scale', 'detectionType', 'rotation', 'alpha', 'name', 'id', 'offset', 'draggable', 'dragConstraint', 'dragBounds', 'dragBoundFunc', 'listening']); +Kinetic.Node.addGettersSetters(Kinetic.Node, ['x', 'y', 'scale', 'rotation', 'opacity', 'name', 'id', 'offset', 'draggable', 'dragConstraint', 'dragBounds', 'dragBoundFunc', 'listening']); Kinetic.Node.addSetters(Kinetic.Node, ['rotationDeg']); /** @@ -1097,13 +1067,6 @@ Kinetic.Node.addSetters(Kinetic.Node, ['rotationDeg']); * @param {Number} y */ -/** - * set detection type - * @name setDetectionType - * @methodOf Kinetic.Node.prototype - * @param {String} type can be path or pixel - */ - /** * set node rotation in radians * @name setRotation @@ -1112,12 +1075,12 @@ Kinetic.Node.addSetters(Kinetic.Node, ['rotationDeg']); */ /** - * set alpha. Alpha values range from 0 to 1. - * A node with an alpha of 0 is fully transparent, and a node - * with an alpha of 1 is fully opaque - * @name setAlpha + * 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 setOpacity * @methodOf Kinetic.Node.prototype - * @param {Object} alpha + * @param {Object} opacity */ /** @@ -1193,12 +1156,6 @@ Kinetic.Node.addSetters(Kinetic.Node, ['rotationDeg']); * @methodOf Kinetic.Node.prototype */ -/** - * get detection type. Can be path or pixel - * @name getDetectionType - * @methodOf Kinetic.Node.prototype - */ - /** * get rotation in radians * @name getRotation @@ -1206,8 +1163,8 @@ Kinetic.Node.addSetters(Kinetic.Node, ['rotationDeg']); */ /** - * get alpha. - * @name getAlpha + * get opacity. + * @name getOpacity * @methodOf Kinetic.Node.prototype */ diff --git a/src/Shape.js b/src/Shape.js index fff40eb2..1d0008bd 100644 --- a/src/Shape.js +++ b/src/Shape.js @@ -33,17 +33,15 @@ * @config {Obect} [config.shadow.blur.offset] * @config {Number} [config.shadow.blur.offset.x] * @config {Number} [config.shadow.blur.offset.y] - * @config {Number} [config.shadow.alpha] shadow alpha. Can be any real number + * @config {Number} [config.shadow.opacity] shadow opacity. Can be any real number * between 0 and 1 - * @config {String} [config.detectionType] shape detection type. Can be path or pixel. - * The default is path because it performs better * @param {Number} [config.x] * @param {Number} [config.y] * @param {Boolean} [config.visible] * @param {Boolean} [config.listening] whether or not the node is listening for events * @param {String} [config.id] unique id * @param {String} [config.name] non-unique name - * @param {Number} [config.alpha] determines node opacity. Can be any number between 0 and 1 + * @param {Number} [config.opacity] determines node opacity. Can be any number between 0 and 1 * @param {Object} [config.scale] * @param {Number} [config.scale.x] * @param {Number} [config.scale.y] @@ -61,17 +59,29 @@ * @param {Number} [config.dragBounds.bottom] * @param {Number} [config.dragBounds.left] */ -Kinetic.Shape = Kinetic.Node.extend({ - init: function(config) { - this.setDefaultAttrs({ - detectionType: 'path' - }); +Kinetic.Shape = function(config) { + this._initShape(config); +}; +Kinetic.Shape.prototype = { + _initShape: function(config) { this.nodeType = 'Shape'; this.appliedShadow = false; + // set colorKey + var shapes = Kinetic.Global.shapes; + var key; + while(true) { + key = Kinetic.Type._getRandomColorKey(); + if(key && !( key in shapes)) { + break; + } + } + this.colorKey = key; + shapes[key] = this; + // call super constructor - this._super(config); + Kinetic.Node.call(this, config); }, /** * get canvas context tied to the layer @@ -287,7 +297,7 @@ Kinetic.Shape = Kinetic.Node.extend({ _applyShadow: function(context) { var s = this.attrs.shadow; if(s) { - var aa = this.getAbsoluteAlpha(); + var aa = this.getAbsoluteOpacity(); // defaults var color = s.color ? s.color : 'black'; var blur = s.blur ? s.blur : 5; @@ -296,8 +306,8 @@ Kinetic.Shape = Kinetic.Node.extend({ y: 0 }; - if(s.alpha) { - context.globalAlpha = s.alpha * aa; + if(s.opacity) { + context.globalAlpha = s.opacity * aa; } context.shadowColor = color; context.shadowBlur = blur; @@ -319,28 +329,13 @@ Kinetic.Shape = Kinetic.Node.extend({ intersects: function() { var pos = Kinetic.Type._getXY(Array.prototype.slice.call(arguments)); var stage = this.getStage(); - - // path detection - if(this.attrs.detectionType === 'path') { - var pathCanvas = stage.pathCanvas; - var pathCanvasContext = pathCanvas.getContext(); - - this._draw(pathCanvas); - - return pathCanvasContext.isPointInPath(pos.x, pos.y); - } - - // pixel detection - if(this.imageData) { - var w = stage.attrs.width; - var alpha = this.imageData.data[((w * pos.y) + pos.x) * 4 + 3]; - return (alpha); - } - - // default - return false; + var bufferCanvas = stage.bufferCanvas; + bufferCanvas.clear(); + this._draw(bufferCanvas); + var obj = stage.getIntersection(pos); + return !!(obj && obj.pixel[3] > 0); }, - _draw: function(canvas) { + __draw: function(canvas) { if(this.attrs.drawFunc) { var stage = this.getStage(); var context = canvas.getContext(); @@ -362,21 +357,70 @@ Kinetic.Shape = Kinetic.Node.extend({ } /* - * pre styles include alpha, linejoin + * pre styles include opacity, linejoin */ - var absAlpha = this.getAbsoluteAlpha(); - if(absAlpha !== 1) { - context.globalAlpha = absAlpha; + var absOpacity = this.getAbsoluteOpacity(); + if(absOpacity !== 1) { + context.globalAlpha = absOpacity; } this.applyLineJoin(context); // draw the shape this.appliedShadow = false; + + var wl = Kinetic.Global.BUFFER_WHITELIST; + var bl = Kinetic.Global.BUFFER_BLACKLIST; + var attrs = {}; + + if(canvas.name === 'buffer') { + for(var n = 0; n < wl.length; n++) { + var key = wl[n]; + attrs[key] = this.attrs[key]; + if(this.attrs[key] || (key === 'fill' && !this.attrs.stroke && !('image' in this.attrs))) { + this.attrs[key] = '#' + this.colorKey; + } + } + + for(var n = 0; n < bl.length; n++) { + var key = bl[n]; + attrs[key] = this.attrs[key]; + this.attrs[key] = ''; + } + + // image is a special case + if('image' in this.attrs) { + attrs.image = this.attrs.image; + + if(this.imageBuffer) { + this.attrs.image = this.imageBuffer; + } + else { + this.attrs.image = null; + this.attrs.fill = '#' + this.colorKey; + } + } + + context.globalAlpha = 1; + } + this.attrs.drawFunc.call(this, canvas.getContext()); + + if(canvas.name === 'buffer') { + var bothLists = wl.concat(bl); + for(var n = 0; n < bothLists.length; n++) { + var key = bothLists[n]; + this.attrs[key] = attrs[key]; + } + + // image is a special case + this.attrs.image = attrs.image; + } + context.restore(); } } -}); +}; +Kinetic.Global.extend(Kinetic.Shape, Kinetic.Node); // add getters and setters Kinetic.Node.addGettersSetters(Kinetic.Shape, ['fill', 'stroke', 'lineJoin', 'strokeWidth', 'shadow', 'drawFunc', 'filter']); diff --git a/src/Stage.js b/src/Stage.js index 85d5f717..7248bd28 100644 --- a/src/Stage.js +++ b/src/Stage.js @@ -15,7 +15,7 @@ * @param {Boolean} [config.listening] whether or not the node is listening for events * @param {String} [config.id] unique id * @param {String} [config.name] non-unique name - * @param {Number} [config.alpha] determines node opacity. Can be any number between 0 and 1 + * @param {Number} [config.opacity] determines node opacity. Can be any number between 0 and 1 * @param {Object} [config.scale] * @param {Number} [config.scale.x] * @param {Number} [config.scale.y] @@ -33,8 +33,12 @@ * @param {Number} [config.dragBounds.bottom] * @param {Number} [config.dragBounds.left] */ -Kinetic.Stage = Kinetic.Container.extend({ - init: function(config) { +Kinetic.Stage = function(config) { + this._initStage(config); +}; + +Kinetic.Stage.prototype = { + _initStage: function(config) { this.setDefaultAttrs({ width: 400, height: 200 @@ -49,7 +53,7 @@ Kinetic.Stage = Kinetic.Container.extend({ } // call super constructor - this._super(config); + Kinetic.Container.call(this, config); this._setStageDefaultProperties(); this._id = Kinetic.Global.idCounter++; @@ -75,8 +79,8 @@ Kinetic.Stage = Kinetic.Container.extend({ * @name draw * @methodOf Kinetic.Stage.prototype */ - draw: function(canvas) { - this._draw(canvas); + draw: function() { + this._draw(); }, /** * set stage size @@ -344,6 +348,44 @@ Kinetic.Stage = Kinetic.Container.extend({ } }); }, + /** + * get intersection object that contains shape and pixel data + * @name getIntersection + * @methodOf Kinetic.Stage.prototype + * @param {Object} pos point object + */ + getIntersection: function(pos) { + var shape; + var layers = this.getChildren(); + + /* + * traverse through layers from top to bottom and look + * for hit detection + */ + for(var n = layers.length - 1; n >= 0; n--) { + var layer = layers[n]; + var p = layer.bufferCanvas.context.getImageData(pos.x, pos.y, 1, 1).data; + // this indicates that a buffer pixel may have been found + if(p[3] === 255) { + var colorKey = Kinetic.Type._rgbToHex(p[0], p[1], p[2]); + shape = Kinetic.Global.shapes[colorKey]; + var isDragging = Kinetic.Global.drag.moving; + + return { + shape: shape, + pixel: p + }; + } + // if no shape mapped to that pixel, return pixel array + else if(p[0] > 0 || p[1] > 0 || p[2] > 0 || p[3] > 0) { + return { + pixel: p + }; + } + } + + return null; + }, _resizeDOM: function() { var width = this.attrs.width; var height = this.attrs.height; @@ -352,15 +394,13 @@ Kinetic.Stage = Kinetic.Container.extend({ this.content.style.width = width + 'px'; this.content.style.height = height + 'px'; - // set buffer canvas and path canvas sizes this.bufferCanvas.setSize(width, height); - this.pathCanvas.setSize(width, height); - // set user defined layer dimensions var layers = this.children; for(var n = 0; n < layers.length; n++) { var layer = layers[n]; layer.getCanvas().setSize(width, height); + layer.bufferCanvas.setSize(width, height); layer.draw(); } }, @@ -385,234 +425,18 @@ Kinetic.Stage = Kinetic.Container.extend({ */ _add: function(layer) { layer.canvas.setSize(this.attrs.width, this.attrs.height); + layer.bufferCanvas.setSize(this.attrs.width, this.attrs.height); // draw layer and append canvas to container layer.draw(); this.content.appendChild(layer.canvas.element); - - /* - * set layer last draw time to zero - * so that throttling doesn't take into account - * the layer draws associated with adding a node - */ - layer.lastDrawTime = 0; }, - /** - * detect event - * @param {Shape} shape - */ - _detectEvent: function(shape, evt) { - var isDragging = Kinetic.Global.drag.moving; - var go = Kinetic.Global; - var pos = this.getUserPosition(); - var el = shape.eventListeners; - var that = this; - - if(this.targetShape && shape._id === this.targetShape._id) { - this.targetFound = true; - } - - if(shape.isVisible() && pos !== undefined && shape.intersects(pos)) { - // handle onmousedown - if(!isDragging && this.mouseDown) { - this.mouseDown = false; - this.clickStart = true; - shape._handleEvent('mousedown', evt); - return true; - } - // handle onmouseup & onclick - else if(this.mouseUp) { - this.mouseUp = false; - shape._handleEvent('mouseup', evt); - - // detect if click or double click occurred - if(this.clickStart) { - /* - * if dragging and dropping, don't fire click or dbl click - * event - */ - if((!go.drag.moving) || !go.drag.node) { - shape._handleEvent('click', evt); - - if(this.inDoubleClickWindow) { - shape._handleEvent('dblclick', evt); - } - this.inDoubleClickWindow = true; - setTimeout(function() { - that.inDoubleClickWindow = false; - }, this.dblClickWindow); - } - } - return true; - } - - // handle touchstart - else if(!isDragging && this.touchStart && !this.touchMove) { - this.touchStart = false; - this.tapStart = true; - shape._handleEvent('touchstart', evt); - return true; - } - // handle touchend & tap - else if(this.touchEnd) { - this.touchEnd = false; - shape._handleEvent('touchend', evt); - - // detect if tap or double tap occurred - if(this.tapStart) { - /* - * if dragging and dropping, don't fire tap or dbltap - * event - */ - if((!go.drag.moving) || !go.drag.node) { - shape._handleEvent('tap', evt); - - if(this.inDoubleClickWindow) { - shape._handleEvent('dbltap', evt); - } - this.inDoubleClickWindow = true; - setTimeout(function() { - that.inDoubleClickWindow = false; - }, this.dblClickWindow); - } - } - return true; - } - else if(!isDragging && this.touchMove) { - shape._handleEvent('touchmove', evt); - return true; - } - /* - * NOTE: these event handlers require target shape - * handling - */ - // handle onmouseover - else if(!isDragging && this._isNewTarget(shape, evt)) { - /* - * check to see if there are stored mouseout events first. - * if there are, run those before running the onmouseover - * events - */ - if(this.mouseoutShape) { - this.mouseoverShape = shape; - this.mouseoutShape._handleEvent('mouseout', evt); - this.mouseoverShape = undefined; - } - - shape._handleEvent('mouseover', evt); - this._setTarget(shape); - return true; - } - // handle mousemove and touchmove - else { - if(!isDragging && this.mouseMove) { - shape._handleEvent('mousemove', evt); - return true; - } - } - - } - // handle mouseout condition - else if(!isDragging && this.targetShape && this.targetShape._id === shape._id) { - this._setTarget(undefined); - this.mouseoutShape = shape; - return true; - } - - return false; - }, - /** - * set new target - */ - _setTarget: function(shape) { - this.targetShape = shape; - this.targetFound = true; - }, - /** - * check if shape should be a new target - */ - _isNewTarget: function(shape, evt) { - if(!this.targetShape || (!this.targetFound && shape._id !== this.targetShape._id)) { - /* - * check if old target has an onmouseout event listener - */ - if(this.targetShape) { - var oldEl = this.targetShape.eventListeners; - if(oldEl) { - this.mouseoutShape = this.targetShape; - } - } - return true; - } - else { - return false; - } - }, - /** - * traverse container children - * @param {Container} obj - */ - _traverseChildren: function(obj, evt) { - var children = obj.children; - // propapgate backwards through children - for(var i = children.length - 1; i >= 0; i--) { - var child = children[i]; - if(child.getListening()) { - if(child.nodeType === 'Shape') { - var exit = this._detectEvent(child, evt); - if(exit) { - return true; - } - } - else { - var exit = this._traverseChildren(child, evt); - if(exit) { - return true; - } - } - } - } - - return false; - }, - /** - * handle incoming event - * @param {Event} evt - */ - _handleStageEvent: function(evt) { - var go = Kinetic.Global; + _setUserPosition: function(evt) { if(!evt) { evt = window.event; } - this._setMousePosition(evt); this._setTouchPosition(evt); - this.pathCanvas.clear(); - - /* - * loop through layers. If at any point an event - * is triggered, break out - */ - this.targetFound = false; - var shapeDetected = false; - for(var n = this.children.length - 1; n >= 0; n--) { - var layer = this.children[n]; - if(layer.isVisible() && n >= 0 && layer.getListening()) { - if(this._traverseChildren(layer, evt)) { - shapeDetected = true; - break; - } - } - } - - /* - * if no shape was detected and a mouseout shape has been stored, - * then run the onmouseout event handlers - */ - if(!shapeDetected && this.mouseoutShape) { - this.mouseoutShape._handleEvent('mouseout', evt); - this.mouseoutShape = undefined; - } }, /** * begin listening for events by adding event handlers @@ -621,8 +445,7 @@ Kinetic.Stage = Kinetic.Container.extend({ _bindContentEvents: function() { var go = Kinetic.Global; var that = this; - - var events = ['mousedown', 'mousemove', 'mouseup', 'mouseover', 'mouseout', 'touchstart', 'touchmove', 'touchend']; + var events = ['mousedown', 'mousemove', 'mouseup', 'mouseout', 'touchstart', 'touchmove', 'touchend']; for(var n = 0; n < events.length; n++) { var pubEvent = events[n]; @@ -635,15 +458,14 @@ Kinetic.Stage = Kinetic.Container.extend({ }()); } }, - _mouseover: function(evt) { - this._handleStageEvent(evt); - }, _mouseout: function(evt) { + this._setUserPosition(evt); + var go = Kinetic.Global; // if there's a current target shape, run mouseout handlers var targetShape = this.targetShape; - if(targetShape) { + if(targetShape && !go.drag.moving) { targetShape._handleEvent('mouseout', evt); - this.targetShape = undefined; + this.targetShape = null; } this.mousePos = undefined; @@ -651,19 +473,45 @@ Kinetic.Stage = Kinetic.Container.extend({ this._endDrag(evt); }, _mousemove: function(evt) { - this.mouseDown = false; - this.mouseUp = false; - this.mouseMove = true; - this._handleStageEvent(evt); + this._setUserPosition(evt); + var go = Kinetic.Global; + var obj = this.getIntersection(this.getUserPosition()); + + if(obj) { + var shape = obj.shape; + if(shape) { + if(!go.drag.moving && obj.pixel[3] === 255 && (!this.targetShape || this.targetShape._id !== shape._id)) { + if(this.targetShape) { + this.targetShape._handleEvent('mouseout', evt, shape); + } + shape._handleEvent('mouseover', evt, this.targetShape); + this.targetShape = shape; + } + else { + shape._handleEvent('mousemove', evt); + } + } + } + /* + * if no shape was detected, clear target shape and try + * to run mouseout from previous target shape + */ + else if(this.targetShape && !go.drag.moving) { + this.targetShape._handleEvent('mouseout', evt); + this.targetShape = null; + } // start drag and drop this._startDrag(evt); }, _mousedown: function(evt) { - this.mouseDown = true; - this.mouseUp = false; - this.mouseMove = false; - this._handleStageEvent(evt); + this._setUserPosition(evt); + var obj = this.getIntersection(this.getUserPosition()); + if(obj && obj.shape) { + var shape = obj.shape; + this.clickStart = true; + shape._handleEvent('mousedown', evt); + } //init stage drag and drop if(this.attrs.draggable) { @@ -671,21 +519,49 @@ Kinetic.Stage = Kinetic.Container.extend({ } }, _mouseup: function(evt) { - this.mouseDown = false; - this.mouseUp = true; - this.mouseMove = false; - this._handleStageEvent(evt); + this._setUserPosition(evt); + var go = Kinetic.Global; + var obj = this.getIntersection(this.getUserPosition()); + var that = this; + if(obj && obj.shape) { + var shape = obj.shape; + shape._handleEvent('mouseup', evt); + + // detect if click or double click occurred + if(this.clickStart) { + /* + * if dragging and dropping, don't fire click or dbl click + * event + */ + if((!go.drag.moving) || !go.drag.node) { + shape._handleEvent('click', evt); + + if(this.inDoubleClickWindow) { + shape._handleEvent('dblclick', evt); + } + this.inDoubleClickWindow = true; + setTimeout(function() { + that.inDoubleClickWindow = false; + }, this.dblClickWindow); + } + } + } this.clickStart = false; // end drag and drop this._endDrag(evt); }, _touchstart: function(evt) { + this._setUserPosition(evt); evt.preventDefault(); - this.touchStart = true; - this.touchEnd = false; - this.touchMove = false; - this._handleStageEvent(evt); + var obj = this.getIntersection(this.getUserPosition()); + + if(obj && obj.shape) { + var shape = obj.shape; + this.tapStart = true; + shape._handleEvent('touchstart', evt); + } + /* * init stage drag and drop */ @@ -694,20 +570,47 @@ Kinetic.Stage = Kinetic.Container.extend({ } }, _touchend: function(evt) { - this.touchStart = false; - this.touchEnd = true; - this.touchMove = false; - this._handleStageEvent(evt); + this._setUserPosition(evt); + var go = Kinetic.Global; + var obj = this.getIntersection(this.getUserPosition()); + var that = this; + if(obj && obj.shape) { + var shape = obj.shape; + shape._handleEvent('touchend', evt); + + // detect if tap or double tap occurred + if(this.tapStart) { + /* + * if dragging and dropping, don't fire tap or dbltap + * event + */ + if((!go.drag.moving) || !go.drag.node) { + shape._handleEvent('tap', evt); + + if(this.inDoubleClickWindow) { + shape._handleEvent('dbltap', evt); + } + this.inDoubleClickWindow = true; + setTimeout(function() { + that.inDoubleClickWindow = false; + }, this.dblClickWindow); + } + } + } + this.tapStart = false; // end drag and drop this._endDrag(evt); }, _touchmove: function(evt) { + this._setUserPosition(evt); evt.preventDefault(); - this.touchEnd = false; - this.touchMove = true; - this._handleStageEvent(evt); + var obj = this.getIntersection(this.getUserPosition()); + if(obj && obj.shape) { + var shape = obj.shape; + shape._handleEvent('touchmove', evt); + } // start drag and drop this._startDrag(evt); @@ -759,13 +662,13 @@ Kinetic.Stage = Kinetic.Container.extend({ var go = Kinetic.Global; var node = go.drag.node; if(node) { - if (node.nodeType === 'Stage') { - node.draw(); - } - else { - node.getLayer().draw(); - } - + if(node.nodeType === 'Stage') { + node.draw(); + } + else { + node.getLayer().draw(); + } + // handle dragend if(go.drag.moving) { go.drag.moving = false; @@ -853,11 +756,7 @@ Kinetic.Stage = Kinetic.Container.extend({ width: this.attrs.width, height: this.attrs.height }); - this.pathCanvas = new Kinetic.Canvas({ - width: this.attrs.width, - height: this.attrs.height - }); - this.pathCanvas.strip(); + this._resizeDOM(); }, _addId: function(node) { @@ -913,35 +812,18 @@ Kinetic.Stage = Kinetic.Container.extend({ _setStageDefaultProperties: function() { this.nodeType = 'Stage'; this.dblClickWindow = 400; - this.targetShape = undefined; - this.targetFound = false; - this.mouseoverShape = undefined; - this.mouseoutShape = undefined; - - // desktop flags + this.targetShape = null; this.mousePos = undefined; - this.mouseDown = false; - this.mouseUp = false; - this.mouseMove = false; this.clickStart = false; - - // mobile flags this.touchPos = undefined; - this.touchStart = false; - this.touchEnd = false; - this.touchMove = false; this.tapStart = false; this.ids = {}; this.names = {}; - //shapes hash. rgb keys and shape values - this.shapes = {}; - this.dragAnim = new Kinetic.Animation(); - }, - _draw: function(canvas) { - this._drawChildren(canvas); + this.dragAnim = new Kinetic.Animation(); } -}); +}; +Kinetic.Global.extend(Kinetic.Stage, Kinetic.Container); // add getters and setters Kinetic.Node.addGettersSetters(Kinetic.Stage, ['width', 'height']); diff --git a/src/filters/Grayscale.js b/src/filters/Grayscale.js index 7cb09b56..c3e77ef7 100644 --- a/src/filters/Grayscale.js +++ b/src/filters/Grayscale.js @@ -1,5 +1,5 @@ -Kinetic.Filters.Grayscale = function() { - var data = this.imageData.data; +Kinetic.Filters.Grayscale = function(imageData) { + var data = imageData.data; for(var i = 0; i < data.length; i += 4) { var brightness = 0.34 * data[i] + 0.5 * data[i + 1] + 0.16 * data[i + 2]; // red diff --git a/src/shapes/Ellipse.js b/src/shapes/Ellipse.js index 46e93702..72b77095 100644 --- a/src/shapes/Ellipse.js +++ b/src/shapes/Ellipse.js @@ -7,8 +7,12 @@ * @augments Kinetic.Shape * @param {Object} config */ -Kinetic.Ellipse = Kinetic.Shape.extend({ - init: function(config) { +Kinetic.Ellipse = function(config) { + this._initEllipse(config); +}; + +Kinetic.Ellipse.prototype = { + _initEllipse: function(config) { this.setDefaultAttrs({ radius: { x: 0, @@ -20,7 +24,7 @@ Kinetic.Ellipse = Kinetic.Shape.extend({ config.drawFunc = this.drawFunc; // call super constructor - this._super(config); + Kinetic.Shape.call(this, config); this._convertRadius(); var that = this; this.on('radiusChange.kinetic', function() { @@ -57,7 +61,8 @@ Kinetic.Ellipse = Kinetic.Shape.extend({ */ this.attrs.radius = type._getXY(radius); } -}); +}; +Kinetic.Global.extend(Kinetic.Ellipse, Kinetic.Shape); // Circle backwards compatibility Kinetic.Circle = Kinetic.Ellipse; diff --git a/src/shapes/Image.js b/src/shapes/Image.js index 1453b8a6..58d3fb69 100644 --- a/src/shapes/Image.js +++ b/src/shapes/Image.js @@ -11,24 +11,35 @@ * @param {Number} [config.height] * @param {Object} [config.crop] */ -Kinetic.Image = Kinetic.Shape.extend({ - init: function(config) { +Kinetic.Image = function(config) { + this._initImage(config); +}; + +Kinetic.Image.prototype = { + _initImage: function(config) { this.shapeType = "Image"; config.drawFunc = this.drawFunc; // call super constructor - this._super(config); + Kinetic.Shape.call(this, config); + + var that = this; + this.on('imageChange', function(evt) { + that._syncSize(); + }); + + this._syncSize(); }, drawFunc: function(context) { + var width = this.getWidth(); + var height = this.getHeight(); + + context.beginPath(); + context.rect(0, 0, width, height); + context.closePath(); + this.fill(context); + this.stroke(context); + if(this.attrs.image) { - var width = this.getWidth(); - var height = this.getHeight(); - - context.beginPath(); - context.rect(0, 0, width, height); - context.closePath(); - this.fill(context); - this.stroke(context); - // if cropping if(this.attrs.crop && this.attrs.crop.width && this.attrs.crop.height) { var cropX = this.attrs.crop.x ? this.attrs.crop.x : 0; @@ -63,34 +74,6 @@ Kinetic.Image = Kinetic.Shape.extend({ height: this.attrs.height }; }, - /** - * get width - * @name getWidth - * @methodOf Kinetic.Image.prototype - */ - getWidth: function() { - if(this.attrs.width) { - return this.attrs.width; - } - if(this.attrs.image) { - return this.attrs.image.width; - } - return 0; - }, - /** - * get height - * @name getHeight - * @methodOf Kinetic.Image.prototype - */ - getHeight: function() { - if(this.attrs.height) { - return this.attrs.height; - } - if(this.attrs.image) { - return this.attrs.image.height; - } - return 0; - }, /** * apply filter * @name applyFilter @@ -101,14 +84,14 @@ Kinetic.Image = Kinetic.Shape.extend({ * filter has been applied */ applyFilter: function(config) { - try { - var trans = this._clearTransform(); - this.saveImageData(this.getWidth(), this.getHeight()); - this._setTransform(trans); - - config.filter.call(this, config); + var canvas = new Kinetic.Canvas(this.attrs.image.width, this.attrs.image.height); + var context = canvas.getContext(); + context.drawImage(this.attrs.image, 0, 0); + try { + var imageData = context.getImageData(0, 0, canvas.getWidth(), canvas.getHeight()); + config.filter(imageData, config); var that = this; - Kinetic.Type._getImage(this.getImageData(), function(imageObj) { + Kinetic.Type._getImage(imageData, function(imageObj) { that.setImage(imageObj); if(config.callback) { @@ -119,12 +102,70 @@ Kinetic.Image = Kinetic.Shape.extend({ catch(e) { Kinetic.Global.warn('Unable to apply filter.'); } + }, + /** + * create image buffer which enables more accurate hit detection mapping of the image + * by avoiding event detections for transparent pixels + * @name createImageBuffer + * @methodOf Kinetic.Image.prototype + * @param {Function} [callback] callback function to be called once + * the buffer image has been created and set + */ + createImageBuffer: function(callback) { + var canvas = new Kinetic.Canvas(this.attrs.width, this.attrs.height); + var context = canvas.getContext(); + context.drawImage(this.attrs.image, 0, 0); + try { + var imageData = context.getImageData(0, 0, canvas.getWidth(), canvas.getHeight()); + var data = imageData.data; + var rgbColorKey = Kinetic.Type._hexToRgb(this.colorKey); + // replace non transparent pixels with color key + for(var i = 0, n = data.length; i < n; i += 4) { + data[i] = rgbColorKey.r; + data[i + 1] = rgbColorKey.g; + data[i + 2] = rgbColorKey.b; + // i+3 is alpha (the fourth element) + } + + var that = this; + Kinetic.Type._getImage(imageData, function(imageObj) { + that.imageBuffer = imageObj; + if(callback) { + callback(); + } + }); + } + catch(e) { + Kinetic.Global.warn('Unable to create image buffer.'); + } + }, + /** + * clear buffer image + * @name clearImageBuffer + * @methodOf Kinetic.Image.prototype + */ + clearImageBuffer: function() { + delete this.imageBuffer; + }, + _syncSize: function() { + if(this.attrs.image) { + if(!this.attrs.width) { + this.setAttrs({ + width: this.attrs.image.width + }); + } + if(!this.attrs.height) { + this.setAttrs({ + height: this.attrs.image.height + }); + } + } } -}); +}; +Kinetic.Global.extend(Kinetic.Image, Kinetic.Shape); // add getters setters -Kinetic.Node.addGettersSetters(Kinetic.Image, ['image', 'crop', 'filter']); -Kinetic.Node.addSetters(Kinetic.Image, ['width', 'height']); +Kinetic.Node.addGettersSetters(Kinetic.Image, ['image', 'crop', 'filter', 'width', 'height']); /** * set width @@ -177,4 +218,16 @@ Kinetic.Node.addSetters(Kinetic.Image, ['width', 'height']); * get filter * @name getFilter * @methodOf Kinetic.Image.prototype + */ + +/** + * get width + * @name getWidth + * @methodOf Kinetic.Image.prototype + */ + +/** + * get height + * @name getHeight + * @methodOf Kinetic.Image.prototype */ \ No newline at end of file diff --git a/src/shapes/Line.js b/src/shapes/Line.js index 2043dfce..f6fbd44a 100644 --- a/src/shapes/Line.js +++ b/src/shapes/Line.js @@ -7,8 +7,12 @@ * @augments Kinetic.Shape * @param {Object} config */ -Kinetic.Line = Kinetic.Shape.extend({ - init: function(config) { +Kinetic.Line = function(config) { + this._initLine(config); +}; + +Kinetic.Line.prototype = { + _initLine: function(config) { this.setDefaultAttrs({ points: [], lineCap: 'butt', @@ -19,7 +23,7 @@ Kinetic.Line = Kinetic.Shape.extend({ this.shapeType = "Line"; config.drawFunc = this.drawFunc; // call super constructor - this._super(config); + Kinetic.Shape.call(this, config); }, drawFunc: function(context) { var lastPos = {}; @@ -94,7 +98,8 @@ Kinetic.Line = Kinetic.Shape.extend({ context.moveTo(x2, y2); } -}); +}; +Kinetic.Global.extend(Kinetic.Line, Kinetic.Shape); // add getters setters Kinetic.Node.addGettersSetters(Kinetic.Line, ['dashArray', 'lineCap', 'points']); diff --git a/src/shapes/Path.js b/src/shapes/Path.js index 04fa8606..308fb8e0 100644 --- a/src/shapes/Path.js +++ b/src/shapes/Path.js @@ -8,15 +8,19 @@ * @augments Kinetic.Shape * @param {Object} config */ -Kinetic.Path = Kinetic.Shape.extend({ - init: function(config) { +Kinetic.Path = function(config) { + this._initPath(config); +}; + +Kinetic.Path.prototype = { + _initPath: function(config) { this.shapeType = "Path"; this.dataArray = []; var that = this; config.drawFunc = this.drawFunc; // call super constructor - this._super(config); + Kinetic.Shape.call(this, config); this.dataArray = Kinetic.Path.parsePathData(this.attrs.data); this.on('dataChange', function() { that.dataArray = Kinetic.Path.parsePathData(that.attrs.data); @@ -66,7 +70,8 @@ Kinetic.Path = Kinetic.Shape.extend({ this.fill(context); this.stroke(context); } -}); +}; +Kinetic.Global.extend(Kinetic.Path, Kinetic.Shape); /* * Utility methods written by jfollas to diff --git a/src/shapes/Polygon.js b/src/shapes/Polygon.js index f45c1db0..3b18e51f 100644 --- a/src/shapes/Polygon.js +++ b/src/shapes/Polygon.js @@ -7,8 +7,12 @@ * @augments Kinetic.Shape * @param {Object} config */ -Kinetic.Polygon = Kinetic.Shape.extend({ - init: function(config) { +Kinetic.Polygon = function(config) { + this._initPolygon(config); +}; + +Kinetic.Polygon.prototype = { + _initPolygon: function(config) { this.setDefaultAttrs({ points: [] }); @@ -16,7 +20,7 @@ Kinetic.Polygon = Kinetic.Shape.extend({ this.shapeType = "Polygon"; config.drawFunc = this.drawFunc; // call super constructor - this._super(config); + Kinetic.Shape.call(this, config); }, drawFunc: function(context) { context.beginPath(); @@ -28,7 +32,8 @@ Kinetic.Polygon = Kinetic.Shape.extend({ this.fill(context); this.stroke(context); } -}); +}; +Kinetic.Global.extend(Kinetic.Polygon, Kinetic.Shape); // add getters setters Kinetic.Node.addGettersSetters(Kinetic.Polygon, ['points']); diff --git a/src/shapes/Rect.js b/src/shapes/Rect.js index 82cdfe22..41c62c4b 100644 --- a/src/shapes/Rect.js +++ b/src/shapes/Rect.js @@ -7,8 +7,11 @@ * @augments Kinetic.Shape * @param {Object} config */ -Kinetic.Rect = Kinetic.Shape.extend({ - init: function(config) { +Kinetic.Rect = function(config) { + this._initRect(config); +} +Kinetic.Rect.prototype = { + _initRect: function(config) { this.setDefaultAttrs({ width: 0, height: 0, @@ -16,8 +19,8 @@ Kinetic.Rect = Kinetic.Shape.extend({ }); this.shapeType = "Rect"; config.drawFunc = this.drawFunc; - // call super constructor - this._super(config); + + Kinetic.Shape.call(this, config); }, drawFunc: function(context) { context.beginPath(); @@ -62,7 +65,8 @@ Kinetic.Rect = Kinetic.Shape.extend({ height: this.attrs.height }; } -}); +}; +Kinetic.Global.extend(Kinetic.Rect, Kinetic.Shape); // add getters setters Kinetic.Node.addGettersSetters(Kinetic.Rect, ['width', 'height', 'cornerRadius']); diff --git a/src/shapes/RegularPolygon.js b/src/shapes/RegularPolygon.js index 69dd6d5f..90abfd88 100644 --- a/src/shapes/RegularPolygon.js +++ b/src/shapes/RegularPolygon.js @@ -7,8 +7,12 @@ * @augments Kinetic.Shape * @param {Object} config */ -Kinetic.RegularPolygon = Kinetic.Shape.extend({ - init: function(config) { +Kinetic.RegularPolygon = function(config) { + this._initRegularPolygon(config); +}; + +Kinetic.RegularPolygon.prototype = { + _initRegularPolygon: function(config) { this.setDefaultAttrs({ radius: 0, sides: 0 @@ -17,7 +21,7 @@ Kinetic.RegularPolygon = Kinetic.Shape.extend({ this.shapeType = "RegularPolygon"; config.drawFunc = this.drawFunc; // call super constructor - this._super(config); + Kinetic.Shape.call(this, config); }, drawFunc: function(context) { context.beginPath(); @@ -32,7 +36,8 @@ Kinetic.RegularPolygon = Kinetic.Shape.extend({ this.fill(context); this.stroke(context); } -}); +}; +Kinetic.Global.extend(Kinetic.RegularPolygon, Kinetic.Shape); // add getters setters Kinetic.Node.addGettersSetters(Kinetic.RegularPolygon, ['radius', 'sides']); diff --git a/src/shapes/Sprite.js b/src/shapes/Sprite.js index 6fe61b43..5cf6577c 100644 --- a/src/shapes/Sprite.js +++ b/src/shapes/Sprite.js @@ -7,8 +7,12 @@ * @augments Kinetic.Shape * @param {Object} config */ -Kinetic.Sprite = Kinetic.Shape.extend({ - init: function(config) { +Kinetic.Sprite = function(config) { + this._initSprite(config); +}; + +Kinetic.Sprite.prototype = { + _initSprite: function(config) { this.setDefaultAttrs({ index: 0, frameRate: 17 @@ -16,7 +20,7 @@ Kinetic.Sprite = Kinetic.Shape.extend({ config.drawFunc = this.drawFunc; // call super constructor - this._super(config); + Kinetic.Shape.call(this, config); this.anim = new Kinetic.Animation(); var that = this; this.on('animationChange.kinetic', function() { @@ -25,10 +29,17 @@ Kinetic.Sprite = Kinetic.Shape.extend({ }); }, drawFunc: function(context) { + var anim = this.attrs.animation; + var index = this.attrs.index; + var f = this.attrs.animations[anim][index]; + + context.beginPath(); + context.rect(0, 0, f.width, f.height); + context.closePath(); + this.fill(context); + this.stroke(context); + if(this.attrs.image) { - var anim = this.attrs.animation; - var index = this.attrs.index; - var f = this.attrs.animations[anim][index]; context.beginPath(); context.rect(0, 0, f.width, f.height); @@ -94,7 +105,8 @@ Kinetic.Sprite = Kinetic.Shape.extend({ this.attrs.index = 0; } } -}); +}; +Kinetic.Global.extend(Kinetic.Sprite, Kinetic.Shape); // add getters setters Kinetic.Node.addGettersSetters(Kinetic.Sprite, ['animation', 'animations', 'index']); diff --git a/src/shapes/Star.js b/src/shapes/Star.js index 7ce2281e..864556be 100644 --- a/src/shapes/Star.js +++ b/src/shapes/Star.js @@ -7,8 +7,12 @@ * @augments Kinetic.Shape * @param {Object} config */ -Kinetic.Star = Kinetic.Shape.extend({ - init: function(config) { +Kinetic.Star = function(config) { + this._initStar(config); +}; + +Kinetic.Star.prototype = { + _initStar: function(config) { this.setDefaultAttrs({ numPoints: 0, innerRadius: 0, @@ -18,7 +22,7 @@ Kinetic.Star = Kinetic.Shape.extend({ this.shapeType = "Star"; config.drawFunc = this.drawFunc; // call super constructor - this._super(config); + Kinetic.Shape.call(this, config); }, drawFunc: function(context) { context.beginPath(); @@ -35,7 +39,8 @@ Kinetic.Star = Kinetic.Shape.extend({ this.fill(context); this.stroke(context); } -}); +}; +Kinetic.Global.extend(Kinetic.Star, Kinetic.Shape); // add getters setters Kinetic.Node.addGettersSetters(Kinetic.Star, ['numPoints', 'innerRadius', 'outerRadius']); diff --git a/src/shapes/Text.js b/src/shapes/Text.js index e133f8ee..6c83c626 100644 --- a/src/shapes/Text.js +++ b/src/shapes/Text.js @@ -7,8 +7,12 @@ * @augments Kinetic.Shape * @param {Object} config */ -Kinetic.Text = Kinetic.Shape.extend({ - init: function(config) { +Kinetic.Text = function(config) { + this._initText(config); +}; + +Kinetic.Text.prototype = { + _initText: function(config) { this.setDefaultAttrs({ fontFamily: 'Calibri', text: '', @@ -29,7 +33,7 @@ Kinetic.Text = Kinetic.Shape.extend({ config.drawFunc = this.drawFunc; // call super constructor - this._super(config); + Kinetic.Shape.call(this, config); // update text data for certain attr changes var attrs = ['fontFamily', 'fontSize', 'fontStyle', 'padding', 'align', 'lineHeight', 'text', 'width', 'height']; @@ -212,7 +216,9 @@ Kinetic.Text = Kinetic.Shape.extend({ } this.textArr = arr; } -}); +}; +Kinetic.Global.extend(Kinetic.Text, Kinetic.Shape); + // add getters setters Kinetic.Node.addGettersSetters(Kinetic.Text, ['fontFamily', 'fontSize', 'fontStyle', 'textFill', 'textStroke', 'textStrokeWidth', 'padding', 'align', 'lineHeight', 'text', 'width', 'height', 'cornerRadius', 'fill', 'stroke', 'strokeWidth', 'shadow']); diff --git a/src/shapes/TextPath.js b/src/shapes/TextPath.js index 614b104a..7b1c4381 100644 --- a/src/shapes/TextPath.js +++ b/src/shapes/TextPath.js @@ -8,8 +8,12 @@ * @augments Kinetic.Shape * @param {Object} config */ -Kinetic.TextPath = Kinetic.Shape.extend({ - init: function(config) { +Kinetic.TextPath = function(config) { + this._initTextPath(config); +}; + +Kinetic.TextPath.prototype = { + _initTextPath: function(config) { this.setDefaultAttrs({ fontFamily: 'Calibri', fontSize: 12, @@ -25,7 +29,7 @@ Kinetic.TextPath = Kinetic.Shape.extend({ config.drawFunc = this.drawFunc; // call super constructor - this._super(config); + Kinetic.Shape.call(this, config); this.dataArray = Kinetic.Path.parsePathData(this.attrs.data); this.on('dataChange', function() { that.dataArray = Kinetic.Path.parsePathData(this.attrs.data); @@ -279,7 +283,8 @@ Kinetic.TextPath = Kinetic.Shape.extend({ p0 = p1; } } -}); +}; +Kinetic.Global.extend(Kinetic.TextPath, Kinetic.Shape); // add setters and getters Kinetic.Node.addGettersSetters(Kinetic.TextPath, ['fontFamily', 'fontSize', 'fontStyle', 'textFill', 'textStroke', 'textStrokeWidth', 'text']); diff --git a/src/util/Canvas.js b/src/util/Canvas.js index 74f8d7a2..cd578c24 100644 --- a/src/util/Canvas.js +++ b/src/util/Canvas.js @@ -92,22 +92,6 @@ Kinetic.Canvas.prototype = { */ strip: function() { var context = this.context; - context.stroke = function() { - }; - context.fill = function() { - }; - context.fillRect = function(x, y, width, height) { - context.rect(x, y, width, height); - }; - context.strokeRect = function(x, y, width, height) { - context.rect(x, y, width, height); - }; - context.drawImage = function() { - }; - context.fillText = function() { - }; - context.strokeText = function() { - }; }, /** * toDataURL diff --git a/src/util/Type.js b/src/util/Type.js index 98d29468..23b2dde9 100644 --- a/src/util/Type.js +++ b/src/util/Type.js @@ -250,5 +250,22 @@ Kinetic.Type = { else { callback(null); } + }, + _rgbToHex: function(r, g, b) { + return ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); + }, + _hexToRgb: function(hex) { + var bigint = parseInt(hex, 16); + return { + r: (bigint >> 16) & 255, + g: (bigint >> 8) & 255, + b: bigint & 255 + }; + }, + _getRandomColorKey: function() { + var r = Math.round(Math.random() * 255); + var g = Math.round(Math.random() * 255); + var b = Math.round(Math.random() * 255); + return this._rgbToHex(r, g, b); } }; diff --git a/tests/html/functionalTests.html b/tests/html/functionalTests.html index 2ff2198b..83602dd7 100644 --- a/tests/html/functionalTests.html +++ b/tests/html/functionalTests.html @@ -23,7 +23,7 @@ test.run(); document.getElementsByTagName('body')[0].addEventListener('mousemove', function(evt) { - console.log(evt.clientX + ',' + evt.clientY); + //console.log(evt.clientX + ',' + evt.clientY); }, false); }; diff --git a/tests/js/functionalTests.js b/tests/js/functionalTests.js index fc88c0e7..fad67d99 100644 --- a/tests/js/functionalTests.js +++ b/tests/js/functionalTests.js @@ -43,11 +43,12 @@ Test.prototype.tests = { /* * simulate drag and drop */ + console.log(1) stage._mousedown({ clientX: 380, clientY: 98 }); - + console.log(2) test(!dragStart, 'dragstart event should not have been triggered'); test(!dragMove, 'dragmove event should not have been triggered'); test(!dragEnd, 'dragend event should not have been triggered'); @@ -261,7 +262,7 @@ Test.prototype.tests = { warn(layer.toDataURL() === urls[0], 'end data url is incorrect'); }, - 'EVENTS - path detection mousedown mouseup mouseover mouseout mousemove click dblclick / touchstart touchend touchmove tap dbltap': function(containerId) { + 'EVENTS - mousedown mouseup mouseover mouseout mousemove click dblclick / touchstart touchend touchmove tap dbltap': function(containerId) { var stage = new Kinetic.Stage({ container: containerId, width: 578, @@ -296,37 +297,37 @@ Test.prototype.tests = { circle.on('mousedown', function() { mousedown = true; - //log('mousedown'); + log('mousedown'); }); circle.on('mouseup', function() { mouseup = true; - //log('mouseup'); + log('mouseup'); }); circle.on('mouseover', function() { mouseover = true; - //log('mouseover'); + log('mouseover'); }); circle.on('mouseout', function() { mouseout = true; - //log('mouseout'); + log('mouseout'); }); circle.on('mousemove', function() { mousemove = true; - //log('mousemove'); + log('mousemove'); }); circle.on('click', function() { click = true; - //log('click'); + log('click'); }); circle.on('dblclick', function() { dblclick = true; - //log('dblclick'); + log('dblclick'); }); /* * mobile @@ -366,286 +367,7 @@ Test.prototype.tests = { }); test(mouseover, '1) mouseover should be true'); - test(!mousemove, '1) mousemove should be false'); - test(!mousedown, '1) mousedown should be false'); - test(!mouseup, '1) mouseup should be false'); - test(!click, '1) click should be false'); - test(!dblclick, '1) dblclick should be false'); - test(!mouseout, '1) mouseout should be false'); - - // move mouse again inside circle to trigger mousemove - stage._mousemove({ - clientX: 290, - clientY: 100 - }); - - test(mouseover, '2) mouseover should be true'); - test(mousemove, '2) mousemove should be true'); - test(!mousedown, '2) mousedown should be false'); - test(!mouseup, '2) mouseup should be false'); - test(!click, '2) click should be false'); - test(!dblclick, '2) dblclick should be false'); - test(!mouseout, '2) mouseout should be false'); - - // mousedown inside circle - stage._mousedown({ - clientX: 290, - clientY: 100 - }); - - test(mouseover, '3) mouseover should be true'); - test(mousemove, '3) mousemove should be true'); - test(mousedown, '3) mousedown should be true'); - test(!mouseup, '3) mouseup should be false'); - test(!click, '3) click should be false'); - test(!dblclick, '3) dblclick should be false'); - test(!mouseout, '3) mouseout should be false'); - - // mouseup inside circle - stage._mouseup({ - clientX: 290, - clientY: 100 - }); - - test(mouseover, '4) mouseover should be true'); - test(mousemove, '4) mousemove should be true'); - test(mousedown, '4) mousedown should be true'); - test(mouseup, '4) mouseup should be true'); - test(click, '4) click should be true'); - test(!dblclick, '4) dblclick should be false'); - test(!mouseout, '4) mouseout should be false'); - - // mousedown inside circle - stage._mousedown({ - clientX: 290, - clientY: 100 - }); - - test(mouseover, '5) mouseover should be true'); - test(mousemove, '5) mousemove should be true'); - test(mousedown, '5) mousedown should be true'); - test(mouseup, '5) mouseup should be true'); - test(click, '5) click should be true'); - test(!dblclick, '5) dblclick should be false'); - test(!mouseout, '5) mouseout should be false'); - - // mouseup inside circle to trigger double click - stage._mouseup({ - clientX: 290, - clientY: 100 - }); - - test(mouseover, '6) mouseover should be true'); - test(mousemove, '6) mousemove should be true'); - test(mousedown, '6) mousedown should be true'); - test(mouseup, '6) mouseup should be true'); - test(click, '6) click should be true'); - test(dblclick, '6) dblclick should be true'); - test(!mouseout, '6) mouseout should be false'); - - // move mouse outside of circle to trigger mouseout - stage._mousemove({ - clientX: 0, - clientY: 100 - }); - stage._mousemove({ - clientX: 0, - clientY: 100 - }); - - test(mouseover, '7) mouseover should be true'); - test(mousemove, '7) mousemove should be true'); - test(mousedown, '7) mousedown should be true'); - test(mouseup, '7) mouseup should be true'); - test(click, '7) click should be true'); - test(dblclick, '7) dblclick should be true'); - test(mouseout, '7) mouseout should be true'); - - /* - * mobile tests - */ - // reset inDoubleClickWindow - stage.inDoubleClickWindow = false; - - // touchstart circle - stage._touchstart({ - clientX: 289, - clientY: 100, - preventDefault: function() { - } - }); - - test(touchstart, '8) touchstart should be true'); - test(!touchmove, '8) touchmove should be false'); - test(!touchend, '8) touchend should be false'); - test(!tap, '8) tap should be false'); - test(!dbltap, '8) dbltap should be false'); - - // touchend circle - stage._touchend({ - clientX: 289, - clientY: 100, - preventDefault: function() { - } - }); - - test(touchstart, '9) touchstart should be true'); - test(!touchmove, '9) touchmove should be false'); - test(touchend, '9) touchend should be true'); - test(tap, '9) tap should be true'); - test(!dbltap, '9) dbltap should be false'); - - // touchstart circle - stage._touchstart({ - clientX: 289, - clientY: 100, - preventDefault: function() { - } - }); - - test(touchstart, '10) touchstart should be true'); - test(!touchmove, '10) touchmove should be false'); - test(touchend, '10) touchend should be true'); - test(tap, '10) tap should be true'); - test(!dbltap, '10) dbltap should be false'); - - // touchend circle to triger dbltap - stage._touchend({ - clientX: 289, - clientY: 100, - preventDefault: function() { - } - }); - - test(touchstart, '11) touchstart should be true'); - test(!touchmove, '11) touchmove should be false'); - test(touchend, '11) touchend should be true'); - test(tap, '11) tap should be true'); - test(dbltap, '11) dbltap should be true'); - - // touchmove circle - stage._touchmove({ - clientX: 290, - clientY: 100, - preventDefault: function() { - } - }); - - test(touchstart, '12) touchstart should be true'); - test(touchmove, '12) touchmove should be true'); - test(touchend, '12) touchend should be true'); - test(tap, '12) tap should be true'); - test(dbltap, '12) dbltap should be true'); - }, - 'EVENTS - pixel detection mousedown mouseup mouseover mouseout mousemove click dblclick / touchstart touchend touchmove tap dbltap': function(containerId) { - var stage = new Kinetic.Stage({ - container: containerId, - width: 578, - height: 200, - throttle: 999 - }); - var layer = new Kinetic.Layer(); - var circle = new Kinetic.Ellipse({ - x: stage.getWidth() / 2, - y: stage.getHeight() / 2, - radius: 70, - fill: 'red', - stroke: 'black', - strokeWidth: 4, - detectionType: 'pixel' - }); - - // desktop events - var mousedown = false; - var mouseup = false; - var click = false; - var dblclick = false; - var mouseover = false; - var mouseout = false; - var mousemove = false; - - // mobile events - var touchstart = false; - var touchend = false; - var tap = false; - var touchmove = false; - var dbltap = false; - - circle.on('mousedown', function() { - mousedown = true; - //log('mousedown'); - }); - - circle.on('mouseup', function() { - mouseup = true; - //log('mouseup'); - }); - - circle.on('mouseover', function() { - mouseover = true; - //log('mouseover'); - }); - - circle.on('mouseout', function() { - mouseout = true; - //log('mouseout'); - }); - - circle.on('mousemove', function() { - mousemove = true; - //log('mousemove'); - }); - - circle.on('click', function() { - click = true; - //log('click'); - }); - - circle.on('dblclick', function() { - dblclick = true; - //log('dblclick'); - }); - /* - * mobile - */ - circle.on('touchstart', function() { - touchstart = true; - //log('touchstart'); - }); - - circle.on('touchend', function() { - touchend = true; - //log('touchend'); - }); - - circle.on('touchmove', function() { - touchmove = true; - //log('touchmove'); - }); - - circle.on('tap', function(evt) { - tap = true; - //log('tap'); - }); - - circle.on('dbltap', function() { - dbltap = true; - //log('dbltap'); - }); - - layer.add(circle); - stage.add(layer); - - circle.saveImageData(); - - // move mouse to center of circle to trigger mouseover - stage._mousemove({ - clientX: 290, - clientY: 100 - }); - - test(mouseover, '1) mouseover should be true'); - test(!mousemove, '1) mousemove should be false'); + test(!mousemove, '1) mousemove should be true'); test(!mousedown, '1) mousedown should be false'); test(!mouseup, '1) mouseup should be false'); test(!click, '1) click should be false'); @@ -935,30 +657,32 @@ Test.prototype.tests = { group.on('mouseover', function() { groupMouseovers++; - //onsole.log('over') + console.log('group over') }); group.on('mouseout', function() { groupMouseouts++; - //console.log('out') + console.log('group out') }); redEllipse.on('mouseover', function() { redMouseovers++; - //console.log('over') + console.log('red over') }); redEllipse.on('mouseout', function() { redMouseouts++; - //console.log('out') + console.log('red out') }); greenEllipse.on('mouseover', function() { greenMouseovers++; + console.log('green over') }); greenEllipse.on('mouseout', function() { greenMouseouts++; + console.log('green out') }); group.add(redEllipse); @@ -1039,6 +763,11 @@ Test.prototype.tests = { test(greenMouseouts === 1, 'greenMouseouts should be 1'); test(groupMouseovers === 1, 'groupMouseovers should be 1'); test(groupMouseouts === 1, 'groupMouseouts should be 1'); + + //document.body.appendChild(layer.bufferCanvas.element) + + layer.bufferCanvas.element.style.marginTop = '220px'; + }, 'EVENTS - test event bubbling': function(containerId) { var stage = new Kinetic.Stage({ diff --git a/tests/js/manualTests.js b/tests/js/manualTests.js index eb92433b..81aced8c 100644 --- a/tests/js/manualTests.js +++ b/tests/js/manualTests.js @@ -67,6 +67,8 @@ Test.prototype.tests = { layer.add(circle); stage.add(layer); + + document.body.appendChild(layer.bufferCanvas.element) }, 'TRANSITION - transition position and rotation': function(containerId) { var stage = new Kinetic.Stage({ @@ -348,7 +350,6 @@ Test.prototype.tests = { fill: 'green', stroke: 'blue', strokeWidth: 20, - detectionType: 'pixel', draggable: true }); @@ -360,13 +361,10 @@ Test.prototype.tests = { log('mouseout'); }); - star.on('dragend', function() { - this.saveImageData(); - }); - layer.add(star); stage.add(layer); - star.saveImageData(); + + //document.body.appendChild(layer.bufferCanvas.element) }, 'EVENTS - drag events click': function(containerId) { var stage = new Kinetic.Stage({ @@ -448,6 +446,11 @@ Test.prototype.tests = { layer.add(greenEllipse); stage.add(layer); + + //greenEllipse.hide(); + layer.draw(); + + //document.body.appendChild(layer.bufferCanvas.element); }, 'EVENTS - move mouse from shape in one group to shape in another group': function(containerId) { var stage = new Kinetic.Stage({ @@ -783,7 +786,7 @@ Test.prototype.tests = { }) }); }, - '*DRAG AND DROP - two draggable shapes': function(containerId) { + 'DRAG AND DROP - two draggable shapes': function(containerId) { var stage = new Kinetic.Stage({ container: containerId, width: 578, @@ -797,7 +800,6 @@ Test.prototype.tests = { fill: 'red', stroke: 'black', strokeWidth: 4, - //detectionType: 'pixel' }); Ellipse.setDraggable(true); @@ -810,7 +812,6 @@ Test.prototype.tests = { stroke: 'black', strokeWidth: 4, draggable: true - //detectionType: 'pixel' }); /* diff --git a/tests/js/performanceTests.js b/tests/js/performanceTests.js index 458cf010..d961904d 100644 --- a/tests/js/performanceTests.js +++ b/tests/js/performanceTests.js @@ -1,5 +1,5 @@ Test.prototype.tests = { - 'DRAWING - draw rect': function(containerId) { + '*DRAWING - draw rect': function(containerId) { var stage = new Kinetic.Stage({ container: containerId, width: 578, @@ -67,7 +67,7 @@ Test.prototype.tests = { anim.start(); }, 4000); }, - '*DRAWING - draw 10,000 small circles with tooltips': function(containerId) { + 'DRAWING - draw 10,000 small circles with tooltips': function(containerId) { var stage = new Kinetic.Stage({ container: containerId, width: 578, @@ -90,8 +90,7 @@ Test.prototype.tests = { var randX = Math.random() * stage.getWidth(); var randY = Math.random() * stage.getHeight(); - //var randRadius = (Math.random() * 5) + 5 - + var circle = new Kinetic.Ellipse({ x: randX, y: randY, @@ -101,6 +100,7 @@ Test.prototype.tests = { circle.on("mousemove", function() { // update tooltip + console.log('mouseover') var mousePos = stage.getMousePosition(); tooltip.setPosition(mousePos.x + 5, mousePos.y + 5); tooltip.setText("node: " + i + ", color: " + color); @@ -116,6 +116,7 @@ Test.prototype.tests = { circlesLayer.add(circle); }()); } + var tooltip = new Kinetic.Text({ text: "", fontFamily: "Calibri", @@ -128,9 +129,12 @@ Test.prototype.tests = { }); tooltipLayer.add(tooltip); + stage.add(circlesLayer); stage.add(tooltipLayer); + + document.body.appendChild(circlesLayer.bufferCanvas.element) }, 'DRAWING - draw rect vs image from image data': function(containerId) { diff --git a/tests/js/unitTests.js b/tests/js/unitTests.js index 09123513..64ace69f 100644 --- a/tests/js/unitTests.js +++ b/tests/js/unitTests.js @@ -1,7 +1,8 @@ Test.prototype.tests = { - //////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////Fp // STAGE tests //////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////// 'STAGE - instantiate stage with id': function(containerId) { var stage = new Kinetic.Stage({ @@ -94,40 +95,6 @@ Test.prototype.tests = { layer.add(group); layer.draw(); }, - 'STAGE - test layer throttle': function(containerId) { - var stage = new Kinetic.Stage({ - container: containerId, - width: 578, - height: 200 - }); - var layer = new Kinetic.Layer(); - var group = new Kinetic.Group(); - var circle = new Kinetic.Ellipse({ - x: stage.getWidth() / 2, - y: stage.getHeight() / 2, - radius: 70, - fill: 'green', - stroke: 'black', - strokeWidth: 4, - name: 'myCircle' - }); - - group.add(circle); - layer.add(group); - stage.add(layer); - - test(layer.lastDrawTime === 0, 'layer last draw time should be 0'); - - /* - * if throttling isn't working correctly, then the circle will - * flash green and then turn red - */ - circle.setFill('red'); - layer.draw(); - - test(layer.lastDrawTime > 0, 'layer last draw time should be greather than 0'); - - }, 'STAGE - add shape with linear gradient fill': function(containerId) { var stage = new Kinetic.Stage({ container: containerId, @@ -162,8 +129,10 @@ Test.prototype.tests = { stage.add(layer); test(circle.getName() === 'myCircle', 'circle name should be myCircle'); + + document.body.appendChild(layer.bufferCanvas.element) }, - 'STAGE - add shape with alpha': function(containerId) { + 'STAGE - add shape with opacity': function(containerId) { var stage = new Kinetic.Stage({ container: containerId, width: 578, @@ -184,10 +153,10 @@ Test.prototype.tests = { layer.add(group); stage.add(layer); - circle.setAlpha(0.5); + circle.setOpacity(0.5); layer.draw(); - circle.setAlpha(0.5); + circle.setOpacity(0.5); layer.draw(); }, 'STAGE - add layer then group then shape': function(containerId) { @@ -237,7 +206,7 @@ Test.prototype.tests = { group.add(circle); layer.draw(); - var expectedJson = '{"attrs":{"width":578,"height":200,"visible":true,"listening":true,"alpha":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80},"nodeType":"Stage","children":[{"attrs":{"clearBeforeDraw":true,"visible":true,"listening":true,"alpha":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80},"nodeType":"Layer","children":[{"attrs":{"visible":true,"listening":true,"alpha":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80},"nodeType":"Group","children":[{"attrs":{"radius":{"x":70,"y":70},"detectionType":"path","visible":true,"listening":true,"name":"myCircle","alpha":1,"x":289,"y":100,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":true,"dragThrottle":80,"fill":"green","stroke":"black","strokeWidth":4},"nodeType":"Shape","shapeType":"Ellipse"}]}]}]}'; + var expectedJson = '{"attrs":{"width":578,"height":200,"visible":true,"listening":true,"opacity":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80},"nodeType":"Stage","children":[{"attrs":{"clearBeforeDraw":true,"visible":true,"listening":true,"opacity":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80},"nodeType":"Layer","children":[{"attrs":{"visible":true,"listening":true,"opacity":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80},"nodeType":"Group","children":[{"attrs":{"radius":{"x":70,"y":70},"detectionType":"path","visible":true,"listening":true,"name":"myCircle","opacity":1,"x":289,"y":100,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":true,"dragThrottle":80,"fill":"green","stroke":"black","strokeWidth":4},"nodeType":"Shape","shapeType":"Ellipse"}]}]}]}'; //console.log(stage.toJSON()) //test(stage.toJSON() === expectedJson, 'problem with serialization'); @@ -324,10 +293,10 @@ Test.prototype.tests = { height: 200 }); - var json = '{"attrs":{"width":578,"height":200,"visible":true,"listening":true,"alpha":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80},"nodeType":"Stage","children":[{"attrs":{"clearBeforeDraw":true,"visible":true,"listening":true,"alpha":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80},"nodeType":"Layer","children":[{"attrs":{"visible":true,"listening":true,"alpha":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80},"nodeType":"Group","children":[{"attrs":{"radius":{"x":70,"y":70},"detectionType":"path","visible":true,"listening":true,"name":"myCircle","alpha":1,"x":289,"y":100,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":true,"dragThrottle":80,"fill":"green","stroke":"black","strokeWidth":4},"nodeType":"Shape","shapeType":"Ellipse"}]}]}]}'; - stage.load(json); + var json = '{"attrs":{"width":578,"height":200,"visible":true,"listening":true,"opacity":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80},"nodeType":"Stage","children":[{"attrs":{"clearBeforeDraw":true,"visible":true,"listening":true,"opacity":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80},"nodeType":"Layer","children":[{"attrs":{"visible":true,"listening":true,"opacity":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80},"nodeType":"Group","children":[{"attrs":{"radius":{"x":70,"y":70},"detectionType":"path","visible":true,"listening":true,"name":"myCircle","opacity":1,"x":289,"y":100,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":true,"dragThrottle":80,"fill":"green","stroke":"black","strokeWidth":4},"nodeType":"Shape","shapeType":"Ellipse"}]}]}]}'; + //stage.load(json); - test(stage.toJSON() === json, "problem loading stage with json"); + //test(stage.toJSON() === json, "problem loading stage with json"); }, 'STAGE - serialize stage with custom shape': function(containerId) { var urls = dataUrls['STAGE - serialize stage with custom shape']; @@ -367,7 +336,7 @@ Test.prototype.tests = { test(triangle.getId() === 'myTriangle', 'triangle id should be myTriangle'); //console.log(stage.toJSON()) - var expectedJson = '{"attrs":{"width":578,"height":200,"visible":true,"listening":true,"alpha":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80},"nodeType":"Stage","children":[{"attrs":{"clearBeforeDraw":true,"visible":true,"listening":true,"alpha":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80},"nodeType":"Layer","children":[{"attrs":{"visible":true,"listening":true,"alpha":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80},"nodeType":"Group","children":[{"attrs":{"detectionType":"path","visible":true,"listening":true,"alpha":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80,"fill":"#00D2FF","stroke":"black","strokeWidth":4,"id":"myTriangle"},"nodeType":"Shape"}]}]}]}'; + var expectedJson = '{"attrs":{"width":578,"height":200,"visible":true,"listening":true,"opacity":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80},"nodeType":"Stage","children":[{"attrs":{"clearBeforeDraw":true,"visible":true,"listening":true,"opacity":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80},"nodeType":"Layer","children":[{"attrs":{"visible":true,"listening":true,"opacity":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80},"nodeType":"Group","children":[{"attrs":{"detectionType":"path","visible":true,"listening":true,"opacity":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80,"fill":"#00D2FF","stroke":"black","strokeWidth":4,"id":"myTriangle"},"nodeType":"Shape"}]}]}]}'; //console.log(stage.toJSON()) //test(stage.toJSON() === expectedJson, "problem serializing stage with custom shape"); @@ -398,16 +367,16 @@ Test.prototype.tests = { this.fill(context); this.stroke(context); }; - var json = '{"attrs":{"width":578,"height":200,"visible":true,"listening":true,"alpha":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80},"nodeType":"Stage","children":[{"attrs":{"clearBeforeDraw":true,"visible":true,"listening":true,"alpha":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80},"nodeType":"Layer","children":[{"attrs":{"visible":true,"listening":true,"alpha":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80},"nodeType":"Group","children":[{"attrs":{"detectionType":"path","visible":true,"listening":true,"alpha":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80,"fill":"#00D2FF","stroke":"black","strokeWidth":4,"id":"myTriangle"},"nodeType":"Shape"}]}]}]}'; - stage.load(json); + var json = '{"attrs":{"width":578,"height":200,"visible":true,"listening":true,"opacity":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80},"nodeType":"Stage","children":[{"attrs":{"clearBeforeDraw":true,"visible":true,"listening":true,"opacity":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80},"nodeType":"Layer","children":[{"attrs":{"visible":true,"listening":true,"opacity":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80},"nodeType":"Group","children":[{"attrs":{"detectionType":"path","visible":true,"listening":true,"opacity":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80,"fill":"#00D2FF","stroke":"black","strokeWidth":4,"id":"myTriangle"},"nodeType":"Shape"}]}]}]}'; + //stage.load(json); - var customShape = stage.get('#myTriangle')[0]; + //var customShape = stage.get('#myTriangle')[0]; - customShape.setDrawFunc(drawTriangle); + //customShape.setDrawFunc(drawTriangle); - stage.draw(); + //stage.draw(); //console.log(stage.toJSON()); - test(stage.toJSON() === json, "problem loading stage with custom shape json"); + //test(stage.toJSON() === json, "problem loading stage with custom shape json"); }, 'EVENTS - test getIntersections': function(containerId) { var stage = new Kinetic.Stage({ @@ -511,7 +480,6 @@ Test.prototype.tests = { test(stage.getScale().x === 0.5, 'stage scale x should be 0.5'); test(stage.getScale().y === 0.5, 'stage scale y should be 0.5'); - stage.draw(); }, 'STAGE - scale stage before add shape': function(containerId) { @@ -691,7 +659,7 @@ Test.prototype.tests = { test(stage.names['newRectName'][0].getName() === 'newRectName', 'new rect name not in names hash'); test(stage.names['myRect'] === undefined, 'old rect name is still in names hash'); }, - 'STAGE - set shape and layer alpha to 0.5': function(containerId) { + 'STAGE - set shape and layer opacity to 0.5': function(containerId) { var stage = new Kinetic.Stage({ container: containerId, width: 578, @@ -707,13 +675,13 @@ Test.prototype.tests = { strokeWidth: 4 }); - circle.setAlpha(0.5); - layer.setAlpha(0.5); + circle.setOpacity(0.5); + layer.setOpacity(0.5); layer.add(circle); stage.add(layer); - test(circle.getAbsoluteAlpha() === 0.25, 'abs alpha should be 0.25'); - test(layer.getAbsoluteAlpha() === 0.5, 'abs alpha should be 0.5'); + test(circle.getAbsoluteOpacity() === 0.25, 'abs opacity should be 0.25'); + test(layer.getAbsoluteOpacity() === 0.5, 'abs opacity should be 0.5'); }, 'STAGE - remove shape without adding its parent to stage': function(containerId) { var stage = new Kinetic.Stage({ @@ -894,7 +862,7 @@ Test.prototype.tests = { layer.add(darth); stage.add(layer); - var expectedJson = '{"attrs":{"width":578,"height":200,"visible":true,"listening":true,"alpha":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80},"nodeType":"Stage","children":[{"attrs":{"clearBeforeDraw":true,"visible":true,"listening":true,"alpha":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80},"nodeType":"Layer","children":[{"attrs":{"detectionType":"path","visible":true,"listening":true,"alpha":1,"x":200,"y":60,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":50,"y":150},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80,"id":"darth"},"nodeType":"Shape","shapeType":"Image"}]}]}'; + var expectedJson = '{"attrs":{"width":578,"height":200,"visible":true,"listening":true,"opacity":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80},"nodeType":"Stage","children":[{"attrs":{"clearBeforeDraw":true,"visible":true,"listening":true,"opacity":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80},"nodeType":"Layer","children":[{"attrs":{"detectionType":"path","visible":true,"listening":true,"opacity":1,"x":200,"y":60,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":50,"y":150},"dragConstraint":"none","dragBounds":{},"draggable":false,"dragThrottle":80,"id":"darth"},"nodeType":"Shape","shapeType":"Image"}]}]}'; //console.log(stage.toJSON()) //test(stage.toJSON() === expectedJson, 'problem with serializing stage with image'); }; @@ -909,11 +877,11 @@ Test.prototype.tests = { height: 200 }); - var json = '{"attrs":{"width":578,"height":200,"visible":true,"listen":true,"alpha":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false},"nodeType":"Stage","children":[{"attrs":{"visible":true,"listen":true,"alpha":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false},"nodeType":"Layer","children":[{"attrs":{"crop":{"x":0,"y":0},"detectionType":"path","visible":true,"listen":true,"alpha":1,"x":200,"y":60,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":50,"y":150},"dragConstraint":"none","dragBounds":{},"draggable":false,"id":"darth"},"nodeType":"Shape","shapeType":"Image"}]}]}'; - stage.load(json); - var image = stage.get('#darth')[0]; - image.setImage(imageObj); - stage.draw(); + var json = '{"attrs":{"width":578,"height":200,"visible":true,"listen":true,"opacity":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false},"nodeType":"Stage","children":[{"attrs":{"visible":true,"listen":true,"opacity":1,"x":0,"y":0,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":0,"y":0},"dragConstraint":"none","dragBounds":{},"draggable":false},"nodeType":"Layer","children":[{"attrs":{"crop":{"x":0,"y":0},"detectionType":"path","visible":true,"listen":true,"opacity":1,"x":200,"y":60,"scale":{"x":1,"y":1},"rotation":0,"offset":{"x":50,"y":150},"dragConstraint":"none","dragBounds":{},"draggable":false,"id":"darth"},"nodeType":"Shape","shapeType":"Image"}]}]}'; + //stage.load(json); + //var image = stage.get('#darth')[0]; + //image.setImage(imageObj); + //stage.draw(); }; imageObj.src = '../assets/darth-vader.jpg'; }, @@ -1301,6 +1269,8 @@ Test.prototype.tests = { circle.setFill({ offset: [-200, -70] }); + + //document.body.appendChild(layer.bufferCanvas.element) }; imageObj.src = '../assets/darth-vader.jpg'; @@ -1354,6 +1324,8 @@ Test.prototype.tests = { test(fill.colorStops.length === 6, 'fill colorStops length should be 6'); + //document.body.appendChild(layer.bufferCanvas.element) + }, 'SHAPE - add rect': function(containerId) { var stage = new Kinetic.Stage({ @@ -1470,10 +1442,10 @@ Test.prototype.tests = { }); /* - * serialize the stage. The json should succeed because objects that have - * methods, such as self, are not serialized, and will therefore avoid - * circular json errors. - */ + * serialize the stage. The json should succeed because objects that have + * methods, such as self, are not serialized, and will therefore avoid + * circular json errors. + */ //var json = stage.toJSON(); }, 'SHAPE - set fill after instantiation': function(containerId) { @@ -1516,7 +1488,7 @@ Test.prototype.tests = { height: 100, offset: [50, 30], crop: [135, 7, 167, 134], - cornerRadius: 20 + draggable: true }); layer.add(darth); @@ -1543,9 +1515,6 @@ Test.prototype.tests = { test(crop.width === 167, 'crop width should be 167'); test(crop.height === 134, 'crop height should be134'); - /* - * test cropping setter - */ darth.setCrop(0, 1, 2, 3); crop = darth.getCrop(); test(crop.x === 0, 'crop x should be 0'); @@ -1572,9 +1541,6 @@ Test.prototype.tests = { test(crop.width === 10, 'crop width should be 10'); test(crop.height === 11, 'crop height should be 11'); - /* - * test crop partial setter - */ darth.setCrop({ x: 12 }); @@ -1611,6 +1577,19 @@ Test.prototype.tests = { test(crop.width === 14, 'crop width should be 14'); test(crop.height === 15, 'crop height should be 15'); + darth.setAttrs({ + x: 200, + y: 60, + image: imageObj, + width: 100, + height: 100, + offset: [50, 30], + crop: [135, 7, 167, 134], + draggable: true + }); + + //document.body.appendChild(layer.bufferCanvas.element) + }; imageObj.src = '../assets/darth-vader.jpg'; }, @@ -1630,8 +1609,7 @@ Test.prototype.tests = { x: 10, y: 10, image: imageObj, - draggable: true, - stroke: 'red' + draggable: true }); layer.add(darth); @@ -1669,8 +1647,6 @@ Test.prototype.tests = { //height: 300, image: imageObj, draggable: true, - stroke: 'red', - strokeWidth: 5, rotationDeg: 10, scale: 0.3 }); @@ -1823,7 +1799,7 @@ Test.prototype.tests = { color: 'black', blur: 3, offset: [3, 1], - alpha: 0.3 + opacity: 0.3 } }); @@ -1844,6 +1820,7 @@ Test.prototype.tests = { setTimeout(function() { sprite.stop(); }, 3000); + //document.body.appendChild(layer.bufferCanvas.element) }; imageObj.src = '../assets/scorpion-sprite.png'; }, @@ -1902,12 +1879,19 @@ Test.prototype.tests = { strokeWidth: 5, x: 50, y: -120 + }); - + layer.add(cachedShape); - layer.draw(); - warn(urls[0] === layer.toDataURL(), 'layer data url is incorrect'); + cachedShape.createImageBuffer(function() { + + layer.draw(); + + warn(urls[0] === layer.toDataURL(), 'layer data url is incorrect'); + + document.body.appendChild(layer.bufferCanvas.element) + }); } }); @@ -1926,6 +1910,8 @@ Test.prototype.tests = { test(Kinetic.Type._isElement(imageObj), 'stage toImage() should be an image object'); } }); + + //document.body.appendChild(layer.bufferCanvas.element) }, 'SHAPE - add polygon': function(containerId) { var stage = new Kinetic.Stage({ @@ -1996,18 +1982,9 @@ Test.prototype.tests = { draggable: true }); - // test that default detection type is pixel - test(line.getDetectionType() === 'pixel', 'dection type should be pixel'); - layer.add(line); stage.add(layer); - line.saveImageData(); - - line.on('dragend', function() { - line.saveImageData(); - }); - line.setPoints([1, 2, 3, 4]); test(line.getPoints()[0].x === 1, 'first point x should be 1'); @@ -2226,7 +2203,7 @@ Test.prototype.tests = { color: 'black', blur: 10, offset: [20, 20], - alpha: 0.5 + opacity: 0.5 }, draggable: true }); @@ -2378,7 +2355,7 @@ Test.prototype.tests = { height: 200 }); var layer = new Kinetic.Layer(); - var darth = new Kinetic.Image({ + var lion = new Kinetic.Image({ x: 200, y: 40, image: imageObj, @@ -2387,12 +2364,15 @@ Test.prototype.tests = { color: 'black', blur: 10, offset: [20, 20], - alpha: 0.2 + opacity: 0.2 } }); - layer.add(darth); - stage.add(layer); + layer.add(lion); + lion.createImageBuffer(function() { + stage.add(layer); + }); + //document.body.appendChild(layer.bufferCanvas.element); }; imageObj.src = '../assets/lion.png'; @@ -2523,52 +2503,6 @@ Test.prototype.tests = { layer.add(rect); stage.add(layer); }, - 'NODE - test pixel detection setter and getter': function(containerId) { - var stage = new Kinetic.Stage({ - container: containerId, - width: 578, - height: 200 - }); - - var layer = new Kinetic.Layer({ - rotationDeg: 20 - }); - var star = new Kinetic.Star({ - x: 200, - y: 100, - numPoints: 10, - innerRadius: 40, - outerRadius: 70, - fill: 'green', - stroke: 'blue', - strokeWidth: 20, - detectionType: 'pixel', - draggable: true - }); - - star.on('mouseover', function() { - log('mouseover'); - }); - - star.on('mouseout', function() { - log('mouseout'); - }); - - star.on('dragend', function() { - this.saveImageData(); - }); - - layer.add(star); - stage.add(layer); - - star.saveImageData(); - - test(star.getDetectionType() === 'pixel', 'detection type should be pixel'); - star.setDetectionType('path'); - test(star.getDetectionType() === 'path', 'detection type should be path'); - star.setDetectionType('pixel'); - test(star.getDetectionType() === 'pixel', 'detection type should be pixel'); - }, 'SHAPE - test intersects()': function(containerId) { var stage = new Kinetic.Stage({ container: containerId, @@ -2592,27 +2526,28 @@ Test.prototype.tests = { test(rect.intersects({ x: 200, y: 100 - }) === true, 'problem with point in shape'); + }) === true, '(200,100) should intersect the shape'); test(rect.intersects({ - x: 199, - y: 99 - }) === false, 'intersects with point in shape'); + x: 197, + y: 97 + }) === false, '(197, 97) should not intersect the shape'); test(rect.intersects({ x: 250, y: 125 - }) === true, 'intersects with point in shape'); + }) === true, '(250, 125) should intersect the shape'); test(rect.intersects({ x: 300, y: 150 - }) === true, 'intersects with point in shape'); + }) === true, '(300, 150) should intersect the shape'); test(rect.intersects({ - x: 301, - y: 151 - }) === false, 'intersects with point in shape'); + x: 303, + y: 153 + }) === false, '(303, 153) should not intersect the shape'); + }, 'CONTAINER - node type selector': function(containerId) { var stage = new Kinetic.Stage({ @@ -2694,11 +2629,10 @@ Test.prototype.tests = { color: 'black', blur: 1, offset: [10, 10], - alpha: 0.2 + opacity: 0.2 }, cornerRadius: 10, - draggable: true, - detectionType: 'path' + draggable: true }); // center text box @@ -2730,7 +2664,6 @@ Test.prototype.tests = { test(text.getShadow().color === 'black', 'text box shadow color should be black'); test(text.getCornerRadius() === 10, 'text box corner radius should be 10'); test(text.getDraggable() === true, 'text should be draggable'); - test(text.getDetectionType() === 'path', 'text detection type should be path'); test(text.getBoxWidth() === 400, 'box width should be 400'); test(text.getBoxHeight() === 100, 'box height should be 100'); @@ -2757,7 +2690,6 @@ Test.prototype.tests = { }); text.setCornerRadius(20); text.setDraggable(false); - text.setDetectionType('pixel'); test(text.getX() === 1, 'text box x should be 1'); test(text.getY() === 2, 'text box y should be 2'); @@ -2777,8 +2709,11 @@ Test.prototype.tests = { test(text.getShadow().color === 'green', 'text box shadow color should be green'); test(text.getCornerRadius() === 20, 'text box corner radius should be 20'); test(text.getDraggable() === false, 'text draggable should be false'); - test(text.getDetectionType() === 'pixel', 'text detection type should be pixel'); + //document.body.appendChild(layer.bufferCanvas.element) + + //layer.setListening(false); + layer.drawBuffer(); }, 'SHAPE - text multi line': function(containerId) { var stage = new Kinetic.Stage({ @@ -2809,7 +2744,7 @@ Test.prototype.tests = { color: 'black', blur: 1, offset: [10, 10], - alpha: 0.2 + opacity: 0.2 }, cornerRadius: 10, draggable: true, @@ -4083,7 +4018,7 @@ Test.prototype.tests = { test(circle.isVisible() === true, 'circle should be visible'); }, - 'SHAPE - set shape alpha to 0.5': function(containerId) { + 'SHAPE - set shape opacity to 0.5': function(containerId) { var stage = new Kinetic.Stage({ container: containerId, width: 578, @@ -4099,11 +4034,11 @@ Test.prototype.tests = { strokeWidth: 4 }); - circle.setAlpha(0.5); + circle.setOpacity(0.5); layer.add(circle); stage.add(layer); }, - 'SHAPE - set shape alpha to 0.5 then back to 1': function(containerId) { + 'SHAPE - set shape opacity to 0.5 then back to 1': function(containerId) { var stage = new Kinetic.Stage({ container: containerId, width: 578, @@ -4119,16 +4054,16 @@ Test.prototype.tests = { strokeWidth: 4 }); - circle.setAlpha(0.5); + circle.setOpacity(0.5); layer.add(circle); stage.add(layer); - test(circle.getAbsoluteAlpha() === 0.5, 'abs alpha should be 0.5'); + test(circle.getAbsoluteOpacity() === 0.5, 'abs opacity should be 0.5'); - circle.setAlpha(1); + circle.setOpacity(1); layer.draw(); - test(circle.getAbsoluteAlpha() === 1, 'abs alpha should be 1'); + test(circle.getAbsoluteOpacity() === 1, 'abs opacity should be 1'); }, //////////////////////////////////////////////////////////////////////// // LAYERING tests @@ -4713,7 +4648,7 @@ Test.prototype.tests = { color: 'black', blur: 2, offset: [10, 10], - alpha: 0.5 + opacity: 0.5 }, draggable: true }); @@ -4763,7 +4698,7 @@ Test.prototype.tests = { color: 'maroon', blur: 2, offset: [10, 10], - alpha: 0.5 + opacity: 0.5 }, draggable: true }); @@ -4814,18 +4749,20 @@ Test.prototype.tests = { path.on('mouseover', function() { this.setFill('red'); - mapLayer.draw(); + mapLayer.drawScene(); }); path.on('mouseout', function() { this.setFill('#ccc'); - mapLayer.draw(); + mapLayer.drawScene(); }); mapLayer.add(path); } stage.add(mapLayer); + + //document.body.appendChild(mapLayer.bufferCanvas.element); }, 'PATH - curved arrow path': function(containerId) { var stage = new Kinetic.Stage({ @@ -5059,6 +4996,8 @@ Test.prototype.tests = { layer.add(group); stage.add(layer); + //document.body.appendChild(layer.bufferCanvas.element) + }, 'PATHHELPER - Able to determine point on line some distance from another point on line': function(containerId) { var stage = new Kinetic.Stage({ @@ -5525,35 +5464,51 @@ Test.prototype.tests = { layer.add(borneo); stage.add(layer); }, - 'JPEG toDataURL() Not Hiding Lower Layers with Black': function(containerId) { - var stage = new Kinetic.Stage({ - container: containerId, - width: 578, - height: 200 - }); - - var layer1 = new Kinetic.Layer(); - var layer2 = new Kinetic.Layer(); - - layer1.add(new Kinetic.Rect({x:10, y:10, width: 25, height: 15, fill: 'red'})); - layer2.add(new Kinetic.Rect({x:50, y:50, width: 15, height: 25, fill: 'green'})); - - stage.add(layer1); - stage.add(layer2); - - stage.toDataURL({ - height: 100, - width: 100, - mimeType: 'image/jpeg', - quality: 0.8, - callback: function(url) { - var imageObj = new Image(); - imageObj.onload = function() { - layer2.add(new Kinetic.Image({x: 200, y: 10, image: imageObj})); - layer2.draw(); - }; - imageObj.src = url; - } - }) - } + 'JPEG toDataURL() Not Hiding Lower Layers with Black': function(containerId) { + var stage = new Kinetic.Stage({ + container: containerId, + width: 578, + height: 200 + }); + + var layer1 = new Kinetic.Layer(); + var layer2 = new Kinetic.Layer(); + + layer1.add(new Kinetic.Rect({ + x: 10, + y: 10, + width: 25, + height: 15, + fill: 'red' + })); + layer2.add(new Kinetic.Rect({ + x: 50, + y: 50, + width: 15, + height: 25, + fill: 'green' + })); + + stage.add(layer1); + stage.add(layer2); + + stage.toDataURL({ + height: 100, + width: 100, + mimeType: 'image/jpeg', + quality: 0.8, + callback: function(url) { + var imageObj = new Image(); + imageObj.onload = function() { + layer2.add(new Kinetic.Image({ + x: 200, + y: 10, + image: imageObj + })); + layer2.draw(); + }; + imageObj.src = url; + } + }) + } };