├── test ├── assets │ ├── lion.png │ ├── bamoon.jpg │ ├── bunny.png │ ├── darth-vader.jpg │ ├── cropped-darth.jpg │ ├── cropped-darth.png │ └── scorpion-sprite.png ├── performance │ ├── random-squares-dev.html │ ├── random-squares-v5.0.1.html │ ├── rotate-star.html │ ├── rotate-cached-star.html │ └── common │ │ └── random-squares.js ├── unit │ ├── Util-test.js │ ├── shapes │ │ ├── Polygon-test.js │ │ ├── Ellipse-test.js │ │ ├── Wedge-test.js │ │ ├── Arc-test.js │ │ ├── Ring-test.js │ │ ├── Blob-test.js │ │ ├── Spline-test.js │ │ ├── Line-test.js │ │ └── Rect-test.js │ ├── filters │ │ ├── Solarize-test.js │ │ ├── Mask-test.js │ │ ├── Noise-test.js │ │ ├── Pixelate-test.js │ │ ├── Threshold-test.js │ │ ├── Posterize-test.js │ │ ├── Enhance-test.js │ │ ├── Invert-test.js │ │ ├── Sepia-test.js │ │ ├── Grayscale-test.js │ │ ├── Emboss-test.js │ │ ├── RGB-test.js │ │ ├── HSL-test.js │ │ ├── Ripple-test.js │ │ ├── Kaleidoscope-test.js │ │ ├── Brighten-test.js │ │ └── HSV-test.js │ ├── Group-test.js │ ├── Canvas-test.js │ ├── plugins │ │ ├── Arrow-test.js │ │ ├── Star-test.js │ │ ├── RegularPolygon-test.js │ │ └── Label-test.js │ ├── FastLayer-test.js │ ├── BaseLayer-test.js │ ├── Collection-test.js │ ├── Global-test.js │ ├── Animation-test.js │ ├── DragAndDrop-test.js │ └── Tween-test.js ├── setStats.js ├── memory │ ├── build-destroy-text.html │ └── build-destroy-star.html ├── memLeakTest.html ├── lib │ └── stats.js ├── runner.js ├── node-runner.js └── runner.html ├── .npmignore ├── doc-includes ├── ContainerParams.txt ├── NodeParams.txt └── ShapeParams.txt ├── .travis.yml ├── .jshintrc ├── src ├── Group.js ├── filters │ ├── Invert.js │ ├── Grayscale.js │ ├── Brighten.js │ ├── Threshold.js │ ├── Noise.js │ ├── Posterize.js │ ├── Solarize.js │ ├── Sepia.js │ ├── RGB.js │ ├── Pixelate.js │ ├── HSV.js │ ├── HSL.js │ └── Enhance.js ├── plugins │ ├── RegularPolygon.js │ ├── Star.js │ └── Arrow.js ├── shapes │ ├── Rect.js │ ├── Circle.js │ ├── Wedge.js │ ├── Arc.js │ ├── Ring.js │ └── Ellipse.js ├── FastLayer.js └── Factory.js ├── .gitignore ├── bower.json ├── bower-template.json ├── package.json ├── nodejs-demo.js ├── README.md └── presentation-schedule.md /test/assets/lion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ericdrowell/KineticJS/HEAD/test/assets/lion.png -------------------------------------------------------------------------------- /test/assets/bamoon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ericdrowell/KineticJS/HEAD/test/assets/bamoon.jpg -------------------------------------------------------------------------------- /test/assets/bunny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ericdrowell/KineticJS/HEAD/test/assets/bunny.png -------------------------------------------------------------------------------- /test/assets/darth-vader.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ericdrowell/KineticJS/HEAD/test/assets/darth-vader.jpg -------------------------------------------------------------------------------- /test/assets/cropped-darth.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ericdrowell/KineticJS/HEAD/test/assets/cropped-darth.jpg -------------------------------------------------------------------------------- /test/assets/cropped-darth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ericdrowell/KineticJS/HEAD/test/assets/cropped-darth.png -------------------------------------------------------------------------------- /test/assets/scorpion-sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ericdrowell/KineticJS/HEAD/test/assets/scorpion-sprite.png -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | dist 2 | doc-includes 3 | jsdoc-master 4 | src 5 | docs 6 | test 7 | .travis.yml 8 | Gruntfile.js 9 | bower-template.json 10 | bower.json 11 | presentation-schedule.md 12 | *.sublime-project 13 | *.sublime-workspace -------------------------------------------------------------------------------- /doc-includes/ContainerParams.txt: -------------------------------------------------------------------------------- 1 | * @param {Object} [config.clip] set clip 2 | * @param {Number} [config.clipX] set clip x 3 | * @param {Number} [config.clipY] set clip y 4 | * @param {Number} [config.clipWidth] set clip width 5 | * @param {Number} [config.clipHeight] set clip height 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.11' 4 | branches: 5 | only: 6 | - master 7 | before_script: 8 | - npm install 9 | script: 10 | - grunt test 11 | deploy: 12 | provider: npm 13 | email: lavrton@gmail.com 14 | api_key: 15 | secure: rYqyBhn3K8tnt/XK6RFodBiIsIqwmUuPBEAOQxRt/elK4F7BVC+ba7/xgsvdFLP+Bqn4sS8b/YjfxUqm0lfxoph3qvvyKZ+qjuGCXBtvbY8EgGqX3FJKq/LJp8Vu4encCUOpI3PWXQEB+0OrC8ntKnBn1L1WP6lzDbRHj49e9ew= 16 | on: 17 | tags: true 18 | repo: ericdrowell/KineticJS 19 | -------------------------------------------------------------------------------- /test/performance/random-squares-dev.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /test/unit/Util-test.js: -------------------------------------------------------------------------------- 1 | suite('Util', function(){ 2 | var util; 3 | 4 | test('get()', function(){ 5 | assert.equal(Kinetic.Util.get(1, 2), 1); 6 | assert.equal(Kinetic.Util.get(undefined, 2), 2); 7 | assert.equal(Kinetic.Util.get(undefined, {foo:'bar'}).foo, 'bar'); 8 | }); 9 | 10 | test('test _getRGBString()', function(){ 11 | 12 | assert.equal(Kinetic.Util._getRGBAString({}), 'rgba(0,0,0,1)'); 13 | 14 | assert.equal(Kinetic.Util._getRGBAString({ 15 | red: 100, 16 | green: 150, 17 | blue: 200, 18 | alpha: 0.5 19 | }), 'rgba(100,150,200,0.5)'); 20 | 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "curly": true, 3 | "immed": true, 4 | "latedef": true, 5 | "newcap": true, 6 | "noarg": true, 7 | "sub": true, 8 | "undef": true, 9 | "boss": true, 10 | "eqnull": true, 11 | "node": true, 12 | "latedef": true, 13 | "quotmark": "single", 14 | "unused": true, 15 | "trailing" : true, 16 | "laxbreak" : true, 17 | "globals": { 18 | "Kinetic": false, 19 | "document": false, 20 | "window" : false, 21 | "navigator" : false, 22 | "define" : false, 23 | "Image" : false, 24 | "assert" : false, 25 | "test": false, 26 | "addStage" : false 27 | } 28 | } -------------------------------------------------------------------------------- /test/unit/shapes/Polygon-test.js: -------------------------------------------------------------------------------- 1 | suite('Polygon', function() { 2 | test('add polygon', function() { 3 | var stage = addStage(); 4 | 5 | var layer = new Kinetic.Layer(); 6 | var points = [73,192,73,160,340,23,500,109,499,139,342,93]; 7 | 8 | var poly = new Kinetic.Line({ 9 | points: points, 10 | fill: 'green', 11 | stroke: 'blue', 12 | strokeWidth: 5, 13 | closed: true 14 | }); 15 | 16 | layer.add(poly); 17 | stage.add(layer); 18 | 19 | assert.equal(poly.getClassName(), 'Line'); 20 | }); 21 | }); -------------------------------------------------------------------------------- /test/performance/random-squares-v5.0.1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/Group.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | Kinetic.Util.addMethods(Kinetic.Group, { 3 | ___init: function(config) { 4 | this.nodeType = 'Group'; 5 | // call super constructor 6 | Kinetic.Container.call(this, config); 7 | }, 8 | _validateAdd: function(child) { 9 | var type = child.getType(); 10 | if (type !== 'Group' && type !== 'Shape') { 11 | Kinetic.Util.error('You may only add groups and shapes to groups.'); 12 | } 13 | } 14 | }); 15 | Kinetic.Util.extend(Kinetic.Group, Kinetic.Container); 16 | 17 | Kinetic.Collection.mapMethods(Kinetic.Group); 18 | })(); 19 | -------------------------------------------------------------------------------- /src/filters/Invert.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | /** 3 | * Invert Filter 4 | * @function 5 | * @memberof Kinetic.Filters 6 | * @param {Object} imageData 7 | * @example 8 | * node.cache(); 9 | * node.filters([Kinetic.Filters.Invert]); 10 | */ 11 | Kinetic.Filters.Invert = function(imageData) { 12 | var data = imageData.data, 13 | len = data.length, 14 | i; 15 | 16 | for(i = 0; i < len; i += 4) { 17 | // red 18 | data[i] = 255 - data[i]; 19 | // green 20 | data[i + 1] = 255 - data[i + 1]; 21 | // blue 22 | data[i + 2] = 255 - data[i + 2]; 23 | } 24 | }; 25 | })(); -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | documentation 3 | analysis 4 | node_modules 5 | bower_components 6 | phantomjs.exe 7 | docs 8 | jsdoc-template 9 | test/sandbox.html 10 | 11 | # Numerous always-ignore extensions 12 | *.diff 13 | *.err 14 | *.orig 15 | *.log 16 | *.rej 17 | *.swo 18 | *.swp 19 | *.vi 20 | *~ 21 | *.sass-cache 22 | 23 | # OS or Editor folders 24 | .DS_Store 25 | Thumbs.db 26 | .cache 27 | .project 28 | .settings 29 | .tmproj 30 | *.esproj 31 | nbproject 32 | *.sublime-project 33 | *.sublime-workspace 34 | *.md.html 35 | 36 | # Dreamweaver added files 37 | _notes 38 | dwsync.xml 39 | 40 | # Komodo 41 | *.komodoproject 42 | .komodotools 43 | 44 | # Folders to ignore 45 | .hg 46 | .svn 47 | .CVS 48 | intermediate 49 | publish 50 | .idea 51 | 52 | -------------------------------------------------------------------------------- /src/filters/Grayscale.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | /** 3 | * Grayscale Filter 4 | * @function 5 | * @memberof Kinetic.Filters 6 | * @param {Object} imageData 7 | * @example 8 | * node.cache(); 9 | * node.filters([Kinetic.Filters.Grayscale]); 10 | */ 11 | Kinetic.Filters.Grayscale = function(imageData) { 12 | var data = imageData.data, 13 | len = data.length, 14 | i, brightness; 15 | 16 | for(i = 0; i < len; i += 4) { 17 | brightness = 0.34 * data[i] + 0.5 * data[i + 1] + 0.16 * data[i + 2]; 18 | // red 19 | data[i] = brightness; 20 | // green 21 | data[i + 1] = brightness; 22 | // blue 23 | data[i + 2] = brightness; 24 | } 25 | }; 26 | })(); 27 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "KineticJS", 3 | "version": "5.1.1", 4 | "homepage": "http://kineticjs.com/", 5 | "authors": [ 6 | "Eric Rowell" 7 | ], 8 | "description": "KineticJS is an HTML5 Canvas JavaScript framework that enables high performance animations, transitions, node nesting, layering, filtering, caching, event handling for desktop and mobile applications, and much more.", 9 | "keywords": [ 10 | "canvas", 11 | "animations", 12 | "graphic", 13 | "html5" 14 | ], 15 | "license": "MIT", 16 | "ignore": [ 17 | "**/.*", 18 | "test", 19 | "tests", 20 | "doc-includes", 21 | "src", 22 | "*.yml", 23 | ".jshitrc", 24 | ".npmignore", 25 | ".gitignore", 26 | "Gruntfile.js", 27 | "bower-template.json", 28 | "server.js", 29 | "presentation-schedule.md" 30 | ], 31 | "main": "kinetic.js" 32 | } 33 | -------------------------------------------------------------------------------- /bower-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "KineticJS", 3 | "version": "@@version", 4 | "homepage": "http://kineticjs.com/", 5 | "authors": [ 6 | "Eric Rowell" 7 | ], 8 | "description": "KineticJS is an HTML5 Canvas JavaScript framework that enables high performance animations, transitions, node nesting, layering, filtering, caching, event handling for desktop and mobile applications, and much more.", 9 | "keywords": [ 10 | "canvas", 11 | "animations", 12 | "graphic", 13 | "html5" 14 | ], 15 | "license": "MIT", 16 | "ignore": [ 17 | "**/.*", 18 | "test", 19 | "tests", 20 | "doc-includes", 21 | "src", 22 | "*.yml", 23 | ".jshitrc", 24 | ".npmignore", 25 | ".gitignore", 26 | "Gruntfile.js", 27 | "bower-template.json", 28 | "server.js", 29 | "presentation-schedule.md" 30 | ], 31 | "main": "kinetic.js" 32 | } 33 | -------------------------------------------------------------------------------- /test/setStats.js: -------------------------------------------------------------------------------- 1 | window.requestAnimFrame = (function(callback){ 2 | return window.requestAnimationFrame || 3 | window.webkitRequestAnimationFrame || 4 | window.mozRequestAnimationFrame || 5 | window.oRequestAnimationFrame || 6 | window.msRequestAnimationFrame || 7 | function(callback){ 8 | window.setTimeout(callback, 1000 / 30); 9 | }; 10 | })(); 11 | 12 | stats = new Stats(); 13 | stats.setMode(0); 14 | stats.domElement.style.position = 'fixed'; 15 | stats.domElement.style.left = '0px'; 16 | stats.domElement.style.top = '0px'; 17 | document.body.appendChild(stats.domElement); 18 | 19 | function setStats() { 20 | stats.begin(); 21 | requestAnimFrame(function(){ 22 | stats.end(); 23 | setStats(); 24 | }); 25 | } 26 | 27 | setStats(); -------------------------------------------------------------------------------- /test/unit/filters/Solarize-test.js: -------------------------------------------------------------------------------- 1 | suite('Solarize', function() { 2 | 3 | // ====================================================== 4 | test('solarize', function(done) { 5 | var stage = addStage(); 6 | 7 | var imageObj = new Image(); 8 | imageObj.onload = function() { 9 | var layer = new Kinetic.Layer(); 10 | darth = new Kinetic.Image({ 11 | x: 10, 12 | y: 10, 13 | image: imageObj, 14 | draggable: true 15 | }); 16 | 17 | layer.add(darth); 18 | stage.add(layer); 19 | darth.cache(); 20 | darth.filters([Kinetic.Filters.Solarize]); 21 | 22 | 23 | layer.draw(); 24 | 25 | 26 | done(); 27 | 28 | }; 29 | imageObj.src = 'assets/darth-vader.jpg'; 30 | //imageObj.src = 'assets/lion.png'; 31 | 32 | }); 33 | 34 | }); -------------------------------------------------------------------------------- /test/unit/shapes/Ellipse-test.js: -------------------------------------------------------------------------------- 1 | suite('Ellipse', function(){ 2 | 3 | // ====================================================== 4 | test('add ellipse', function(){ 5 | var stage = addStage(); 6 | var layer = new Kinetic.Layer(); 7 | var ellipse = new Kinetic.Ellipse({ 8 | x: stage.getWidth() / 2, 9 | y: stage.getHeight() / 2, 10 | radius: {x:70, y:35}, 11 | fill: 'green', 12 | stroke: 'black', 13 | strokeWidth: 8 14 | }); 15 | layer.add(ellipse); 16 | stage.add(layer); 17 | assert.equal(ellipse.getClassName(), 'Ellipse'); 18 | 19 | var trace = layer.getContext().getTrace(); 20 | assert.equal(trace, 'clearRect(0,0,578,200);save();transform(1,0,0,1,289,100);beginPath();save();scale(1,0.5);arc(0,0,70,0,6.283,false);restore();closePath();fillStyle=green;fill();lineWidth=8;strokeStyle=black;stroke();restore();'); 21 | }); 22 | }); -------------------------------------------------------------------------------- /test/unit/shapes/Wedge-test.js: -------------------------------------------------------------------------------- 1 | suite('Wedge', function() { 2 | // ====================================================== 3 | test('add wedge', function() { 4 | var stage = addStage(); 5 | var layer = new Kinetic.Layer(); 6 | var wedge = new Kinetic.Wedge({ 7 | x: 100, 8 | y: 100, 9 | radius: 70, 10 | angle: 180 * 0.4, 11 | fill: 'green', 12 | stroke: 'black', 13 | strokeWidth: 4, 14 | name: 'myCircle', 15 | draggable: true 16 | }); 17 | 18 | layer.add(wedge); 19 | stage.add(layer); 20 | 21 | assert.equal(wedge.getClassName(), 'Wedge'); 22 | 23 | var trace = layer.getContext().getTrace(); 24 | //console.log(trace); 25 | assert.equal(trace, 'clearRect(0,0,578,200);save();transform(1,0,0,1,100,100);beginPath();arc(0,0,70,0,1.257,false);lineTo(0,0);closePath();fillStyle=green;fill();lineWidth=4;strokeStyle=black;stroke();restore();'); 26 | }); 27 | 28 | }); -------------------------------------------------------------------------------- /test/unit/shapes/Arc-test.js: -------------------------------------------------------------------------------- 1 | suite('Arc', function() { 2 | // ====================================================== 3 | test('add arc', function() { 4 | var stage = addStage(); 5 | var layer = new Kinetic.Layer(); 6 | var arc = new Kinetic.Arc({ 7 | x: 100, 8 | y: 100, 9 | innerRadius: 50, 10 | outerRadius: 80, 11 | angle: 90, 12 | fill: 'green', 13 | stroke: 'black', 14 | strokeWidth: 4, 15 | name: 'myArc', 16 | draggable: true 17 | }); 18 | 19 | layer.add(arc); 20 | stage.add(layer); 21 | 22 | assert.equal(arc.getClassName(), 'Arc'); 23 | 24 | var trace = layer.getContext().getTrace(); 25 | //console.log(trace); 26 | assert.equal(trace, 'clearRect(0,0,578,200);save();transform(1,0,0,1,100,100);beginPath();arc(0,0,80,0,1.571,false);arc(0,0,50,1.571,0,true);closePath();fillStyle=green;fill();lineWidth=4;strokeStyle=black;stroke();restore();'); 27 | }); 28 | 29 | }); -------------------------------------------------------------------------------- /test/unit/Group-test.js: -------------------------------------------------------------------------------- 1 | suite('Group', function() { 2 | 3 | // ====================================================== 4 | test('cache group with text', function() { 5 | var stage = addStage(); 6 | 7 | var layer = new Kinetic.Layer(); 8 | var group = new Kinetic.Group({ 9 | draggable : true, 10 | x: 100, 11 | y: 40 12 | }); 13 | var text = new Kinetic.Text({ 14 | text : "some text", 15 | fontSize: 20, 16 | fill: "black", 17 | y : 50 18 | }); 19 | 20 | var rect = new Kinetic.Rect({ 21 | height : 100, 22 | width : 100, 23 | stroke : "#00B80C", 24 | strokeWidth: 10, 25 | cornerRadius: 1 26 | }); 27 | group.add(text); 28 | group.add(rect); 29 | layer.add(group); 30 | 31 | stage.add(layer); 32 | 33 | group.cache({ 34 | x: -5, 35 | y: -5, 36 | width : 110, 37 | height : 110, 38 | drawBorder: true 39 | }).offsetX(5).offsetY(5); 40 | 41 | stage.draw(); 42 | }); 43 | }); 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /test/unit/Canvas-test.js: -------------------------------------------------------------------------------- 1 | suite('Canvas', function() { 2 | // ====================================================== 3 | test('pixel ratio', function() { 4 | var stage = addStage(); 5 | 6 | var layer = new Kinetic.Layer(); 7 | 8 | var circle = new Kinetic.Circle({ 9 | x: 578/2, 10 | y: 100, 11 | radius: 70, 12 | fill: 'green', 13 | stroke: 'blue', 14 | strokeWidth: 4 15 | }); 16 | 17 | layer.add(circle); 18 | stage.add(layer); 19 | 20 | 21 | stage.setWidth(578/2); 22 | stage.setHeight(100); 23 | 24 | stage.draw(); 25 | 26 | layer.getCanvas().setPixelRatio(1); 27 | assert.equal(layer.getCanvas().getPixelRatio(), 1); 28 | assert.equal(layer.getCanvas().width, 289); 29 | assert.equal(layer.getCanvas().height, 100); 30 | 31 | layer.getCanvas().setPixelRatio(2); 32 | assert.equal(layer.getCanvas().getPixelRatio(), 2); 33 | assert.equal(layer.getCanvas().width, 578); 34 | assert.equal(layer.getCanvas().height, 200); 35 | 36 | layer.draw(); 37 | 38 | }); 39 | }); -------------------------------------------------------------------------------- /test/unit/plugins/Arrow-test.js: -------------------------------------------------------------------------------- 1 | suite('Arrow', function() { 2 | // ====================================================== 3 | test('add arrow', function() { 4 | var stage = addStage(); 5 | var layer = new Kinetic.Layer(); 6 | 7 | var arrow = new Kinetic.Arrow({ 8 | points: [73,160, 340, 23], 9 | stroke: 'blue', 10 | fill : 'blue', 11 | strokeWidth: 1, 12 | draggable: true, 13 | tension: 0 14 | }); 15 | 16 | layer.add(arrow); 17 | stage.add(layer); 18 | 19 | arrow.setPoints([1, 2, 3, 4]); 20 | assert.equal(arrow.points()[0], 1); 21 | 22 | arrow.setPoints([5,6,7,8]); 23 | assert.equal(arrow.getPoints()[0], 5); 24 | arrow.setPoints([73, 160, 340, 23, 50,100, 80, 50]); 25 | arrow.tension(0); 26 | 27 | arrow.pointerLength(15); 28 | assert.equal(arrow.pointerLength(), 15); 29 | 30 | arrow.pointerWidth(15); 31 | assert.equal(arrow.pointerWidth(), 15); 32 | 33 | assert.equal(arrow.getClassName(), 'Arrow'); 34 | 35 | layer.draw(); 36 | showHit(layer); 37 | }); 38 | }); -------------------------------------------------------------------------------- /test/unit/filters/Mask-test.js: -------------------------------------------------------------------------------- 1 | suite('Mask', function() { 2 | 3 | // ====================================================== 4 | test('basic', function(done) { 5 | var stage = addStage(); 6 | 7 | var imageObj = new Image(); 8 | imageObj.onload = function() { 9 | 10 | var layer = new Kinetic.Layer({ 11 | throttle: 999 12 | }); 13 | var bamoon = new Kinetic.Image({ 14 | x: 0, 15 | y: 0, 16 | image: imageObj, 17 | draggable: true 18 | }), 19 | filtered = new Kinetic.Image({ 20 | x: 300, 21 | y: 0, 22 | image: imageObj, 23 | draggable: true 24 | }); 25 | 26 | layer.add(bamoon); 27 | layer.add(filtered); 28 | stage.add(layer); 29 | 30 | filtered.cache(); 31 | filtered.filters([Kinetic.Filters.Mask]); 32 | filtered.threshold(10); 33 | 34 | layer.draw(); 35 | 36 | done(); 37 | 38 | }; 39 | imageObj.src = 'assets/bamoon.jpg'; 40 | 41 | }); 42 | 43 | }); -------------------------------------------------------------------------------- /doc-includes/NodeParams.txt: -------------------------------------------------------------------------------- 1 | @param {Number} [config.x] 2 | * @param {Number} [config.y] 3 | * @param {Number} [config.width] 4 | * @param {Number} [config.height] 5 | * @param {Boolean} [config.visible] 6 | * @param {Boolean} [config.listening] whether or not the node is listening for events 7 | * @param {String} [config.id] unique id 8 | * @param {String} [config.name] non-unique name 9 | * @param {Number} [config.opacity] determines node opacity. Can be any number between 0 and 1 10 | * @param {Object} [config.scale] set scale 11 | * @param {Number} [config.scaleX] set scale x 12 | * @param {Number} [config.scaleY] set scale y 13 | * @param {Number} [config.rotation] rotation in degrees 14 | * @param {Object} [config.offset] offset from center point and rotation point 15 | * @param {Number} [config.offsetX] set offset x 16 | * @param {Number} [config.offsetY] set offset y 17 | * @param {Boolean} [config.draggable] makes the node draggable. When stages are draggable, you can drag and drop 18 | * the entire stage by dragging any portion of the stage 19 | * @param {Number} [config.dragDistance] 20 | * @param {Function} [config.dragBoundFunc] -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kinetic", 3 | "version": "5.1.1", 4 | "devDependencies": { 5 | "grunt-contrib-jshint": "0.10.0", 6 | "grunt-contrib-nodeunit": "0.4.1", 7 | "grunt-contrib-uglify": "0.6.0", 8 | "grunt-contrib-concat": "0.5.0", 9 | "grunt-replace": "0.7.9", 10 | "grunt-contrib-clean": "0.6.0", 11 | "mocha": "1.21.4", 12 | "chai": "1.9.2", 13 | "phantomjs": "1.9.10", 14 | "mocha-phantomjs": "3.5.0", 15 | "grunt-cli": "0.1.13", 16 | "grunt": "0.4.5", 17 | "connect": "3.2.0", 18 | "grunt-contrib-copy": "~0.6.0", 19 | "jsdoc": "~3.3.0-alpha9", 20 | "grunt-mocha-phantomjs": "~0.6.0", 21 | "grunt-contrib-watch": "~0.6.1", 22 | "grunt-shell": "~1.1.1" 23 | }, 24 | "keywords": [ 25 | "canvas", 26 | "animations", 27 | "graphic", 28 | "html5" 29 | ], 30 | "browser": { 31 | "canvas": false, 32 | "jsdom": false 33 | }, 34 | "bugs": { 35 | "url": "https://github.com/ericdrowell/KineticJS/issues" 36 | }, 37 | "readmeFilename": "README.md", 38 | "main": "kinetic.js", 39 | "repository": { 40 | "type": "git", 41 | "url": "git://github.com/ericdrowell/KineticJS.git" 42 | }, 43 | "author": "Eric Rowell", 44 | "license": "MIT" 45 | } 46 | -------------------------------------------------------------------------------- /test/unit/FastLayer-test.js: -------------------------------------------------------------------------------- 1 | suite('FastLayer', function() { 2 | 3 | // ====================================================== 4 | test('basic render', function() { 5 | var stage = addStage(); 6 | 7 | var layer = new Kinetic.FastLayer(); 8 | 9 | var circle = new Kinetic.Circle({ 10 | x: 100, 11 | y: stage.getHeight() / 2, 12 | radius: 70, 13 | fill: 'green', 14 | stroke: 'black', 15 | strokeWidth: 4 16 | }); 17 | 18 | layer.add(circle); 19 | stage.add(layer); 20 | 21 | 22 | }); 23 | 24 | test('cache shape on fast layer', function(){ 25 | var stage = addStage(); 26 | var layer = new Kinetic.FastLayer(); 27 | 28 | var circle = new Kinetic.Circle({ 29 | x: 74, 30 | y: 74, 31 | radius: 70, 32 | fill: 'green', 33 | stroke: 'black', 34 | strokeWidth: 4, 35 | name: 'myCircle' 36 | }); 37 | 38 | layer.add(circle); 39 | stage.add(layer); 40 | 41 | 42 | circle.cache({ 43 | x: -74, 44 | y: -74, 45 | width: 148, 46 | height: 148 47 | }).offset({ 48 | x: 74, 49 | y: 74 50 | }); 51 | 52 | layer.draw(); 53 | 54 | 55 | }); 56 | 57 | 58 | }); -------------------------------------------------------------------------------- /test/unit/BaseLayer-test.js: -------------------------------------------------------------------------------- 1 | suite('BaseLayer', function() { 2 | 3 | // ====================================================== 4 | test('width and height', function() { 5 | var stage = addStage(); 6 | 7 | var layer = new Kinetic.FastLayer(); 8 | assert.equal(layer.width(), undefined, 'while layer is not on stage width is undefined'); 9 | assert.equal(layer.height(), undefined, 'while layer is not on stage height is undefined'); 10 | 11 | layer.width(10); 12 | assert.equal(layer.width(), undefined, 'while layer is not on stage changing width doing nothing'); 13 | layer.height(10); 14 | assert.equal(layer.height(), undefined, 'while layer is not on stage changing height doing nothing'); 15 | stage.add(layer); 16 | 17 | assert.equal(layer.width(), stage.width(), 'while layer is on stage width is stage`s width'); 18 | assert.equal(layer.height(), stage.height(), 'while layer is on stage height is stage`s height'); 19 | 20 | layer.width(10); 21 | assert.equal(layer.width(), stage.width(), 'while layer is on stage changing width doing nothing'); 22 | layer.height(10); 23 | assert.equal(layer.height(), stage.height(), 'while layer is on stage changing height doing nothing'); 24 | }); 25 | }); -------------------------------------------------------------------------------- /src/filters/Brighten.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | /** 3 | * Brighten Filter. 4 | * @function 5 | * @memberof Kinetic.Filters 6 | * @param {Object} imageData 7 | * @example 8 | * node.cache(); 9 | * node.filters([Kinetic.Filters.Brighten]); 10 | * node.brightness(0.8); 11 | */ 12 | Kinetic.Filters.Brighten = function(imageData) { 13 | var brightness = this.brightness() * 255, 14 | data = imageData.data, 15 | len = data.length, 16 | i; 17 | 18 | for(i = 0; i < len; i += 4) { 19 | // red 20 | data[i] += brightness; 21 | // green 22 | data[i + 1] += brightness; 23 | // blue 24 | data[i + 2] += brightness; 25 | } 26 | }; 27 | 28 | Kinetic.Factory.addGetterSetter(Kinetic.Node, 'brightness', 0, null, Kinetic.Factory.afterSetFilter); 29 | /** 30 | * get/set filter brightness. The brightness is a number between -1 and 1.  Positive values 31 | * brighten the pixels and negative values darken them. Use with {@link Kinetic.Filters.Brighten} filter. 32 | * @name brightness 33 | * @method 34 | * @memberof Kinetic.Node.prototype 35 | * @param {Number} brightness value between -1 and 1 36 | * @returns {Number} 37 | */ 38 | 39 | })(); 40 | -------------------------------------------------------------------------------- /test/performance/rotate-star.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 56 | 57 | -------------------------------------------------------------------------------- /src/filters/Threshold.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | /** 4 | * Threshold Filter. Pushes any value above the mid point to 5 | * the max and any value below the mid point to the min. 6 | * This affects the alpha channel. 7 | * @function 8 | * @name Threshold 9 | * @memberof Kinetic.Filters 10 | * @param {Object} imageData 11 | * @author ippo615 12 | * @example 13 | * node.cache(); 14 | * node.filters([Kinetic.Filters.Threshold]); 15 | * node.threshold(0.1); 16 | */ 17 | 18 | Kinetic.Filters.Threshold = function (imageData) { 19 | var level = this.threshold() * 255, 20 | data = imageData.data, 21 | len = data.length, 22 | i; 23 | 24 | for (i = 0; i < len; i += 1) { 25 | data[i] = data[i] < level ? 0 : 255; 26 | } 27 | }; 28 | 29 | Kinetic.Factory.addGetterSetter(Kinetic.Node, 'threshold', 0.5, null, Kinetic.Factory.afterSetFilter); 30 | 31 | /** 32 | * get/set threshold. Must be a value between 0 and 1. Use with {@link Kinetic.Filters.Threshold} or {@link Kinetic.Filters.Mask} filter. 33 | * @name threshold 34 | * @method 35 | * @memberof Kinetic.Node.prototype 36 | * @param {Number} threshold 37 | * @returns {Number} 38 | */ 39 | })(); -------------------------------------------------------------------------------- /src/filters/Noise.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | /** 4 | * Noise Filter. Randomly adds or substracts to the color channels 5 | * @function 6 | * @name Noise 7 | * @memberof Kinetic.Filters 8 | * @param {Object} imageData 9 | * @author ippo615 10 | * @example 11 | * node.cache(); 12 | * node.filters([Kinetic.Filters.Noise]); 13 | * node.noise(0.8); 14 | */ 15 | Kinetic.Filters.Noise = function (imageData) { 16 | var amount = this.noise() * 255, 17 | data = imageData.data, 18 | nPixels = data.length, 19 | half = amount / 2, 20 | i; 21 | 22 | for (i = 0; i < nPixels; i += 4) { 23 | data[i + 0] += half - 2 * half * Math.random(); 24 | data[i + 1] += half - 2 * half * Math.random(); 25 | data[i + 2] += half - 2 * half * Math.random(); 26 | } 27 | }; 28 | 29 | Kinetic.Factory.addGetterSetter(Kinetic.Node, 'noise', 0.2, null, Kinetic.Factory.afterSetFilter); 30 | 31 | /** 32 | * get/set noise amount. Must be a value between 0 and 1. Use with {@link Kinetic.Filters.Noise} filter. 33 | * @name noise 34 | * @method 35 | * @memberof Kinetic.Node.prototype 36 | * @param {Number} noise 37 | * @returns {Number} 38 | */ 39 | })(); 40 | -------------------------------------------------------------------------------- /test/unit/filters/Noise-test.js: -------------------------------------------------------------------------------- 1 | suite('Noise', function () { 2 | 3 | // ====================================================== 4 | test('noise tween', function(done) { 5 | var stage = addStage(); 6 | 7 | var imageObj = new Image(); 8 | imageObj.onload = function() { 9 | 10 | var layer = new Kinetic.Layer(); 11 | darth = new Kinetic.Image({ 12 | x: 10, 13 | y: 10, 14 | image: imageObj, 15 | draggable: true 16 | }); 17 | 18 | layer.add(darth); 19 | stage.add(layer); 20 | 21 | darth.cache(); 22 | darth.filters([Kinetic.Filters.Noise]); 23 | darth.noise(1); 24 | layer.draw(); 25 | 26 | var tween = new Kinetic.Tween({ 27 | node: darth, 28 | duration: 5.0, 29 | noise: 0, 30 | easing: Kinetic.Easings.EaseInOut 31 | }); 32 | 33 | darth.on('mouseover', function() { 34 | tween.play(); 35 | }); 36 | 37 | darth.on('mouseout', function() { 38 | tween.reverse(); 39 | }); 40 | 41 | done(); 42 | }; 43 | imageObj.src = 'assets/lion.png'; 44 | 45 | }); 46 | 47 | }); 48 | -------------------------------------------------------------------------------- /test/unit/filters/Pixelate-test.js: -------------------------------------------------------------------------------- 1 | suite('Pixelate', function () { 2 | 3 | // ====================================================== 4 | test('tween pixelate', function(done) { 5 | var stage = addStage(); 6 | 7 | var imageObj = new Image(); 8 | imageObj.onload = function() { 9 | 10 | var layer = new Kinetic.Layer(); 11 | lion = new Kinetic.Image({ 12 | x: 10, 13 | y: 10, 14 | image: imageObj, 15 | draggable: true 16 | }); 17 | 18 | layer.add(lion); 19 | stage.add(layer); 20 | 21 | lion.cache(); 22 | lion.filters([Kinetic.Filters.Pixelate]); 23 | lion.pixelSize(16); 24 | layer.draw(); 25 | 26 | var tween = new Kinetic.Tween({ 27 | node: lion, 28 | duration: 3.0, 29 | pixelSize: 1, 30 | easing: Kinetic.Easings.EaseInOut 31 | }); 32 | 33 | lion.on('mouseover', function() { 34 | tween.play(); 35 | }); 36 | 37 | lion.on('mouseout', function() { 38 | tween.reverse(); 39 | }); 40 | 41 | done(); 42 | 43 | }; 44 | imageObj.src = 'assets/lion.png'; 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /test/memory/build-destroy-text.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 59 | 60 | -------------------------------------------------------------------------------- /test/unit/filters/Threshold-test.js: -------------------------------------------------------------------------------- 1 | suite('Threshold', function () { 2 | // ====================================================== 3 | test('image tween', function(done) { 4 | var stage = addStage(); 5 | 6 | var imageObj = new Image(); 7 | imageObj.onload = function() { 8 | 9 | var layer = new Kinetic.Layer(); 10 | darth = new Kinetic.Image({ 11 | x: 10, 12 | y: 10, 13 | image: imageObj, 14 | draggable: true 15 | }); 16 | 17 | layer.add(darth); 18 | stage.add(layer); 19 | 20 | darth.cache(); 21 | darth.filters([Kinetic.Filters.Threshold]); 22 | darth.threshold(1); 23 | layer.draw(); 24 | 25 | var tween = new Kinetic.Tween({ 26 | node: darth, 27 | duration: 5.0, 28 | threshold: 0, 29 | easing: Kinetic.Easings.EaseInOut 30 | }); 31 | 32 | darth.on('mouseover', function() { 33 | tween.play(); 34 | }); 35 | 36 | darth.on('mouseout', function() { 37 | tween.reverse(); 38 | }); 39 | 40 | done(); 41 | }; 42 | imageObj.src = 'assets/darth-vader.jpg'; 43 | 44 | }); 45 | 46 | }); 47 | -------------------------------------------------------------------------------- /test/unit/filters/Posterize-test.js: -------------------------------------------------------------------------------- 1 | suite('Posterize', function () { 2 | 3 | // ====================================================== 4 | test('on image tween', function(done) { 5 | var stage = addStage(); 6 | 7 | var imageObj = new Image(); 8 | imageObj.onload = function() { 9 | 10 | var layer = new Kinetic.Layer(); 11 | darth = new Kinetic.Image({ 12 | x: 10, 13 | y: 10, 14 | image: imageObj, 15 | draggable: true 16 | }); 17 | 18 | layer.add(darth); 19 | stage.add(layer); 20 | 21 | darth.cache(); 22 | darth.filters([Kinetic.Filters.Posterize]); 23 | darth.levels(0.2); 24 | layer.draw(); 25 | 26 | var tween = new Kinetic.Tween({ 27 | node: darth, 28 | duration: 1.0, 29 | levels: 0, 30 | easing: Kinetic.Easings.Linear 31 | }); 32 | 33 | darth.on('mouseover', function() { 34 | tween.play(); 35 | }); 36 | 37 | darth.on('mouseout', function() { 38 | tween.reverse(); 39 | }); 40 | 41 | done(); 42 | }; 43 | imageObj.src = 'assets/darth-vader.jpg'; 44 | 45 | }); 46 | 47 | }); 48 | -------------------------------------------------------------------------------- /src/filters/Posterize.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | /** 4 | * Posterize Filter. Adjusts the channels so that there are no more 5 | * than n different values for that channel. This is also applied 6 | * to the alpha channel. 7 | * @function 8 | * @name Posterize 9 | * @author ippo615 10 | * @memberof Kinetic.Filters 11 | * @param {Object} imageData 12 | * @example 13 | * node.cache(); 14 | * node.filters([Kinetic.Filters.Posterize]); 15 | * node.levels(0.8); 16 | */ 17 | 18 | Kinetic.Filters.Posterize = function (imageData) { 19 | // level must be between 1 and 255 20 | var levels = Math.round(this.levels() * 254) + 1, 21 | data = imageData.data, 22 | len = data.length, 23 | scale = (255 / levels), 24 | i; 25 | 26 | for (i = 0; i < len; i += 1) { 27 | data[i] = Math.floor(data[i] / scale) * scale; 28 | } 29 | }; 30 | 31 | Kinetic.Factory.addGetterSetter(Kinetic.Node, 'levels', 0.5, null, Kinetic.Factory.afterSetFilter); 32 | 33 | /** 34 | * get/set levels. Must be a number between 0 and 1. Use with {@link Kinetic.Filters.Posterize} filter. 35 | * @name levels 36 | * @method 37 | * @memberof Kinetic.Node.prototype 38 | * @param {Number} level between 0 and 1 39 | * @returns {Number} 40 | */ 41 | })(); -------------------------------------------------------------------------------- /test/memory/build-destroy-star.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 63 | 64 | -------------------------------------------------------------------------------- /test/performance/rotate-cached-star.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 63 | 64 | -------------------------------------------------------------------------------- /src/filters/Solarize.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | /** 3 | * Solarize Filter 4 | * Pixastic Lib - Solarize filter - v0.1.0 5 | * Copyright (c) 2008 Jacob Seidelin, jseidelin@nihilogic.dk, http://blog.nihilogic.dk/ 6 | * License: [http://www.pixastic.com/lib/license.txt] 7 | * @function 8 | * @name Solarize 9 | * @memberof Kinetic.Filters 10 | * @param {Object} imageData 11 | * @example 12 | * node.cache(); 13 | * node.filters([Kinetic.Filters.Solarize]); 14 | */ 15 | Kinetic.Filters.Solarize = function (imageData) { 16 | var data = imageData.data, 17 | w = imageData.width, 18 | h = imageData.height, 19 | w4 = w*4, 20 | y = h; 21 | 22 | do { 23 | var offsetY = (y-1)*w4; 24 | var x = w; 25 | do { 26 | var offset = offsetY + (x-1)*4; 27 | var r = data[offset]; 28 | var g = data[offset+1]; 29 | var b = data[offset+2]; 30 | 31 | if (r > 127) { 32 | r = 255 - r; 33 | } 34 | if (g > 127) { 35 | g = 255 - g; 36 | } 37 | if (b > 127) { 38 | b = 255 - b; 39 | } 40 | 41 | data[offset] = r; 42 | data[offset+1] = g; 43 | data[offset+2] = b; 44 | } while (--x); 45 | } while (--y); 46 | }; 47 | })(); 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/filters/Sepia.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | /** 3 | * Sepia Filter 4 | * Based on: Pixastic Lib - Sepia filter - v0.1.0 5 | * Copyright (c) 2008 Jacob Seidelin, jseidelin@nihilogic.dk, http://blog.nihilogic.dk/ 6 | * @function 7 | * @name Sepia 8 | * @memberof Kinetic.Filters 9 | * @param {Object} imageData 10 | * @author Jacob Seidelin 11 | * @license MPL v1.1 [http://www.pixastic.com/lib/license.txt] 12 | * @example 13 | * node.cache(); 14 | * node.filters([Kinetic.Filters.Sepia]); 15 | */ 16 | Kinetic.Filters.Sepia = function (imageData) { 17 | var data = imageData.data, 18 | w = imageData.width, 19 | y = imageData.height, 20 | w4 = w*4, 21 | offsetY, x, offset, or, og, ob, r, g, b; 22 | 23 | do { 24 | offsetY = (y-1)*w4; 25 | x = w; 26 | do { 27 | offset = offsetY + (x-1)*4; 28 | 29 | or = data[offset]; 30 | og = data[offset+1]; 31 | ob = data[offset+2]; 32 | 33 | r = or * 0.393 + og * 0.769 + ob * 0.189; 34 | g = or * 0.349 + og * 0.686 + ob * 0.168; 35 | b = or * 0.272 + og * 0.534 + ob * 0.131; 36 | 37 | data[offset] = r > 255 ? 255 : r; 38 | data[offset+1] = g > 255 ? 255 : g; 39 | data[offset+2] = b > 255 ? 255 : b; 40 | data[offset+3] = data[offset+3]; 41 | } while (--x); 42 | } while (--y); 43 | }; 44 | })(); 45 | -------------------------------------------------------------------------------- /test/memLeakTest.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | KineticJS Memory leak test 5 | 6 | 7 | 8 | Check memory before tests. Open console. Run "run()". See memory after. 9 |
10 | 11 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /test/unit/shapes/Ring-test.js: -------------------------------------------------------------------------------- 1 | suite('Ring', function() { 2 | 3 | // ====================================================== 4 | test('add ring', function() { 5 | var stage = addStage(); 6 | var layer = new Kinetic.Layer(); 7 | var ring = new Kinetic.Ring({ 8 | x: stage.getWidth() / 2, 9 | y: stage.getHeight() / 2, 10 | innerRadius: 50, 11 | outerRadius: 90, 12 | fill: 'green', 13 | stroke: 'black', 14 | strokeWidth: 4, 15 | draggable: true 16 | }); 17 | layer.add(ring); 18 | stage.add(layer); 19 | assert.equal(ring.getClassName(), 'Ring'); 20 | 21 | var trace = layer.getContext().getTrace(); 22 | assert.equal(trace, 'clearRect(0,0,578,200);save();transform(1,0,0,1,289,100);beginPath();arc(0,0,50,0,6.283,false);moveTo(90,0);arc(0,0,90,6.283,0,true);closePath();fillStyle=green;fill();lineWidth=4;strokeStyle=black;stroke();restore();'); 23 | }); 24 | 25 | // ====================================================== 26 | // test for https://github.com/ericdrowell/KineticJS/issues/987 27 | test('ring attrs sync', function() { 28 | var stage = addStage(); 29 | var layer = new Kinetic.Layer(); 30 | var ring = new Kinetic.Ring({ 31 | name: 'ring', 32 | x: 30, 33 | y: 50, 34 | width: 50, 35 | height: 50, 36 | innerRadius: 15, 37 | outerRadius: 30, 38 | fill: 'green', 39 | stroke: 'black', 40 | strokeWidth: 4, 41 | draggable: true 42 | }); 43 | layer.add(ring); 44 | stage.add(layer); 45 | 46 | 47 | var cring = ring.clone(); 48 | assert.equal(cring.outerRadius(), ring.outerRadius()); 49 | 50 | assert.equal(ring.attrs.width, ring.outerRadius() * 2); 51 | }); 52 | 53 | }); -------------------------------------------------------------------------------- /nodejs-demo.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'), 2 | Kinetic = require('./dist/kinetic-dev'); 3 | 4 | 5 | // Create stage. Container parameter is not required in NodeJS. 6 | var stage = new Kinetic.Stage({ 7 | width : 100, 8 | height : 100 9 | }); 10 | 11 | var layer = new Kinetic.Layer(); 12 | stage.add(layer); 13 | var rect = new Kinetic.Rect({ 14 | width : 100, 15 | height : 100, 16 | x : 50, 17 | y : 50, 18 | fill : 'green' 19 | }); 20 | var text = new Kinetic.Text({ 21 | text : 'Generated inside node js', 22 | x : 20, 23 | y : 20, 24 | fill : 'black' 25 | }); 26 | layer.add(rect).add(text); 27 | layer.draw(); 28 | stage.setSize({ 29 | width : 200, 30 | height : 200 31 | }); 32 | 33 | // check tween works 34 | var tween = new Kinetic.Tween({ 35 | node : rect, 36 | duration : 1, 37 | x : -50 38 | }); 39 | tween.play(); 40 | 41 | // After tween we want to convert stage to dataURL 42 | setTimeout(function(){ 43 | stage.toDataURL({ 44 | callback: function(data){ 45 | // Then add result to stage 46 | var img = new Kinetic.window.Image(); 47 | img.onload = function() { 48 | var image = new Kinetic.Image({ 49 | image : img, 50 | x : 10, 51 | y : 50 52 | }); 53 | layer.add(image); 54 | layer.draw(); 55 | // save stage image as file 56 | stage.toDataURL({ 57 | callback: function(data){ 58 | var base64Data = data.replace(/^data:image\/png;base64,/, ''); 59 | fs.writeFile('out.png', base64Data, 'base64', function(err) { 60 | err && console.log(err); 61 | console.log('See out.png'); 62 | }); 63 | } 64 | }); 65 | }; 66 | img.src = data; 67 | 68 | } 69 | }); 70 | }, 1050); -------------------------------------------------------------------------------- /test/unit/plugins/Star-test.js: -------------------------------------------------------------------------------- 1 | suite('Star', function() { 2 | // ====================================================== 3 | test('add five point star', function() { 4 | var stage = addStage(); 5 | 6 | var layer = new Kinetic.Layer(); 7 | 8 | var star = new Kinetic.Star({ 9 | x: 200, 10 | y: 100, 11 | numPoints: 5, 12 | innerRadius: 40, 13 | outerRadius: 70, 14 | fill: 'green', 15 | stroke: 'blue', 16 | strokeWidth: 5, 17 | name: 'foobar', 18 | center: { 19 | x: 0, 20 | y: -70 21 | }, 22 | scale: { 23 | x: 0.5, 24 | y: 0.5 25 | } 26 | }); 27 | 28 | layer.add(star); 29 | stage.add(layer); 30 | 31 | assert.equal(star.getClassName(), 'Star'); 32 | }); 33 | 34 | // ====================================================== 35 | test('add star with line join and shadow', function() { 36 | var stage = addStage(); 37 | var layer = new Kinetic.Layer(); 38 | 39 | var rect = new Kinetic.Rect({ 40 | x: 250, 41 | y: 75, 42 | width: 100, 43 | height: 100, 44 | fill: 'red' 45 | }); 46 | 47 | var star = new Kinetic.Star({ 48 | x: 200, 49 | y: 100, 50 | numPoints: 5, 51 | innerRadius: 40, 52 | outerRadius: 70, 53 | fill: 'green', 54 | stroke: 'blue', 55 | strokeWidth: 5, 56 | lineJoin: "round", 57 | shadowColor: 'black', 58 | shadowBlur: 10, 59 | shadowOffset: [20, 20], 60 | shadowOpacity: 0.5, 61 | draggable: true 62 | }); 63 | 64 | layer.add(rect); 65 | layer.add(star); 66 | 67 | stage.add(layer); 68 | 69 | assert.equal(star.getLineJoin(), 'round'); 70 | star.setLineJoin('bevel'); 71 | assert.equal(star.getLineJoin(), 'bevel'); 72 | 73 | star.setLineJoin('round'); 74 | }); 75 | }); -------------------------------------------------------------------------------- /test/unit/Collection-test.js: -------------------------------------------------------------------------------- 1 | suite('Collection', function(){ 2 | var util; 3 | 4 | test('test collection method mapping', function(){ 5 | // Node method 6 | assert.notEqual(Kinetic.Collection.prototype.on, undefined); 7 | 8 | // Layer method 9 | assert.notEqual(Kinetic.Collection.prototype.getContext, undefined); 10 | 11 | // Container method 12 | assert.notEqual(Kinetic.Collection.prototype.hasChildren, undefined); 13 | 14 | // Shape method 15 | assert.notEqual(Kinetic.Collection.prototype.strokeWidth, undefined); 16 | }); 17 | 18 | test('add circle to stage', function(){ 19 | var stage = addStage(); 20 | var layer = new Kinetic.Layer(); 21 | var circle1 = new Kinetic.Circle({ 22 | x: 100, 23 | y: 100, 24 | radius: 70, 25 | fill: 'green', 26 | stroke: 'black', 27 | strokeWidth: 4, 28 | name: 'myCircle', 29 | draggable: true 30 | }); 31 | 32 | 33 | var circle2 = new Kinetic.Circle({ 34 | x:300, 35 | y: 100, 36 | radius: 70, 37 | fill: 'red', 38 | stroke: 'black', 39 | strokeWidth: 4, 40 | name: 'myCircle', 41 | draggable: true 42 | }); 43 | 44 | 45 | layer.add(circle1).add(circle2); 46 | stage.add(layer); 47 | 48 | layer.find('Circle').fill('blue').stroke('green'); 49 | layer.draw(); 50 | 51 | //console.log(layer.getContext().getTrace()); 52 | 53 | assert.equal(layer.getContext().getTrace(),'clearRect(0,0,578,200);save();transform(1,0,0,1,100,100);beginPath();arc(0,0,70,0,6.283,false);closePath();fillStyle=green;fill();lineWidth=4;strokeStyle=black;stroke();restore();save();transform(1,0,0,1,300,100);beginPath();arc(0,0,70,0,6.283,false);closePath();fillStyle=red;fill();lineWidth=4;strokeStyle=black;stroke();restore();clearRect(0,0,578,200);save();transform(1,0,0,1,100,100);beginPath();arc(0,0,70,0,6.283,false);closePath();fillStyle=blue;fill();lineWidth=4;strokeStyle=green;stroke();restore();save();transform(1,0,0,1,300,100);beginPath();arc(0,0,70,0,6.283,false);closePath();fillStyle=blue;fill();lineWidth=4;strokeStyle=green;stroke();restore();'); 54 | 55 | 56 | }); 57 | }); -------------------------------------------------------------------------------- /test/lib/stats.js: -------------------------------------------------------------------------------- 1 | // stats.js - http://github.com/mrdoob/stats.js 2 | var Stats=function(){var l=Date.now(),m=l,g=0,n=Infinity,o=0,h=0,p=Infinity,q=0,r=0,s=0,f=document.createElement("div");f.id="stats";f.addEventListener("mousedown",function(b){b.preventDefault();t(++s%2)},!1);f.style.cssText="width:80px;opacity:0.9;cursor:pointer";var a=document.createElement("div");a.id="fps";a.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#002";f.appendChild(a);var i=document.createElement("div");i.id="fpsText";i.style.cssText="color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px"; 3 | i.innerHTML="FPS";a.appendChild(i);var c=document.createElement("div");c.id="fpsGraph";c.style.cssText="position:relative;width:74px;height:30px;background-color:#0ff";for(a.appendChild(c);74>c.children.length;){var j=document.createElement("span");j.style.cssText="width:1px;height:30px;float:left;background-color:#113";c.appendChild(j)}var d=document.createElement("div");d.id="ms";d.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#020;display:none";f.appendChild(d);var k=document.createElement("div"); 4 | k.id="msText";k.style.cssText="color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px";k.innerHTML="MS";d.appendChild(k);var e=document.createElement("div");e.id="msGraph";e.style.cssText="position:relative;width:74px;height:30px;background-color:#0f0";for(d.appendChild(e);74>e.children.length;)j=document.createElement("span"),j.style.cssText="width:1px;height:30px;float:left;background-color:#131",e.appendChild(j);var t=function(b){s=b;switch(s){case 0:a.style.display= 5 | "block";d.style.display="none";break;case 1:a.style.display="none",d.style.display="block"}};return{REVISION:11,domElement:f,setMode:t,begin:function(){l=Date.now()},end:function(){var b=Date.now();g=b-l;n=Math.min(n,g);o=Math.max(o,g);k.textContent=g+" MS ("+n+"-"+o+")";var a=Math.min(30,30-30*(g/200));e.appendChild(e.firstChild).style.height=a+"px";r++;b>m+1E3&&(h=Math.round(1E3*r/(b-m)),p=Math.min(p,h),q=Math.max(q,h),i.textContent=h+" FPS ("+p+"-"+q+")",a=Math.min(30,30-30*(h/100)),c.appendChild(c.firstChild).style.height= 6 | a+"px",m=b,r=0);return b},update:function(){l=this.end()}}}; -------------------------------------------------------------------------------- /test/unit/filters/Enhance-test.js: -------------------------------------------------------------------------------- 1 | suite('Enhance', function () { 2 | // ====================================================== 3 | test('on image', function(done) { 4 | var stage = addStage(); 5 | 6 | var imageObj = new Image(); 7 | imageObj.onload = function() { 8 | 9 | var layer = new Kinetic.Layer(); 10 | var filt = new Kinetic.Image({ 11 | x: 10, 12 | y: 10, 13 | image: imageObj, 14 | draggable: true 15 | }); 16 | var orig = new Kinetic.Image({ 17 | x: 200, 18 | y: 10, 19 | image: imageObj, 20 | draggable: true 21 | }); 22 | 23 | layer.add(filt); 24 | layer.add(orig); 25 | stage.add(layer); 26 | 27 | filt.cache(); 28 | filt.enhance(1.0); 29 | filt.filters([Kinetic.Filters.Enhance]); 30 | layer.draw(); 31 | 32 | done(); 33 | }; 34 | imageObj.src = 'assets/scorpion-sprite.png'; 35 | 36 | }); 37 | 38 | // ====================================================== 39 | test('tween enhance', function(done) { 40 | var stage = addStage(); 41 | 42 | var imageObj = new Image(); 43 | imageObj.onload = function() { 44 | 45 | var layer = new Kinetic.Layer(); 46 | darth = new Kinetic.Image({ 47 | x: 10, 48 | y: 10, 49 | image: imageObj, 50 | draggable: true 51 | }); 52 | 53 | layer.add(darth); 54 | stage.add(layer); 55 | 56 | darth.cache(); 57 | darth.filters([Kinetic.Filters.Enhance]); 58 | darth.enhance(-1); 59 | layer.draw(); 60 | 61 | var tween = new Kinetic.Tween({ 62 | node: darth, 63 | duration: 2.0, 64 | enhance: 1.0, 65 | easing: Kinetic.Easings.EaseInOut 66 | }); 67 | 68 | darth.on('mouseover', function() { 69 | tween.play(); 70 | }); 71 | 72 | darth.on('mouseout', function() { 73 | tween.reverse(); 74 | }); 75 | 76 | done(); 77 | 78 | }; 79 | imageObj.src = 'assets/scorpion-sprite.png'; 80 | }); 81 | }); 82 | -------------------------------------------------------------------------------- /test/unit/filters/Invert-test.js: -------------------------------------------------------------------------------- 1 | suite('Invert', function() { 2 | // ====================================================== 3 | test('basic', function(done) { 4 | var stage = addStage(); 5 | 6 | var imageObj = new Image(); 7 | imageObj.onload = function() { 8 | 9 | var layer = new Kinetic.Layer(); 10 | darth = new Kinetic.Image({ 11 | x: 10, 12 | y: 10, 13 | image: imageObj, 14 | draggable: true 15 | }); 16 | 17 | layer.add(darth); 18 | stage.add(layer); 19 | 20 | darth.cache(); 21 | darth.filters([Kinetic.Filters.Invert]); 22 | layer.draw(); 23 | 24 | done(); 25 | }; 26 | imageObj.src = 'assets/darth-vader.jpg'; 27 | 28 | }); 29 | 30 | // ====================================================== 31 | test('crop', function(done) { 32 | var stage = addStage(); 33 | 34 | var imageObj = new Image(); 35 | imageObj.onload = function() { 36 | 37 | var layer = new Kinetic.Layer(); 38 | darth = new Kinetic.Image({ 39 | x: 10, 40 | y: 10, 41 | image: imageObj, 42 | crop: {x:128, y:48, width:256, height:128}, 43 | draggable: true 44 | }); 45 | 46 | layer.add(darth); 47 | stage.add(layer); 48 | 49 | darth.cache(); 50 | darth.filters([Kinetic.Filters.Invert]); 51 | layer.draw(); 52 | 53 | done(); 54 | 55 | }; 56 | imageObj.src = 'assets/darth-vader.jpg'; 57 | }); 58 | 59 | // ====================================================== 60 | test('transparancy', function(done) { 61 | var stage = addStage(); 62 | 63 | var imageObj = new Image(); 64 | imageObj.onload = function() { 65 | 66 | var layer = new Kinetic.Layer(); 67 | darth = new Kinetic.Image({ 68 | x: 10, 69 | y: 10, 70 | image: imageObj, 71 | draggable: true 72 | }); 73 | 74 | layer.add(darth); 75 | stage.add(layer); 76 | 77 | darth.cache(); 78 | darth.filters([Kinetic.Filters.Invert]); 79 | layer.draw(); 80 | 81 | done(); 82 | }; 83 | imageObj.src = 'assets/lion.png'; 84 | 85 | }); 86 | }); -------------------------------------------------------------------------------- /test/unit/filters/Sepia-test.js: -------------------------------------------------------------------------------- 1 | suite('Filter Sepia', function() { 2 | // ====================================================== 3 | test('basic', function(done) { 4 | var stage = addStage(); 5 | 6 | var imageObj = new Image(); 7 | imageObj.onload = function() { 8 | 9 | var layer = new Kinetic.Layer(); 10 | darth = new Kinetic.Image({ 11 | x: 10, 12 | y: 10, 13 | image: imageObj, 14 | draggable: true 15 | }); 16 | 17 | layer.add(darth); 18 | stage.add(layer); 19 | 20 | darth.cache(); 21 | darth.filters([Kinetic.Filters.Sepia]); 22 | layer.draw(); 23 | 24 | done(); 25 | }; 26 | imageObj.src = 'assets/darth-vader.jpg'; 27 | 28 | }); 29 | 30 | // ====================================================== 31 | test('crop', function(done) { 32 | var stage = addStage(); 33 | 34 | var imageObj = new Image(); 35 | imageObj.onload = function() { 36 | 37 | var layer = new Kinetic.Layer(); 38 | darth = new Kinetic.Image({ 39 | x: 10, 40 | y: 10, 41 | image: imageObj, 42 | crop: {x:128, y:48, width:256, height:128}, 43 | draggable: true 44 | }); 45 | 46 | 47 | layer.add(darth); 48 | stage.add(layer); 49 | 50 | darth.cache(); 51 | darth.filters([Kinetic.Filters.Sepia]); 52 | layer.draw(); 53 | 54 | done(); 55 | 56 | }; 57 | imageObj.src = 'assets/darth-vader.jpg'; 58 | }); 59 | 60 | // ====================================================== 61 | test('with transparency', function(done) { 62 | var stage = addStage(); 63 | 64 | var imageObj = new Image(); 65 | imageObj.onload = function() { 66 | 67 | var layer = new Kinetic.Layer(); 68 | darth = new Kinetic.Image({ 69 | x: 10, 70 | y: 10, 71 | image: imageObj, 72 | draggable: true 73 | }); 74 | 75 | layer.add(darth); 76 | stage.add(layer); 77 | 78 | darth.cache(); 79 | darth.filters([Kinetic.Filters.Sepia]); 80 | layer.draw(); 81 | 82 | done(); 83 | }; 84 | imageObj.src = 'assets/lion.png'; 85 | 86 | }); 87 | }); -------------------------------------------------------------------------------- /test/unit/filters/Grayscale-test.js: -------------------------------------------------------------------------------- 1 | suite('Grayscale', function() { 2 | // ====================================================== 3 | test('basic', function(done) { 4 | var stage = addStage(); 5 | 6 | var imageObj = new Image(); 7 | imageObj.onload = function() { 8 | 9 | var layer = new Kinetic.Layer(); 10 | darth = new Kinetic.Image({ 11 | x: 10, 12 | y: 10, 13 | image: imageObj, 14 | draggable: true 15 | }); 16 | 17 | layer.add(darth); 18 | stage.add(layer); 19 | 20 | darth.cache(); 21 | darth.filters([Kinetic.Filters.Grayscale]); 22 | layer.draw(); 23 | 24 | done(); 25 | }; 26 | imageObj.src = 'assets/darth-vader.jpg'; 27 | 28 | }); 29 | 30 | // ====================================================== 31 | test('crop', function(done) { 32 | var stage = addStage(); 33 | 34 | var imageObj = new Image(); 35 | imageObj.onload = function() { 36 | 37 | var layer = new Kinetic.Layer(); 38 | darth = new Kinetic.Image({ 39 | x: 10, 40 | y: 10, 41 | image: imageObj, 42 | crop: {x:128, y:48, width:256, height:128}, 43 | draggable: true 44 | }); 45 | 46 | layer.add(darth); 47 | stage.add(layer); 48 | 49 | darth.cache(); 50 | darth.filters([Kinetic.Filters.Grayscale]); 51 | layer.draw(); 52 | 53 | done(); 54 | 55 | }; 56 | imageObj.src = 'assets/darth-vader.jpg'; 57 | }); 58 | 59 | // ====================================================== 60 | test('with transparency', function(done) { 61 | var stage = addStage(); 62 | 63 | var imageObj = new Image(); 64 | imageObj.onload = function() { 65 | 66 | var layer = new Kinetic.Layer(); 67 | darth = new Kinetic.Image({ 68 | x: 10, 69 | y: 10, 70 | image: imageObj, 71 | draggable: true 72 | }); 73 | 74 | layer.add(darth); 75 | stage.add(layer); 76 | 77 | darth.cache(); 78 | darth.filters([Kinetic.Filters.Grayscale]); 79 | layer.draw(); 80 | 81 | done(); 82 | }; 83 | imageObj.src = 'assets/lion.png'; 84 | 85 | }); 86 | 87 | }); -------------------------------------------------------------------------------- /test/unit/plugins/RegularPolygon-test.js: -------------------------------------------------------------------------------- 1 | suite('RegularPolygon', function() { 2 | // ====================================================== 3 | test('add regular polygon triangle', function() { 4 | var stage = addStage(); 5 | 6 | var layer = new Kinetic.Layer(); 7 | 8 | var poly = new Kinetic.RegularPolygon({ 9 | x: 200, 10 | y: 100, 11 | sides: 3, 12 | radius: 50, 13 | fill: 'green', 14 | stroke: 'blue', 15 | strokeWidth: 5, 16 | name: 'foobar', 17 | center: { 18 | x: 0, 19 | y: -50 20 | } 21 | }); 22 | 23 | layer.add(poly); 24 | stage.add(layer); 25 | 26 | assert.equal(poly.getClassName(), 'RegularPolygon'); 27 | 28 | }); 29 | 30 | // ====================================================== 31 | test('add regular polygon square', function() { 32 | var stage = addStage(); 33 | var layer = new Kinetic.Layer(); 34 | 35 | var poly = new Kinetic.RegularPolygon({ 36 | x: 200, 37 | y: 100, 38 | sides: 4, 39 | radius: 50, 40 | fill: 'green', 41 | stroke: 'blue', 42 | strokeWidth: 5, 43 | name: 'foobar' 44 | }); 45 | 46 | layer.add(poly); 47 | stage.add(layer); 48 | }); 49 | 50 | // ====================================================== 51 | test('add regular polygon pentagon', function() { 52 | var stage = addStage(); 53 | var layer = new Kinetic.Layer(); 54 | 55 | var poly = new Kinetic.RegularPolygon({ 56 | x: 200, 57 | y: 100, 58 | sides: 5, 59 | radius: 50, 60 | fill: 'green', 61 | stroke: 'blue', 62 | strokeWidth: 5, 63 | name: 'foobar' 64 | }); 65 | 66 | layer.add(poly); 67 | stage.add(layer); 68 | }); 69 | 70 | // ====================================================== 71 | test('add regular polygon octogon', function() { 72 | var stage = addStage(); 73 | var layer = new Kinetic.Layer(); 74 | 75 | var poly = new Kinetic.RegularPolygon({ 76 | x: 200, 77 | y: 100, 78 | sides: 8, 79 | radius: 50, 80 | fill: 'green', 81 | stroke: 'blue', 82 | strokeWidth: 5, 83 | name: 'foobar' 84 | }); 85 | 86 | layer.add(poly); 87 | stage.add(layer); 88 | }); 89 | 90 | }); -------------------------------------------------------------------------------- /src/filters/RGB.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | /** 3 | * RGB Filter 4 | * @function 5 | * @name RGB 6 | * @memberof Kinetic.Filters 7 | * @param {Object} imageData 8 | * @author ippo615 9 | * @example 10 | * node.cache(); 11 | * node.filters([Kinetic.Filters.RGB]); 12 | * node.blue(120); 13 | * node.green(200); 14 | */ 15 | Kinetic.Filters.RGB = function (imageData) { 16 | var data = imageData.data, 17 | nPixels = data.length, 18 | red = this.red(), 19 | green = this.green(), 20 | blue = this.blue(), 21 | i, brightness; 22 | 23 | for (i = 0; i < nPixels; i += 4) { 24 | brightness = (0.34 * data[i] + 0.5 * data[i + 1] + 0.16 * data[i + 2])/255; 25 | data[i ] = brightness*red; // r 26 | data[i + 1] = brightness*green; // g 27 | data[i + 2] = brightness*blue; // b 28 | data[i + 3] = data[i + 3]; // alpha 29 | } 30 | }; 31 | 32 | Kinetic.Factory.addGetterSetter(Kinetic.Node, 'red', 0, function(val) { 33 | this._filterUpToDate = false; 34 | if (val > 255) { 35 | return 255; 36 | } 37 | else if (val < 0) { 38 | return 0; 39 | } 40 | else { 41 | return Math.round(val); 42 | } 43 | }); 44 | /** 45 | * get/set filter red value. Use with {@link Kinetic.Filters.RGB} filter. 46 | * @name red 47 | * @method 48 | * @memberof Kinetic.Node.prototype 49 | * @param {Integer} red value between 0 and 255 50 | * @returns {Integer} 51 | */ 52 | 53 | Kinetic.Factory.addGetterSetter(Kinetic.Node, 'green', 0, function(val) { 54 | this._filterUpToDate = false; 55 | if (val > 255) { 56 | return 255; 57 | } 58 | else if (val < 0) { 59 | return 0; 60 | } 61 | else { 62 | return Math.round(val); 63 | } 64 | }); 65 | /** 66 | * get/set filter green value. Use with {@link Kinetic.Filters.RGB} filter. 67 | * @name green 68 | * @method 69 | * @memberof Kinetic.Node.prototype 70 | * @param {Integer} green value between 0 and 255 71 | * @returns {Integer} 72 | */ 73 | 74 | Kinetic.Factory.addGetterSetter(Kinetic.Node, 'blue', 0, Kinetic.Validators.RGBComponent, Kinetic.Factory.afterSetFilter); 75 | /** 76 | * get/set filter blue value. Use with {@link Kinetic.Filters.RGB} filter. 77 | * @name blue 78 | * @method 79 | * @memberof Kinetic.Node.prototype 80 | * @param {Integer} blue value between 0 and 255 81 | * @returns {Integer} 82 | */ 83 | })(); 84 | -------------------------------------------------------------------------------- /src/plugins/RegularPolygon.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | /** 3 | * RegularPolygon constructor.  Examples include triangles, squares, pentagons, hexagons, etc. 4 | * @constructor 5 | * @memberof Kinetic 6 | * @augments Kinetic.Shape 7 | * @param {Object} config 8 | * @param {Number} config.sides 9 | * @param {Number} config.radius 10 | * @@shapeParams 11 | * @@nodeParams 12 | * @example 13 | * var hexagon = new Kinetic.RegularPolygon({ 14 | * x: 100, 15 | * y: 200, 16 | * sides: 6, 17 | * radius: 70, 18 | * fill: 'red', 19 | * stroke: 'black', 20 | * strokeWidth: 4 21 | * }); 22 | */ 23 | Kinetic.RegularPolygon = function(config) { 24 | this.___init(config); 25 | }; 26 | 27 | Kinetic.RegularPolygon.prototype = { 28 | ___init: function(config) { 29 | // call super constructor 30 | Kinetic.Shape.call(this, config); 31 | this.className = 'RegularPolygon'; 32 | this.sceneFunc(this._sceneFunc); 33 | }, 34 | _sceneFunc: function(context) { 35 | var sides = this.attrs.sides, 36 | radius = this.attrs.radius, 37 | n, x, y; 38 | 39 | context.beginPath(); 40 | context.moveTo(0, 0 - radius); 41 | 42 | for(n = 1; n < sides; n++) { 43 | x = radius * Math.sin(n * 2 * Math.PI / sides); 44 | y = -1 * radius * Math.cos(n * 2 * Math.PI / sides); 45 | context.lineTo(x, y); 46 | } 47 | context.closePath(); 48 | context.fillStrokeShape(this); 49 | } 50 | }; 51 | Kinetic.Util.extend(Kinetic.RegularPolygon, Kinetic.Shape); 52 | 53 | // add getters setters 54 | Kinetic.Factory.addGetterSetter(Kinetic.RegularPolygon, 'radius', 0); 55 | 56 | /** 57 | * set radius 58 | * @name setRadius 59 | * @method 60 | * @memberof Kinetic.RegularPolygon.prototype 61 | * @param {Number} radius 62 | */ 63 | 64 | /** 65 | * get radius 66 | * @name getRadius 67 | * @method 68 | * @memberof Kinetic.RegularPolygon.prototype 69 | */ 70 | 71 | Kinetic.Factory.addGetterSetter(Kinetic.RegularPolygon, 'sides', 0); 72 | 73 | /** 74 | * set number of sides 75 | * @name setSides 76 | * @method 77 | * @memberof Kinetic.RegularPolygon.prototype 78 | * @param {int} sides 79 | */ 80 | 81 | /** 82 | * get number of sides 83 | * @name getSides 84 | * @method 85 | * @memberof Kinetic.RegularPolygon.prototype 86 | */ 87 | 88 | Kinetic.Collection.mapMethods(Kinetic.RegularPolygon); 89 | })(); 90 | -------------------------------------------------------------------------------- /src/shapes/Rect.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | /** 3 | * Rect constructor 4 | * @constructor 5 | * @memberof Kinetic 6 | * @augments Kinetic.Shape 7 | * @param {Object} config 8 | * @param {Number} [config.cornerRadius] 9 | * @@shapeParams 10 | * @@nodeParams 11 | * @example 12 | * var rect = new Kinetic.Rect({ 13 | * width: 100, 14 | * height: 50, 15 | * fill: 'red', 16 | * stroke: 'black', 17 | * strokeWidth: 5 18 | * }); 19 | */ 20 | Kinetic.Rect = function(config) { 21 | this.___init(config); 22 | }; 23 | 24 | Kinetic.Rect.prototype = { 25 | ___init: function(config) { 26 | Kinetic.Shape.call(this, config); 27 | this.className = 'Rect'; 28 | this.sceneFunc(this._sceneFunc); 29 | }, 30 | _sceneFunc: function(context) { 31 | var cornerRadius = this.getCornerRadius(), 32 | width = this.getWidth(), 33 | height = this.getHeight(); 34 | 35 | 36 | context.beginPath(); 37 | 38 | if(!cornerRadius) { 39 | // simple rect - don't bother doing all that complicated maths stuff. 40 | context.rect(0, 0, width, height); 41 | } 42 | else { 43 | // arcTo would be nicer, but browser support is patchy (Opera) 44 | context.moveTo(cornerRadius, 0); 45 | context.lineTo(width - cornerRadius, 0); 46 | context.arc(width - cornerRadius, cornerRadius, cornerRadius, Math.PI * 3 / 2, 0, false); 47 | context.lineTo(width, height - cornerRadius); 48 | context.arc(width - cornerRadius, height - cornerRadius, cornerRadius, 0, Math.PI / 2, false); 49 | context.lineTo(cornerRadius, height); 50 | context.arc(cornerRadius, height - cornerRadius, cornerRadius, Math.PI / 2, Math.PI, false); 51 | context.lineTo(0, cornerRadius); 52 | context.arc(cornerRadius, cornerRadius, cornerRadius, Math.PI, Math.PI * 3 / 2, false); 53 | } 54 | context.closePath(); 55 | context.fillStrokeShape(this); 56 | } 57 | }; 58 | 59 | Kinetic.Util.extend(Kinetic.Rect, Kinetic.Shape); 60 | 61 | Kinetic.Factory.addGetterSetter(Kinetic.Rect, 'cornerRadius', 0); 62 | /** 63 | * get/set corner radius 64 | * @name cornerRadius 65 | * @method 66 | * @memberof Kinetic.Rect.prototype 67 | * @param {Number} cornerRadius 68 | * @returns {Number} 69 | * @example 70 | * // get corner radius 71 | * var cornerRadius = rect.cornerRadius(); 72 | * 73 | * // set corner radius 74 | * rect.cornerRadius(10); 75 | */ 76 | 77 | Kinetic.Collection.mapMethods(Kinetic.Rect); 78 | })(); 79 | -------------------------------------------------------------------------------- /test/unit/filters/Emboss-test.js: -------------------------------------------------------------------------------- 1 | suite('Emboss', function() { 2 | 3 | // ====================================================== 4 | test('basic emboss', function(done) { 5 | var stage = addStage(); 6 | 7 | var imageObj = new Image(); 8 | imageObj.onload = function() { 9 | var layer = new Kinetic.Layer(); 10 | darth = new Kinetic.Image({ 11 | x: 10, 12 | y: 10, 13 | image: imageObj, 14 | draggable: true 15 | }); 16 | 17 | layer.add(darth); 18 | stage.add(layer); 19 | darth.cache(); 20 | darth.filters([Kinetic.Filters.Emboss]); 21 | darth.embossStrength(0.5); 22 | darth.embossWhiteLevel(0.8); 23 | darth.embossDirection('top-right'); 24 | 25 | layer.draw(); 26 | 27 | var tween = new Kinetic.Tween({ 28 | node: darth, 29 | duration: 0.6, 30 | embossStrength: 10, 31 | easing: Kinetic.Easings.EaseInOut 32 | }); 33 | 34 | darth.on('mouseover', function() { 35 | tween.play(); 36 | }); 37 | 38 | darth.on('mouseout', function() { 39 | tween.reverse(); 40 | }); 41 | 42 | done(); 43 | 44 | }; 45 | imageObj.src = 'assets/darth-vader.jpg'; 46 | //imageObj.src = 'assets/lion.png'; 47 | 48 | }); 49 | 50 | // ====================================================== 51 | test('blended emboss', function(done) { 52 | var stage = addStage(); 53 | 54 | var imageObj = new Image(); 55 | imageObj.onload = function() { 56 | var layer = new Kinetic.Layer(); 57 | darth = new Kinetic.Image({ 58 | x: 10, 59 | y: 10, 60 | image: imageObj, 61 | draggable: true 62 | }); 63 | 64 | layer.add(darth); 65 | stage.add(layer); 66 | darth.cache(); 67 | darth.filters([Kinetic.Filters.Emboss]); 68 | darth.embossStrength(0.5); 69 | darth.embossWhiteLevel(0.2); 70 | darth.embossBlend(true); 71 | 72 | layer.draw(); 73 | 74 | var tween = new Kinetic.Tween({ 75 | node: darth, 76 | duration: 0.6, 77 | embossStrength: 10, 78 | easing: Kinetic.Easings.EaseInOut 79 | }); 80 | 81 | darth.on('mouseover', function() { 82 | tween.play(); 83 | }); 84 | 85 | darth.on('mouseout', function() { 86 | tween.reverse(); 87 | }); 88 | 89 | done(); 90 | 91 | }; 92 | imageObj.src = 'assets/darth-vader.jpg'; 93 | //imageObj.src = 'assets/lion.png'; 94 | 95 | }); 96 | }); -------------------------------------------------------------------------------- /src/shapes/Circle.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | // the 0.0001 offset fixes a bug in Chrome 27 3 | var PIx2 = (Math.PI * 2) - 0.0001, 4 | CIRCLE = 'Circle'; 5 | 6 | /** 7 | * Circle constructor 8 | * @constructor 9 | * @memberof Kinetic 10 | * @augments Kinetic.Shape 11 | * @param {Object} config 12 | * @param {Number} config.radius 13 | * @@shapeParams 14 | * @@nodeParams 15 | * @example 16 | * // create circle 17 | * var circle = new Kinetic.Circle({ 18 | * radius: 40, 19 | * fill: 'red', 20 | * stroke: 'black' 21 | * strokeWidth: 5 22 | * }); 23 | */ 24 | Kinetic.Circle = function(config) { 25 | this.___init(config); 26 | }; 27 | 28 | Kinetic.Circle.prototype = { 29 | ___init: function(config) { 30 | // call super constructor 31 | Kinetic.Shape.call(this, config); 32 | this.className = CIRCLE; 33 | this.sceneFunc(this._sceneFunc); 34 | }, 35 | _sceneFunc: function(context) { 36 | context.beginPath(); 37 | context.arc(0, 0, this.getRadius(), 0, PIx2, false); 38 | context.closePath(); 39 | context.fillStrokeShape(this); 40 | }, 41 | // implements Shape.prototype.getWidth() 42 | getWidth: function() { 43 | return this.getRadius() * 2; 44 | }, 45 | // implements Shape.prototype.getHeight() 46 | getHeight: function() { 47 | return this.getRadius() * 2; 48 | }, 49 | // implements Shape.prototype.setWidth() 50 | setWidth: function(width) { 51 | Kinetic.Node.prototype.setWidth.call(this, width); 52 | if (this.radius() !== width / 2) { 53 | this.setRadius(width / 2); 54 | } 55 | }, 56 | // implements Shape.prototype.setHeight() 57 | setHeight: function(height) { 58 | Kinetic.Node.prototype.setHeight.call(this, height); 59 | if (this.radius() !== height / 2) { 60 | this.setRadius(height / 2); 61 | } 62 | }, 63 | setRadius : function(val) { 64 | this._setAttr('radius', val); 65 | this.setWidth(val * 2); 66 | this.setHeight(val * 2); 67 | } 68 | }; 69 | Kinetic.Util.extend(Kinetic.Circle, Kinetic.Shape); 70 | 71 | // add getters setters 72 | Kinetic.Factory.addGetter(Kinetic.Circle, 'radius', 0); 73 | Kinetic.Factory.addOverloadedGetterSetter(Kinetic.Circle, 'radius'); 74 | 75 | /** 76 | * get/set radius 77 | * @name radius 78 | * @method 79 | * @memberof Kinetic.Circle.prototype 80 | * @param {Number} radius 81 | * @returns {Number} 82 | * @example 83 | * // get radius 84 | * var radius = circle.radius(); 85 | * 86 | * // set radius 87 | * circle.radius(10); 88 | */ 89 | 90 | Kinetic.Collection.mapMethods(Kinetic.Circle); 91 | })(); 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ##Mothballed 2 | I will no longer be maintaining this repo or the official KineticJS website because I have moved onto other ventures and projects. The latest version of KineticJS, 5.1.0, is very solid and can still be used in production applications. Please feel free to fork the repo if you'd like to make changes. 3 | 4 | ##Concrete.js Alternative 5 | Concrete.js is the lightweight version of KineticJS. It supports perpherial things like hit detection, layering, pixel ratio management, exports, caching, and downloads. While KineticJS is a heavy weight framework based on a scene graph, Concrete.js doesn't have an opinion on whether or not your canvas app requires a scene graph. You can learn more by going to [www.concretejs.com](http://www.concretejs.com) 6 | 7 | Also, you can now find tars of every stable KineticJS build on [www.kineticjs.com](http://www.kineticjs.com) 8 | 9 | #Installation 10 | 11 | * `bower install kineticjs` 12 | * `npm install kinetic` - for Browserify. For nodejs you have to install some [dependencies](#NodeJS) 13 | 14 | ###NodeJS 15 | 16 | Support of NodeJS is experimental. 17 | 18 | We are using [node-canvas](https://github.com/LearnBoost/node-canvas) to create canvas element. 19 | 20 | 1. Install node-canvas [https://github.com/LearnBoost/node-canvas/wiki/_pages](https://github.com/LearnBoost/node-canvas/wiki/_pages) 21 | 2. `npm install jsdom` 22 | 3. `npm install kinetic` 23 | 24 | See file `nodejs-demo.js` for example. 25 | 26 | #Dev environment 27 | 28 | Before doing all dev stuff make sure you have node installed. After that, run `npm install --dev` in the main directory to install the node module dependencies. 29 | 30 | Run `grunt --help` to see all build options. 31 | 32 | ##Building the KineticJS Framework 33 | 34 | To build a development version of the framework, run `grunt dev`. To run a full build, which also produces the minified version and the individually minified modules for the custom build, run `grunt full`. You can also run `grunt beta` to generate a beta version. 35 | 36 | If you add a file in the src directory, be sure to add the filename to the sourceFiles array variable in Gruntfile.js. 37 | 38 | ##Testing 39 | 40 | [![Build Status](https://travis-ci.org/ericdrowell/KineticJS.png)](https://travis-ci.org/ericdrowell/KineticJS) 41 | 42 | KineticJS uses Mocha for testing. 43 | 44 | * If you need run test only one time run `grunt test`. 45 | * While developing it is easy to use `grunt server` with watch task. Just run it and go to [http://localhost:8080/test/runner.html](http://localhost:8080/test/runner.html). After src file change kinetic-dev.js will be automatically created, so you just need refresh test the page. 46 | 47 | KineticJS is covered with hundreds of tests and well over a thousand assertions. KineticJS uses TDD (test driven development) which means that every new feature or bug fix is accompanied with at least one new test. 48 | 49 | ##Generate documentation 50 | 51 | Run `grunt docs` which will build the documentation files and place them in the docs folder. 52 | -------------------------------------------------------------------------------- /src/FastLayer.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | Kinetic.Util.addMethods(Kinetic.FastLayer, { 4 | ____init: function(config) { 5 | this.nodeType = 'Layer'; 6 | this.canvas = new Kinetic.SceneCanvas(); 7 | // call super constructor 8 | Kinetic.BaseLayer.call(this, config); 9 | }, 10 | _validateAdd: function(child) { 11 | var type = child.getType(); 12 | if (type !== 'Shape') { 13 | Kinetic.Util.error('You may only add shapes to a fast layer.'); 14 | } 15 | }, 16 | _setCanvasSize: function(width, height) { 17 | this.canvas.setSize(width, height); 18 | }, 19 | hitGraphEnabled: function() { 20 | return false; 21 | }, 22 | getIntersection: function() { 23 | return null; 24 | }, 25 | drawScene: function(can) { 26 | var layer = this.getLayer(), 27 | canvas = can || (layer && layer.getCanvas()); 28 | 29 | if(this.getClearBeforeDraw()) { 30 | canvas.getContext().clear(); 31 | } 32 | 33 | Kinetic.Container.prototype.drawScene.call(this, canvas); 34 | 35 | return this; 36 | }, 37 | // the apply transform method is handled by the Layer and FastLayer class 38 | // because it is up to the layer to decide if an absolute or relative transform 39 | // should be used 40 | _applyTransform: function(shape, context, top) { 41 | if (!top || top._id !== this._id) { 42 | var m = shape.getTransform().getMatrix(); 43 | context.transform(m[0], m[1], m[2], m[3], m[4], m[5]); 44 | } 45 | }, 46 | draw: function() { 47 | this.drawScene(); 48 | return this; 49 | }, 50 | /** 51 | * clear scene and hit canvas contexts tied to the layer 52 | * @method 53 | * @memberof Kinetic.FastLayer.prototype 54 | * @param {Object} [bounds] 55 | * @param {Number} [bounds.x] 56 | * @param {Number} [bounds.y] 57 | * @param {Number} [bounds.width] 58 | * @param {Number} [bounds.height] 59 | * @example 60 | * layer.clear(); 61 | * layer.clear(0, 0, 100, 100); 62 | */ 63 | clear: function(bounds) { 64 | this.getContext().clear(bounds); 65 | return this; 66 | }, 67 | // extend Node.prototype.setVisible 68 | setVisible: function(visible) { 69 | Kinetic.Node.prototype.setVisible.call(this, visible); 70 | if(visible) { 71 | this.getCanvas()._canvas.style.display = 'block'; 72 | } 73 | else { 74 | this.getCanvas()._canvas.style.display = 'none'; 75 | } 76 | return this; 77 | } 78 | }); 79 | Kinetic.Util.extend(Kinetic.FastLayer, Kinetic.BaseLayer); 80 | 81 | Kinetic.Collection.mapMethods(Kinetic.FastLayer); 82 | })(); 83 | -------------------------------------------------------------------------------- /src/shapes/Wedge.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | /** 3 | * Wedge constructor 4 | * @constructor 5 | * @augments Kinetic.Shape 6 | * @param {Object} config 7 | * @param {Number} config.angle in degrees 8 | * @param {Number} config.radius 9 | * @param {Boolean} [config.clockwise] 10 | * @@shapeParams 11 | * @@nodeParams 12 | * @example 13 | * // draw a wedge that's pointing downwards 14 | * var wedge = new Kinetic.Wedge({ 15 | * radius: 40, 16 | * fill: 'red', 17 | * stroke: 'black' 18 | * strokeWidth: 5, 19 | * angleDeg: 60, 20 | * rotationDeg: -120 21 | * }); 22 | */ 23 | Kinetic.Wedge = function(config) { 24 | this.___init(config); 25 | }; 26 | 27 | Kinetic.Wedge.prototype = { 28 | ___init: function(config) { 29 | // call super constructor 30 | Kinetic.Shape.call(this, config); 31 | this.className = 'Wedge'; 32 | this.sceneFunc(this._sceneFunc); 33 | }, 34 | _sceneFunc: function(context) { 35 | context.beginPath(); 36 | context.arc(0, 0, this.getRadius(), 0, Kinetic.getAngle(this.getAngle()), this.getClockwise()); 37 | context.lineTo(0, 0); 38 | context.closePath(); 39 | context.fillStrokeShape(this); 40 | } 41 | }; 42 | Kinetic.Util.extend(Kinetic.Wedge, Kinetic.Shape); 43 | 44 | // add getters setters 45 | Kinetic.Factory.addGetterSetter(Kinetic.Wedge, 'radius', 0); 46 | 47 | /** 48 | * get/set radius 49 | * @name radius 50 | * @method 51 | * @memberof Kinetic.Wedge.prototype 52 | * @param {Number} radius 53 | * @returns {Number} 54 | * @example 55 | * // get radius 56 | * var radius = wedge.radius(); 57 | * 58 | * // set radius 59 | * wedge.radius(10); 60 | */ 61 | 62 | Kinetic.Factory.addGetterSetter(Kinetic.Wedge, 'angle', 0); 63 | 64 | /** 65 | * get/set angle in degrees 66 | * @name angle 67 | * @method 68 | * @memberof Kinetic.Wedge.prototype 69 | * @param {Number} angle 70 | * @returns {Number} 71 | * @example 72 | * // get angle 73 | * var angle = wedge.angle(); 74 | * 75 | * // set angle 76 | * wedge.angle(20); 77 | */ 78 | 79 | Kinetic.Factory.addGetterSetter(Kinetic.Wedge, 'clockwise', false); 80 | 81 | /** 82 | * get/set clockwise flag 83 | * @name clockwise 84 | * @method 85 | * @memberof Kinetic.Wedge.prototype 86 | * @param {Number} clockwise 87 | * @returns {Number} 88 | * @example 89 | * // get clockwise flag 90 | * var clockwise = wedge.clockwise(); 91 | * 92 | * // draw wedge counter-clockwise 93 | * wedge.clockwise(false); 94 | * 95 | * // draw wedge clockwise 96 | * wedge.clockwise(true); 97 | */ 98 | 99 | Kinetic.Factory.backCompat(Kinetic.Wedge, { 100 | angleDeg: 'angle', 101 | getAngleDeg: 'getAngle', 102 | setAngleDeg: 'setAngle' 103 | }); 104 | 105 | Kinetic.Collection.mapMethods(Kinetic.Wedge); 106 | })(); 107 | -------------------------------------------------------------------------------- /src/plugins/Star.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | /** 3 | * Star constructor 4 | * @constructor 5 | * @memberof Kinetic 6 | * @augments Kinetic.Shape 7 | * @param {Object} config 8 | * @param {Integer} config.numPoints 9 | * @param {Number} config.innerRadius 10 | * @param {Number} config.outerRadius 11 | * @@shapeParams 12 | * @@nodeParams 13 | * @example 14 | * var star = new Kinetic.Star({ 15 | * x: 100, 16 | * y: 200, 17 | * numPoints: 5, 18 | * innerRadius: 70, 19 | * outerRadius: 70, 20 | * fill: 'red', 21 | * stroke: 'black', 22 | * strokeWidth: 4 23 | * }); 24 | */ 25 | Kinetic.Star = function(config) { 26 | this.___init(config); 27 | }; 28 | 29 | Kinetic.Star.prototype = { 30 | ___init: function(config) { 31 | // call super constructor 32 | Kinetic.Shape.call(this, config); 33 | this.className = 'Star'; 34 | this.sceneFunc(this._sceneFunc); 35 | }, 36 | _sceneFunc: function(context) { 37 | var innerRadius = this.innerRadius(), 38 | outerRadius = this.outerRadius(), 39 | numPoints = this.numPoints(); 40 | 41 | context.beginPath(); 42 | context.moveTo(0, 0 - outerRadius); 43 | 44 | for(var n = 1; n < numPoints * 2; n++) { 45 | var radius = n % 2 === 0 ? outerRadius : innerRadius; 46 | var x = radius * Math.sin(n * Math.PI / numPoints); 47 | var y = -1 * radius * Math.cos(n * Math.PI / numPoints); 48 | context.lineTo(x, y); 49 | } 50 | context.closePath(); 51 | 52 | context.fillStrokeShape(this); 53 | } 54 | }; 55 | Kinetic.Util.extend(Kinetic.Star, Kinetic.Shape); 56 | 57 | // add getters setters 58 | Kinetic.Factory.addGetterSetter(Kinetic.Star, 'numPoints', 5); 59 | 60 | /** 61 | * set number of points 62 | * @name setNumPoints 63 | * @method 64 | * @memberof Kinetic.Star.prototype 65 | * @param {Integer} points 66 | */ 67 | 68 | /** 69 | * get number of points 70 | * @name getNumPoints 71 | * @method 72 | * @memberof Kinetic.Star.prototype 73 | */ 74 | 75 | Kinetic.Factory.addGetterSetter(Kinetic.Star, 'innerRadius', 0); 76 | 77 | /** 78 | * set inner radius 79 | * @name setInnerRadius 80 | * @method 81 | * @memberof Kinetic.Star.prototype 82 | * @param {Number} radius 83 | */ 84 | 85 | /** 86 | * get inner radius 87 | * @name getInnerRadius 88 | * @method 89 | * @memberof Kinetic.Star.prototype 90 | */ 91 | 92 | Kinetic.Factory.addGetterSetter(Kinetic.Star, 'outerRadius', 0); 93 | 94 | /** 95 | * set outer radius 96 | * @name setOuterRadius 97 | * @method 98 | * @memberof Kinetic.Star.prototype 99 | * @param {Number} radius 100 | */ 101 | 102 | /** 103 | * get outer radius 104 | * @name getOuterRadius 105 | * @method 106 | * @memberof Kinetic.Star.prototype 107 | */ 108 | 109 | Kinetic.Collection.mapMethods(Kinetic.Star); 110 | })(); 111 | -------------------------------------------------------------------------------- /test/performance/common/random-squares.js: -------------------------------------------------------------------------------- 1 | var stage; 2 | var circlesLayer; 3 | var circles; 4 | var stats; 5 | var width = 500; 6 | var height = 300; 7 | 8 | var VERSION = Kinetic.version === 'dev' ? 'new' : 'old'; 9 | 10 | window.requestAnimFrame = (function(){ 11 | return window.requestAnimationFrame || 12 | window.webkitRequestAnimationFrame || 13 | window.mozRequestAnimationFrame || 14 | window.oRequestAnimationFrame || 15 | window.msRequestAnimationFrame || 16 | function(callback){ 17 | window.setTimeout(callback, 1000 / 30); 18 | }; 19 | })(); 20 | 21 | function fpsCounter() { 22 | //fps stat--------------------------- 23 | stats = new Stats(); 24 | stats.setMode(0); 25 | stats.domElement.style.position = 'fixed'; 26 | stats.domElement.style.left = '0px'; 27 | stats.domElement.style.top = '0px'; 28 | $('html').append( stats.domElement ); 29 | } 30 | 31 | $(function() { 32 | fpsCounter(); 33 | 34 | make_stage(); 35 | 36 | 37 | var colors = ["red", "orange", "yellow", "green", "blue", "cyan", "purple"]; 38 | var colorIndex = 0; 39 | 40 | circles = []; 41 | for(var n = 0; n < 1500; n++) {( function() { 42 | var color = colors[colorIndex++]; 43 | if(colorIndex >= colors.length) { 44 | colorIndex = 0; 45 | } 46 | 47 | var shape = make_shape(color); 48 | circlesLayer.add(shape); 49 | circles.push(shape); 50 | }()); 51 | } 52 | 53 | stage.add(circlesLayer); 54 | animate((new Date()).getTime()); 55 | 56 | }); 57 | 58 | function animate(lastTime){ 59 | stats.begin(); 60 | // update 61 | var date = new Date(); 62 | var time = date.getTime(); 63 | var timeDiff = time - lastTime; 64 | var period = timeDiff/1000; //times per second, our period 65 | 66 | for (var i = 0; i < circles.length; i++) { 67 | var x = Math.round(Math.random() * width); 68 | var y = Math.round(Math.random() * height); 69 | 70 | circles[i].setPosition({x: x, y: y}); 71 | 72 | } 73 | lastTime = time; 74 | 75 | circlesLayer.draw(); 76 | stats.end(); 77 | requestAnimFrame(function(){ 78 | animate(lastTime); 79 | }); 80 | } 81 | 82 | function make_shape(color) { 83 | return new Kinetic.Rect({ 84 | fill: color, 85 | width: 10, 86 | height: 10 87 | }); 88 | } 89 | 90 | function make_stage() { 91 | stage = new Kinetic.Stage({ 92 | container: "container", 93 | width: width, 94 | height: height 95 | }); 96 | 97 | if (VERSION === 'new') { 98 | console.log('create fast layer') 99 | circlesLayer = new Kinetic.FastLayer(); 100 | } 101 | else { 102 | console.log('create normal layer') 103 | circlesLayer = new Kinetic.Layer(); 104 | } 105 | } -------------------------------------------------------------------------------- /test/unit/shapes/Blob-test.js: -------------------------------------------------------------------------------- 1 | suite('Blob', function(){ 2 | // ====================================================== 3 | test('add blob', function() { 4 | var stage = addStage(); 5 | var layer = new Kinetic.Layer(); 6 | 7 | var blob = new Kinetic.Line({ 8 | points: [73,140,340,23,500,109,300,170], 9 | stroke: 'blue', 10 | strokeWidth: 10, 11 | draggable: true, 12 | fill: '#aaf', 13 | tension: 0.8, 14 | closed: true 15 | }); 16 | 17 | layer.add(blob); 18 | stage.add(layer); 19 | 20 | assert.equal(blob.getTension(), 0.8); 21 | 22 | assert.equal(blob.getClassName(), 'Line'); 23 | 24 | //console.log(blob1.getPoints()) 25 | 26 | // test setter 27 | blob.setTension(1.5); 28 | assert.equal(blob.getTension(), 1.5); 29 | 30 | var trace = layer.getContext().getTrace(); 31 | assert.equal(trace, 'clearRect(0,0,578,200);save();transform(1,0,0,1,0,0);beginPath();moveTo(73,140);bezierCurveTo(90.922,74.135,129.542,38.279,340,23);bezierCurveTo(471.142,13.479,514.876,54.33,500,109);bezierCurveTo(482.876,171.93,463.05,158.163,300,170);bezierCurveTo(121.45,182.963,58.922,191.735,73,140);closePath();fillStyle=#aaf;fill();lineWidth=10;strokeStyle=blue;stroke();restore();'); 32 | }); 33 | 34 | // ====================================================== 35 | test('define tension first', function() { 36 | var stage = addStage(); 37 | var layer = new Kinetic.Layer(); 38 | 39 | 40 | var blob = new Kinetic.Line({ 41 | tension: 0.8, 42 | points: [73,140,340,23,500,109,300,170], 43 | stroke: 'blue', 44 | strokeWidth: 10, 45 | draggable: true, 46 | fill: '#aaf', 47 | closed: true 48 | 49 | }); 50 | 51 | layer.add(blob); 52 | stage.add(layer); 53 | 54 | assert.equal(stage.find('Line')[0].getPoints().length, 8); 55 | 56 | }); 57 | 58 | // ====================================================== 59 | test('check for kinetic event handlers', function() { 60 | var stage = addStage(); 61 | var layer = new Kinetic.Layer(); 62 | 63 | var blob = new Kinetic.Line({ 64 | points: [73,140,340,23,500,109,300,170], 65 | stroke: 'blue', 66 | strokeWidth: 10, 67 | draggable: true, 68 | fill: '#aaf', 69 | tension: 0.8, 70 | closed: true 71 | }); 72 | 73 | layer.add(blob); 74 | 75 | stage.add(layer); 76 | 77 | assert.equal(blob.eventListeners.pointsChange[0].name, 'kinetic'); 78 | assert.equal(blob.eventListeners.tensionChange[0].name, 'kinetic'); 79 | 80 | // removing handlers should not remove kinetic specific handlers 81 | blob.off('pointsChange'); 82 | blob.off('tensionChange'); 83 | 84 | assert.equal(blob.eventListeners.pointsChange[0].name, 'kinetic'); 85 | assert.equal(blob.eventListeners.tensionChange[0].name, 'kinetic'); 86 | 87 | // you can force remove an event by adding the name 88 | blob.off('pointsChange.kinetic'); 89 | blob.off('tensionChange.kinetic'); 90 | 91 | assert.equal(blob.eventListeners.pointsChange, undefined); 92 | assert.equal(blob.eventListeners.tensionChange, undefined); 93 | }); 94 | }); -------------------------------------------------------------------------------- /presentation-schedule.md: -------------------------------------------------------------------------------- 1 | # Presentation Schedule 2 | To add/modify your presentation, just click the "Edit" button, make your changes, and submit for review. 3 | 4 | ## Aug 30 2013 5 | * _Event:_ [devLink Technical Conference](http://www.devlink.net/) 6 | * _Session:_ [Custom Graphics for Your Web Application: The HTML5 Canvas and Kinetic.js](http://www.devlink.net/agenda) 7 | * _Speaker:_ [Jason Follas](http://www.devlink.net/speakers) [@jfollas](https://twitter.com/jfollas) 8 | 9 | ## Aug 14 2013 10 | * _Event:_ [That Conference](http://www.thatconference.com/) 11 | * _Session:_ [Custom Graphics for Your Web Application: The HTML5 Canvas and Kinetic.js](http://www.thatconference.com/sessions/session_953) 12 | * _Speaker:_ [Jason Follas](http://www.thatconference.com/Speakers/speaker_415) [@jfollas](https://twitter.com/jfollas) 13 | 14 | ## Jul 12 2013 15 | * _Event:_ [Codestock](http://www.codestock.org/) 16 | * _Session:_ [Custom Graphics for Your Web Application: The HTML5 Canvas and Kinetic.js](http://www.codestock.org/sessions/custom-graphics-for-your-web-application-the-html5-canvas-and-kinetic-js/) 17 | * _Speaker:_ [Jason Follas](http://www.codestock.org/speakers/jason-follas/) [@jfollas](https://twitter.com/jfollas) 18 | 19 | ## May 17 2013 20 | * _Event:_ [Stir Trek](http://www.stirtrek.com/) 21 | * _Session:_ [Custom Graphics for Your Web Application: The HTML5 Canvas and Kinetic.js](http://www.stirtrek.com/Sessions) 22 | * _Speaker:_ [Jason Follas](http://www.stirtrek.com/Speakers) [@jfollas](https://twitter.com/jfollas) 23 | 24 | ## Apr 25 2013 25 | * _Event:_ [Front End Developers United](http://www.meetup.com/Front-End-Developers-United/events/114192072/) 26 | * _Session:_ [Data Viz and KineticJS](http://www.meetup.com/Front-End-Developers-United/events/114192072/) 27 | * _Speaker:_ [Eric Rowell](http://www.twitter.com/ericdrowell) 28 | * _Video:_ [http://www.youtube.com/watch?feature=player_detailpage&v=ZS6QqNJ0VRA#t=1s](http://www.youtube.com/watch?feature=player_detailpage&v=ZS6QqNJ0VRA#t=1s) 29 | 30 | ## Apr 10 2013 31 | * _Event:_ [DevIntersection](http://devintersection.com/default.aspx) 32 | * _Session:_ Custom Graphics for Your Web Application: The HTML5 Canvas and Kinetic.js 33 | * _Speaker:_ Jason Follas [@jfollas](https://twitter.com/jfollas) 34 | 35 | ## Mar 13 2013 36 | * _Event:_ [Toledo Web Professionals](http://www.meetup.com/Toledo-Web-Design/events/92857942/) 37 | * _Session:_ [Canvas Interaction with Jason Follas at Hanson, Inc.](http://www.meetup.com/Toledo-Web-Design/events/92857942/) 38 | * _Speaker:_ [Jason Follas](http://devconnections.com/shows/fall2012/speakers.aspx?s=189&sp=2762) [@jfollas](https://twitter.com/jfollas) 39 | 40 | ## Nov 1 2012 41 | * _Event:_ [DevConnections](http://devconnections.com/shows/fall2012/sessions.aspx?s=189) 42 | * _Session:_ [Custom Graphics for Your Web Application: The HTML5 Canvas and Kinetic.js](http://devconnections.com/shows/fall2012/sessions.aspx?s=189) 43 | * _Speaker:_ [Jason Follas](http://devconnections.com/shows/fall2012/speakers.aspx?s=189&sp=2762) [@jfollas](https://twitter.com/jfollas) 44 | * _Place:_ Las Vegas, NV at the Bellagio 45 | * _Presentation:_ [https://github.com/jfollas/CanvasKineticDemo](https://github.com/jfollas/CanvasKineticDemo) 46 | 47 | ## Sep 19 2012 48 | * _Event:_ [Gang](http://migang.org/?p=97) 49 | * _Session:_ [The HTML5 Canvas and Kinetic.js](http://migang.org/?p=97) 50 | * _Speaker:_ [Jason Follas](http://migang.org/?p=97) [@jfollas](https://twitter.com/jfollas) 51 | * _Place:_ Great Lakes 52 | -------------------------------------------------------------------------------- /src/filters/Pixelate.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | /** 4 | * Pixelate Filter. Averages groups of pixels and redraws 5 | * them as larger pixels 6 | * @function 7 | * @name Pixelate 8 | * @memberof Kinetic.Filters 9 | * @param {Object} imageData 10 | * @author ippo615 11 | * @example 12 | * node.cache(); 13 | * node.filters([Kinetic.Filters.Pixelate]); 14 | * node.pixelSize(10); 15 | */ 16 | 17 | Kinetic.Filters.Pixelate = function (imageData) { 18 | 19 | var pixelSize = Math.ceil(this.pixelSize()), 20 | width = imageData.width, 21 | height = imageData.height, 22 | x, y, i, 23 | //pixelsPerBin = pixelSize * pixelSize, 24 | red, green, blue, alpha, 25 | nBinsX = Math.ceil(width / pixelSize), 26 | nBinsY = Math.ceil(height / pixelSize), 27 | xBinStart, xBinEnd, yBinStart, yBinEnd, 28 | xBin, yBin, pixelsInBin; 29 | imageData = imageData.data; 30 | 31 | for (xBin = 0; xBin < nBinsX; xBin += 1) { 32 | for (yBin = 0; yBin < nBinsY; yBin += 1) { 33 | 34 | // Initialize the color accumlators to 0 35 | red = 0; 36 | green = 0; 37 | blue = 0; 38 | alpha = 0; 39 | 40 | // Determine which pixels are included in this bin 41 | xBinStart = xBin * pixelSize; 42 | xBinEnd = xBinStart + pixelSize; 43 | yBinStart = yBin * pixelSize; 44 | yBinEnd = yBinStart + pixelSize; 45 | 46 | // Add all of the pixels to this bin! 47 | pixelsInBin = 0; 48 | for (x = xBinStart; x < xBinEnd; x += 1) { 49 | if( x >= width ){ continue; } 50 | for (y = yBinStart; y < yBinEnd; y += 1) { 51 | if( y >= height ){ continue; } 52 | i = (width * y + x) * 4; 53 | red += imageData[i + 0]; 54 | green += imageData[i + 1]; 55 | blue += imageData[i + 2]; 56 | alpha += imageData[i + 3]; 57 | pixelsInBin += 1; 58 | } 59 | } 60 | 61 | // Make sure the channels are between 0-255 62 | red = red / pixelsInBin; 63 | green = green / pixelsInBin; 64 | blue = blue / pixelsInBin; 65 | 66 | // Draw this bin 67 | for (x = xBinStart; x < xBinEnd; x += 1) { 68 | if( x >= width ){ continue; } 69 | for (y = yBinStart; y < yBinEnd; y += 1) { 70 | if( y >= height ){ continue; } 71 | i = (width * y + x) * 4; 72 | imageData[i + 0] = red; 73 | imageData[i + 1] = green; 74 | imageData[i + 2] = blue; 75 | imageData[i + 3] = alpha; 76 | } 77 | } 78 | } 79 | } 80 | 81 | }; 82 | 83 | Kinetic.Factory.addGetterSetter(Kinetic.Node, 'pixelSize', 8, null, Kinetic.Factory.afterSetFilter); 84 | 85 | /** 86 | * get/set pixel size. Use with {@link Kinetic.Filters.Pixelate} filter. 87 | * @name pixelSize 88 | * @method 89 | * @memberof Kinetic.Node.prototype 90 | * @param {Integer} pixelSize 91 | * @returns {Integer} 92 | */ 93 | })(); -------------------------------------------------------------------------------- /src/filters/HSV.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | /** 4 | * HSV Filter. Adjusts the hue, saturation and value 5 | * @function 6 | * @name HSV 7 | * @memberof Kinetic.Filters 8 | * @param {Object} imageData 9 | * @author ippo615 10 | * @example 11 | * image.filters([Kinetic.Filters.HSV]); 12 | * image.value(200); 13 | */ 14 | 15 | Kinetic.Filters.HSV = function (imageData) { 16 | var data = imageData.data, 17 | nPixels = data.length, 18 | v = Math.pow(2,this.value()), 19 | s = Math.pow(2,this.saturation()), 20 | h = Math.abs((this.hue()) + 360) % 360, 21 | i; 22 | 23 | // Basis for the technique used: 24 | // http://beesbuzz.biz/code/hsv_color_transforms.php 25 | // V is the value multiplier (1 for none, 2 for double, 0.5 for half) 26 | // S is the saturation multiplier (1 for none, 2 for double, 0.5 for half) 27 | // H is the hue shift in degrees (0 to 360) 28 | // vsu = V*S*cos(H*PI/180); 29 | // vsw = V*S*sin(H*PI/180); 30 | //[ .299V+.701vsu+.168vsw .587V-.587vsu+.330vsw .114V-.114vsu-.497vsw ] [R] 31 | //[ .299V-.299vsu-.328vsw .587V+.413vsu+.035vsw .114V-.114vsu+.292vsw ]*[G] 32 | //[ .299V-.300vsu+1.25vsw .587V-.588vsu-1.05vsw .114V+.886vsu-.203vsw ] [B] 33 | 34 | // Precompute the values in the matrix: 35 | var vsu = v*s*Math.cos(h*Math.PI/180), 36 | vsw = v*s*Math.sin(h*Math.PI/180); 37 | // (result spot)(source spot) 38 | var rr = 0.299*v+0.701*vsu+0.167*vsw, 39 | rg = 0.587*v-0.587*vsu+0.330*vsw, 40 | rb = 0.114*v-0.114*vsu-0.497*vsw; 41 | var gr = 0.299*v-0.299*vsu-0.328*vsw, 42 | gg = 0.587*v+0.413*vsu+0.035*vsw, 43 | gb = 0.114*v-0.114*vsu+0.293*vsw; 44 | var br = 0.299*v-0.300*vsu+1.250*vsw, 45 | bg = 0.587*v-0.586*vsu-1.050*vsw, 46 | bb = 0.114*v+0.886*vsu-0.200*vsw; 47 | 48 | var r,g,b,a; 49 | 50 | for (i = 0; i < nPixels; i += 4) { 51 | r = data[i+0]; 52 | g = data[i+1]; 53 | b = data[i+2]; 54 | a = data[i+3]; 55 | 56 | data[i+0] = rr*r + rg*g + rb*b; 57 | data[i+1] = gr*r + gg*g + gb*b; 58 | data[i+2] = br*r + bg*g + bb*b; 59 | data[i+3] = a; // alpha 60 | } 61 | 62 | }; 63 | 64 | Kinetic.Factory.addGetterSetter(Kinetic.Node, 'hue', 0, null, Kinetic.Factory.afterSetFilter); 65 | /** 66 | * get/set hsv hue in degrees. Use with {@link Kinetic.Filters.HSV} or {@link Kinetic.Filters.HSL} filter. 67 | * @name hue 68 | * @method 69 | * @memberof Kinetic.Node.prototype 70 | * @param {Number} hue value between 0 and 359 71 | * @returns {Number} 72 | */ 73 | 74 | Kinetic.Factory.addGetterSetter(Kinetic.Node, 'saturation', 0, null, Kinetic.Factory.afterSetFilter); 75 | /** 76 | * get/set hsv saturation. Use with {@link Kinetic.Filters.HSV} or {@link Kinetic.Filters.HSL} filter. 77 | * @name saturation 78 | * @method 79 | * @memberof Kinetic.Node.prototype 80 | * @param {Number} saturation 0 is no change, -1.0 halves the saturation, 1.0 doubles, etc.. 81 | * @returns {Number} 82 | */ 83 | 84 | Kinetic.Factory.addGetterSetter(Kinetic.Node, 'value', 0, null, Kinetic.Factory.afterSetFilter); 85 | /** 86 | * get/set hsv value. Use with {@link Kinetic.Filters.HSV} filter. 87 | * @name value 88 | * @method 89 | * @memberof Kinetic.Node.prototype 90 | * @param {Number} value 0 is no change, -1.0 halves the value, 1.0 doubles, etc.. 91 | * @returns {Number} 92 | */ 93 | 94 | })(); 95 | -------------------------------------------------------------------------------- /test/runner.js: -------------------------------------------------------------------------------- 1 | mocha.ui('tdd'); 2 | mocha.setup("bdd"); 3 | var assert = chai.assert, 4 | kineticContainer = document.getElementById('kinetic-container'), 5 | origAssertEqual = assert.equal, 6 | origAssert = assert, 7 | origNotEqual = assert.notEqual, 8 | assertionCount = 0, 9 | assertions = document.createElement('em'); 10 | 11 | window.requestAnimFrame = (function(callback){ 12 | return window.requestAnimationFrame || 13 | window.webkitRequestAnimationFrame || 14 | window.mozRequestAnimationFrame || 15 | window.oRequestAnimationFrame || 16 | window.msRequestAnimationFrame || 17 | function(callback){ 18 | window.setTimeout(callback, 1000 / 30); 19 | }; 20 | })(); 21 | 22 | function init() { 23 | // assert extenders so that we can count assertions 24 | assert = function() { 25 | origAssert.apply(this, arguments); 26 | assertions.innerHTML = ++assertionCount; 27 | }; 28 | assert.equal = function() { 29 | origAssertEqual.apply(this, arguments); 30 | assertions.innerHTML = ++assertionCount; 31 | }; 32 | assert.notEqual = function() { 33 | origNotEqual.apply(this, arguments); 34 | assertions.innerHTML = ++assertionCount; 35 | }; 36 | 37 | window.onload = function() { 38 | var mochaStats = document.getElementById('mocha-stats'); 39 | 40 | if (mochaStats) { 41 | var li = document.createElement('li'); 42 | var anchor = document.createElement('a'); 43 | 44 | anchor.href = '#'; 45 | anchor.innerHTML = 'assertions:'; 46 | assertions.innerHTML = 0; 47 | 48 | li.appendChild(anchor); 49 | li.appendChild(assertions); 50 | mochaStats.appendChild(li); 51 | } 52 | } 53 | 54 | //addStats(); 55 | } 56 | 57 | 58 | 59 | 60 | Kinetic.enableTrace = true; 61 | Kinetic.showWarnings = false; 62 | 63 | function addStats() { 64 | stats = new Stats(); 65 | stats.setMode(0); 66 | stats.domElement.style.position = 'fixed'; 67 | stats.domElement.style.left = '0px'; 68 | stats.domElement.style.top = '0px'; 69 | document.getElementsByTagName('body')[0].appendChild( stats.domElement ); 70 | 71 | 72 | function animate(lastTime){ 73 | stats.begin(); 74 | 75 | requestAnimFrame(function(){ 76 | stats.end(); 77 | animate(lastTime); 78 | }); 79 | } 80 | 81 | animate(); 82 | } 83 | 84 | 85 | 86 | function addStage() { 87 | var container = document.createElement('div'), 88 | stage = new Kinetic.Stage({ 89 | container: container, 90 | width: 578, 91 | height: 200 92 | }); 93 | 94 | kineticContainer.appendChild(container); 95 | 96 | return stage; 97 | } 98 | 99 | function addContainer() { 100 | var container = document.createElement('div'); 101 | 102 | kineticContainer.appendChild(container); 103 | 104 | return container; 105 | } 106 | 107 | function showCanvas(canvas) { 108 | canvas.style.position = 'relative'; 109 | 110 | kineticContainer.appendChild(canvas); 111 | } 112 | function showHit(layer) { 113 | var canvas = layer.hitCanvas._canvas; 114 | canvas.style.position = 'relative'; 115 | 116 | kineticContainer.appendChild(canvas); 117 | } 118 | 119 | beforeEach(function(){ 120 | var title = document.createElement('h2'), 121 | test = this.currentTest; 122 | 123 | title.innerHTML = test.parent.title + ' - ' + test.title; 124 | title.className = 'kinetic-title'; 125 | kineticContainer.appendChild(title); 126 | 127 | // resets 128 | Kinetic.inDblClickWindow = false; 129 | Kinetic.DD.isDragging = false; 130 | Kinetic.DD.node = undefined; 131 | }); 132 | 133 | init(); -------------------------------------------------------------------------------- /src/filters/HSL.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | Kinetic.Factory.addGetterSetter(Kinetic.Node, 'hue', 0, null, Kinetic.Factory.afterSetFilter); 4 | /** 5 | * get/set hsv hue in degrees. Use with {@link Kinetic.Filters.HSV} or {@link Kinetic.Filters.HSL} filter. 6 | * @name hue 7 | * @method 8 | * @memberof Kinetic.Node.prototype 9 | * @param {Number} hue value between 0 and 359 10 | * @returns {Number} 11 | */ 12 | 13 | Kinetic.Factory.addGetterSetter(Kinetic.Node, 'saturation', 0, null, Kinetic.Factory.afterSetFilter); 14 | /** 15 | * get/set hsv saturation. Use with {@link Kinetic.Filters.HSV} or {@link Kinetic.Filters.HSL} filter. 16 | * @name saturation 17 | * @method 18 | * @memberof Kinetic.Node.prototype 19 | * @param {Number} saturation 0 is no change, -1.0 halves the saturation, 1.0 doubles, etc.. 20 | * @returns {Number} 21 | */ 22 | 23 | Kinetic.Factory.addGetterSetter(Kinetic.Node, 'luminance', 0, null, Kinetic.Factory.afterSetFilter); 24 | /** 25 | * get/set hsl luminance. Use with {@link Kinetic.Filters.HSL} filter. 26 | * @name value 27 | * @method 28 | * @memberof Kinetic.Node.prototype 29 | * @param {Number} value 0 is no change, -1.0 halves the value, 1.0 doubles, etc.. 30 | * @returns {Number} 31 | */ 32 | 33 | /** 34 | * HSL Filter. Adjusts the hue, saturation and luminance (or lightness) 35 | * @function 36 | * @memberof Kinetic.Filters 37 | * @param {Object} imageData 38 | * @author ippo615 39 | * @example 40 | * image.filters([Kinetic.Filters.HSL]); 41 | * image.luminance(200); 42 | */ 43 | 44 | Kinetic.Filters.HSL = function (imageData) { 45 | var data = imageData.data, 46 | nPixels = data.length, 47 | v = 1, 48 | s = Math.pow(2,this.saturation()), 49 | h = Math.abs((this.hue()) + 360) % 360, 50 | l = this.luminance()*127, 51 | i; 52 | 53 | // Basis for the technique used: 54 | // http://beesbuzz.biz/code/hsv_color_transforms.php 55 | // V is the value multiplier (1 for none, 2 for double, 0.5 for half) 56 | // S is the saturation multiplier (1 for none, 2 for double, 0.5 for half) 57 | // H is the hue shift in degrees (0 to 360) 58 | // vsu = V*S*cos(H*PI/180); 59 | // vsw = V*S*sin(H*PI/180); 60 | //[ .299V+.701vsu+.168vsw .587V-.587vsu+.330vsw .114V-.114vsu-.497vsw ] [R] 61 | //[ .299V-.299vsu-.328vsw .587V+.413vsu+.035vsw .114V-.114vsu+.292vsw ]*[G] 62 | //[ .299V-.300vsu+1.25vsw .587V-.588vsu-1.05vsw .114V+.886vsu-.203vsw ] [B] 63 | 64 | // Precompute the values in the matrix: 65 | var vsu = v*s*Math.cos(h*Math.PI/180), 66 | vsw = v*s*Math.sin(h*Math.PI/180); 67 | // (result spot)(source spot) 68 | var rr = 0.299*v+0.701*vsu+0.167*vsw, 69 | rg = 0.587*v-0.587*vsu+0.330*vsw, 70 | rb = 0.114*v-0.114*vsu-0.497*vsw; 71 | var gr = 0.299*v-0.299*vsu-0.328*vsw, 72 | gg = 0.587*v+0.413*vsu+0.035*vsw, 73 | gb = 0.114*v-0.114*vsu+0.293*vsw; 74 | var br = 0.299*v-0.300*vsu+1.250*vsw, 75 | bg = 0.587*v-0.586*vsu-1.050*vsw, 76 | bb = 0.114*v+0.886*vsu-0.200*vsw; 77 | 78 | var r,g,b,a; 79 | 80 | for (i = 0; i < nPixels; i += 4) { 81 | r = data[i+0]; 82 | g = data[i+1]; 83 | b = data[i+2]; 84 | a = data[i+3]; 85 | 86 | data[i+0] = rr*r + rg*g + rb*b + l; 87 | data[i+1] = gr*r + gg*g + gb*b + l; 88 | data[i+2] = br*r + bg*g + bb*b + l; 89 | data[i+3] = a; // alpha 90 | } 91 | }; 92 | })(); 93 | -------------------------------------------------------------------------------- /src/shapes/Arc.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | /** 3 | * Arc constructor 4 | * @constructor 5 | * @augments Kinetic.Shape 6 | * @param {Object} config 7 | * @param {Number} config.angle in degrees 8 | * @param {Number} config.innerRadius 9 | * @param {Number} config.outerRadius 10 | * @param {Boolean} [config.clockwise] 11 | * @@shapeParams 12 | * @@nodeParams 13 | * @example 14 | * // draw a Arc that's pointing downwards 15 | * var arc = new Kinetic.Arc({ 16 | * innerRadius: 40, 17 | * outerRadius: 80, 18 | * fill: 'red', 19 | * stroke: 'black' 20 | * strokeWidth: 5, 21 | * angle: 60, 22 | * rotationDeg: -120 23 | * }); 24 | */ 25 | Kinetic.Arc = function(config) { 26 | this.___init(config); 27 | }; 28 | 29 | Kinetic.Arc.prototype = { 30 | ___init: function(config) { 31 | // call super constructor 32 | Kinetic.Shape.call(this, config); 33 | this.className = 'Arc'; 34 | this.sceneFunc(this._sceneFunc); 35 | }, 36 | _sceneFunc: function(context) { 37 | var angle = Kinetic.getAngle(this.angle()), 38 | clockwise = this.clockwise(); 39 | 40 | context.beginPath(); 41 | context.arc(0, 0, this.getOuterRadius(), 0, angle, clockwise); 42 | context.arc(0, 0, this.getInnerRadius(), angle, 0, !clockwise); 43 | context.closePath(); 44 | context.fillStrokeShape(this); 45 | } 46 | }; 47 | Kinetic.Util.extend(Kinetic.Arc, Kinetic.Shape); 48 | 49 | // add getters setters 50 | Kinetic.Factory.addGetterSetter(Kinetic.Arc, 'innerRadius', 0); 51 | 52 | /** 53 | * get/set innerRadius 54 | * @name innerRadius 55 | * @method 56 | * @memberof Kinetic.Arc.prototype 57 | * @param {Number} innerRadius 58 | * @returns {Number} 59 | * @example 60 | * // get inner radius 61 | * var innerRadius = arc.innerRadius(); 62 | * 63 | * // set inner radius 64 | * arc.innerRadius(20); 65 | */ 66 | 67 | Kinetic.Factory.addGetterSetter(Kinetic.Arc, 'outerRadius', 0); 68 | 69 | /** 70 | * get/set outerRadius 71 | * @name outerRadius 72 | * @method 73 | * @memberof Kinetic.Arc.prototype 74 | * @param {Number} outerRadius 75 | * @returns {Number} 76 | * @example 77 | * // get outer radius 78 | * var outerRadius = arc.outerRadius(); 79 | * 80 | * // set outer radius 81 | * arc.outerRadius(20); 82 | */ 83 | 84 | Kinetic.Factory.addGetterSetter(Kinetic.Arc, 'angle', 0); 85 | 86 | /** 87 | * get/set angle in degrees 88 | * @name angle 89 | * @method 90 | * @memberof Kinetic.Arc.prototype 91 | * @param {Number} angle 92 | * @returns {Number} 93 | * @example 94 | * // get angle 95 | * var angle = arc.angle(); 96 | * 97 | * // set angle 98 | * arc.angle(20); 99 | */ 100 | 101 | Kinetic.Factory.addGetterSetter(Kinetic.Arc, 'clockwise', false); 102 | 103 | /** 104 | * get/set clockwise flag 105 | * @name clockwise 106 | * @method 107 | * @memberof Kinetic.Arc.prototype 108 | * @param {Boolean} clockwise 109 | * @returns {Boolean} 110 | * @example 111 | * // get clockwise flag 112 | * var clockwise = arc.clockwise(); 113 | * 114 | * // draw arc counter-clockwise 115 | * arc.clockwise(false); 116 | * 117 | * // draw arc clockwise 118 | * arc.clockwise(true); 119 | */ 120 | 121 | Kinetic.Collection.mapMethods(Kinetic.Arc); 122 | })(); 123 | -------------------------------------------------------------------------------- /test/unit/filters/RGB-test.js: -------------------------------------------------------------------------------- 1 | suite('RGB', function() { 2 | // ====================================================== 3 | test('colorize basic', function(done) { 4 | var stage = addStage(); 5 | 6 | var imageObj = new Image(); 7 | imageObj.onload = function() { 8 | 9 | var layer = new Kinetic.Layer(); 10 | darth = new Kinetic.Image({ 11 | x: 10, 12 | y: 10, 13 | image: imageObj, 14 | draggable: true 15 | }); 16 | 17 | layer.add(darth); 18 | stage.add(layer); 19 | 20 | darth.cache(); 21 | darth.filters([Kinetic.Filters.RGB]); 22 | darth.red(255).green(0).blue(128); 23 | layer.draw(); 24 | 25 | // Assert fails even though '[255,0,128] = [255,0,128]' 26 | //assert.equal(darth.getFilterColorizeColor(), [255,0,128]); 27 | 28 | done(); 29 | }; 30 | imageObj.src = 'assets/darth-vader.jpg'; 31 | 32 | }); 33 | 34 | // ====================================================== 35 | test('colorize crop', function(done) { 36 | var stage = addStage(); 37 | 38 | var imageObj = new Image(); 39 | imageObj.onload = function() { 40 | 41 | var layer = new Kinetic.Layer(); 42 | darth = new Kinetic.Image({ 43 | x: 10, 44 | y: 10, 45 | image: imageObj, 46 | crop: {x:128, y:48, width:256, height:128}, 47 | draggable: true 48 | }); 49 | 50 | layer.add(darth); 51 | stage.add(layer); 52 | 53 | darth.cache(); 54 | darth.filters([Kinetic.Filters.RGB]); 55 | darth.red(0).green(255).blue(0); 56 | layer.draw(); 57 | 58 | // assert.equal(darth.getFilterColorizeColor(), [0,255,0]); 59 | 60 | done(); 61 | 62 | }; 63 | imageObj.src = 'assets/darth-vader.jpg'; 64 | }); 65 | 66 | // ====================================================== 67 | test('colorize transparancy', function(done) { 68 | var stage = addStage(); 69 | var layer = new Kinetic.Layer(); 70 | 71 | var colors = [ 72 | [255,0,0], 73 | [255,128,0], 74 | [255,255,0], 75 | [0,255,0], 76 | [0,255,128], 77 | [0,255,255], 78 | [0,0,255], 79 | [128,0,255], 80 | [255,0,255], 81 | [0,0,0], 82 | [128,128,128], 83 | [255,255,255] 84 | ]; 85 | var i,l = colors.length; 86 | var nAdded = 0; 87 | for( i=0; i= l ){ 105 | stage.add(layer); 106 | layer.draw(); 107 | done(); 108 | } 109 | };})(colors[i],-64+i/l*stage.getWidth()); 110 | imageObj.src = 'assets/lion.png'; 111 | } 112 | 113 | }); 114 | 115 | }); 116 | -------------------------------------------------------------------------------- /test/unit/Global-test.js: -------------------------------------------------------------------------------- 1 | suite('Global', function() { 2 | 3 | // ====================================================== 4 | test('test Kinetic version number', function() { 5 | assert.equal(Kinetic.version, 'dev'); 6 | }); 7 | 8 | // ====================================================== 9 | test('getAngle()', function() { 10 | // test that default angleDeg is true 11 | assert.equal(Kinetic.angleDeg, true); 12 | assert.equal(Kinetic.getAngle(180), Math.PI); 13 | 14 | Kinetic.angleDeg = false; 15 | assert.equal(Kinetic.getAngle(1), 1); 16 | 17 | // set angleDeg back to true for future tests 18 | Kinetic.angleDeg = true; 19 | }); 20 | 21 | 22 | // ====================================================== 23 | test('UA tests', function() { 24 | 25 | var ua; 26 | 27 | // Chrome 34.0.1847.137 m 28 | ua = Kinetic._parseUA('Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.137 Safari/537.36'); 29 | 30 | assert.equal(ua.browser, 'chrome'); 31 | assert.equal(ua.mobile, false); 32 | assert.equal(ua.version, '34.0.1847.137'); 33 | assert.equal(ua.ieMobile, false); 34 | 35 | 36 | // Internet Explorer 9 37 | ua = Kinetic._parseUA('Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)'); 38 | assert.equal(ua.browser, 'msie'); 39 | assert.equal(ua.mobile, false); 40 | assert.equal(ua.version, '9.0'); 41 | assert.equal(ua.ieMobile, false); 42 | 43 | // Internet Explorer 10 44 | ua = Kinetic._parseUA('Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)'); 45 | assert.equal(ua.browser, 'msie'); 46 | assert.equal(ua.mobile, false); 47 | assert.equal(ua.version, '10.0'); 48 | assert.equal(ua.ieMobile, false); 49 | 50 | // Internet Explorer 10 Mobile (Windows Phone 8) 51 | ua = Kinetic._parseUA('Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 920)'); 52 | assert.equal(ua.browser, 'msie'); 53 | assert.equal(ua.mobile, true); 54 | assert.equal(ua.version, '10.0'); 55 | assert.equal(ua.ieMobile, true); // <-- forces Kinetic mouse events to be Kinetic touch events instead 56 | 57 | // Internet Explorer 11 Mobile (Windows Phone 8.1) 58 | ua = Kinetic._parseUA('Mozilla/5.0 (Windows Phone 8.1; ARM; Trident/7.0; Touch; rv:11; IEMobile/11.0; NOKIA; Lumia 928) like Gecko'); 59 | assert.equal(ua.browser, 'mozilla'); 60 | assert.equal(ua.mobile, true); 61 | assert.equal(ua.version, '11'); 62 | assert.equal(ua.ieMobile, true); // <-- forces Kinetic mouse events to be Kinetic touch events instead 63 | 64 | // Internet Explorer 11 on 64-bit Windows 8.1 with Update 65 | ua = Kinetic._parseUA('Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko'); 66 | assert.equal(ua.browser, 'mozilla'); 67 | assert.equal(ua.mobile, false); 68 | assert.equal(ua.version, '11.0'); 69 | assert.equal(ua.ieMobile, false); 70 | 71 | // Windows 8.1 with Update HTML/JS appx 72 | ua = Kinetic._parseUA('Mozilla/5.0 (Windows NT 6.3; Win64; x64; Trident/7.0; .NET4.0E; .NET4.0C; .NET CLR 3.5.30729; .NET CLR 2.0.50727; .NET CLR 3.0.30729; Tablet PC 2.0; MSAppHost/2.0; rv:11.0) like Gecko'); 73 | assert.equal(ua.browser, 'mozilla'); 74 | assert.equal(ua.mobile, false); 75 | assert.equal(ua.version, '11.0'); 76 | assert.equal(ua.ieMobile, false); 77 | 78 | 79 | }); 80 | 81 | 82 | 83 | 84 | }); 85 | -------------------------------------------------------------------------------- /src/shapes/Ring.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | // the 0.0001 offset fixes a bug in Chrome 27 3 | var PIx2 = (Math.PI * 2) - 0.0001; 4 | 5 | /** 6 | * Ring constructor 7 | * @constructor 8 | * @augments Kinetic.Shape 9 | * @param {Object} config 10 | * @param {Number} config.innerRadius 11 | * @param {Number} config.outerRadius 12 | * @param {Boolean} [config.clockwise] 13 | * @@shapeParams 14 | * @@nodeParams 15 | * @example 16 | * var ring = new Kinetic.Ring({ 17 | * innerRadius: 40, 18 | * outerRadius: 80, 19 | * fill: 'red', 20 | * stroke: 'black', 21 | * strokeWidth: 5 22 | * }); 23 | */ 24 | Kinetic.Ring = function(config) { 25 | this.___init(config); 26 | }; 27 | 28 | Kinetic.Ring.prototype = { 29 | ___init: function(config) { 30 | // call super constructor 31 | Kinetic.Shape.call(this, config); 32 | this.className = 'Ring'; 33 | this.sceneFunc(this._sceneFunc); 34 | }, 35 | _sceneFunc: function(context) { 36 | context.beginPath(); 37 | context.arc(0, 0, this.getInnerRadius(), 0, PIx2, false); 38 | context.moveTo(this.getOuterRadius(), 0); 39 | context.arc(0, 0, this.getOuterRadius(), PIx2, 0, true); 40 | context.closePath(); 41 | context.fillStrokeShape(this); 42 | }, 43 | // implements Shape.prototype.getWidth() 44 | getWidth: function() { 45 | return this.getOuterRadius() * 2; 46 | }, 47 | // implements Shape.prototype.getHeight() 48 | getHeight: function() { 49 | return this.getOuterRadius() * 2; 50 | }, 51 | // implements Shape.prototype.setWidth() 52 | setWidth: function(width) { 53 | Kinetic.Node.prototype.setWidth.call(this, width); 54 | if (this.outerRadius() !== width / 2) { 55 | this.setOuterRadius(width / 2); 56 | } 57 | }, 58 | // implements Shape.prototype.setHeight() 59 | setHeight: function(height) { 60 | Kinetic.Node.prototype.setHeight.call(this, height); 61 | if (this.outerRadius() !== height / 2) { 62 | this.setOuterRadius(height / 2); 63 | } 64 | }, 65 | setOuterRadius : function(val) { 66 | this._setAttr('outerRadius', val); 67 | this.setWidth(val * 2); 68 | this.setHeight(val * 2); 69 | } 70 | }; 71 | Kinetic.Util.extend(Kinetic.Ring, Kinetic.Shape); 72 | 73 | // add getters setters 74 | Kinetic.Factory.addGetterSetter(Kinetic.Ring, 'innerRadius', 0); 75 | 76 | /** 77 | * get/set innerRadius 78 | * @name innerRadius 79 | * @method 80 | * @memberof Kinetic.Ring.prototype 81 | * @param {Number} innerRadius 82 | * @returns {Number} 83 | * @example 84 | * // get inner radius 85 | * var innerRadius = ring.innerRadius(); 86 | * 87 | * // set inner radius 88 | * ring.innerRadius(20); 89 | */ 90 | 91 | Kinetic.Factory.addGetter(Kinetic.Ring, 'outerRadius', 0); 92 | Kinetic.Factory.addOverloadedGetterSetter(Kinetic.Ring, 'outerRadius'); 93 | 94 | /** 95 | * get/set outerRadius 96 | * @name outerRadius 97 | * @method 98 | * @memberof Kinetic.Ring.prototype 99 | * @param {Number} outerRadius 100 | * @returns {Number} 101 | * @example 102 | * // get outer radius 103 | * var outerRadius = ring.outerRadius(); 104 | * 105 | * // set outer radius 106 | * ring.outerRadius(20); 107 | */ 108 | 109 | Kinetic.Collection.mapMethods(Kinetic.Ring); 110 | })(); 111 | -------------------------------------------------------------------------------- /test/unit/Animation-test.js: -------------------------------------------------------------------------------- 1 | suite('Animation', function() { 2 | // ====================================================== 3 | test('test start and stop', function() { 4 | var stage = addStage(); 5 | var layer = new Kinetic.Layer(); 6 | var rect = new Kinetic.Rect({ 7 | x: 200, 8 | y: 100, 9 | width: 100, 10 | height: 50, 11 | fill: 'green', 12 | stroke: 'black', 13 | strokeWidth: 4 14 | }); 15 | 16 | layer.add(rect); 17 | stage.add(layer); 18 | 19 | var amplitude = 150; 20 | var period = 1000; 21 | // in ms 22 | var centerX = stage.getWidth() / 2 - 100 / 2; 23 | 24 | var anim = new Kinetic.Animation(function(frame) { 25 | rect.setX(amplitude * Math.sin(frame.time * 2 * Math.PI / period) + centerX); 26 | }, layer); 27 | var a = Kinetic.Animation.animations; 28 | var startLen = a.length; 29 | 30 | assert.equal(a.length, startLen, '1should be no animations running'); 31 | 32 | anim.start(); 33 | assert.equal(a.length, startLen + 1, '2should be 1 animation running'); 34 | 35 | anim.stop(); 36 | assert.equal(a.length, startLen, '3should be no animations running'); 37 | 38 | anim.start(); 39 | assert.equal(a.length, startLen + 1, '4should be 1 animation running'); 40 | 41 | anim.start(); 42 | assert.equal(a.length, startLen + 1, '5should be 1 animation runningg'); 43 | 44 | anim.stop(); 45 | assert.equal(a.length, startLen, '6should be no animations running'); 46 | 47 | anim.stop(); 48 | assert.equal(a.length, startLen, '7should be no animations running'); 49 | }); 50 | 51 | // ====================================================== 52 | test('layer batch draw', function() { 53 | var stage = addStage(); 54 | var layer = new Kinetic.Layer(); 55 | var rect = new Kinetic.Rect({ 56 | x: 200, 57 | y: 100, 58 | width: 100, 59 | height: 50, 60 | fill: 'green', 61 | stroke: 'black', 62 | strokeWidth: 4 63 | }); 64 | 65 | layer.add(rect); 66 | stage.add(layer); 67 | 68 | draws = 0; 69 | 70 | layer.on('draw', function() { 71 | //console.log('draw') 72 | draws++; 73 | }); 74 | 75 | layer.draw(); 76 | layer.draw(); 77 | layer.draw(); 78 | 79 | assert.equal(draws, 3, 'draw count should be 3'); 80 | 81 | layer.batchDraw(); 82 | layer.batchDraw(); 83 | layer.batchDraw(); 84 | 85 | assert.notEqual(draws, 6, 'should not be 6 draws'); 86 | }); 87 | 88 | // ====================================================== 89 | test('stage batch draw', function() { 90 | var stage = addStage(); 91 | var layer = new Kinetic.Layer(); 92 | var rect = new Kinetic.Rect({ 93 | x: 200, 94 | y: 100, 95 | width: 100, 96 | height: 50, 97 | fill: 'green', 98 | stroke: 'black', 99 | strokeWidth: 4 100 | }); 101 | 102 | layer.add(rect); 103 | stage.add(layer); 104 | 105 | draws = 0; 106 | 107 | layer.on('draw', function() { 108 | //console.log('draw') 109 | draws++; 110 | }); 111 | 112 | stage.draw(); 113 | stage.draw(); 114 | stage.draw(); 115 | 116 | assert.equal(draws, 3, 'draw count should be 3'); 117 | 118 | stage.batchDraw(); 119 | stage.batchDraw(); 120 | stage.batchDraw(); 121 | 122 | assert.notEqual(draws, 6, 'should not be 6 draws'); 123 | 124 | }); 125 | }); -------------------------------------------------------------------------------- /test/unit/shapes/Spline-test.js: -------------------------------------------------------------------------------- 1 | suite('Spline', function() { 2 | // ====================================================== 3 | test('add splines', function() { 4 | var stage = addStage(); 5 | var layer = new Kinetic.Layer(); 6 | 7 | var line1 = new Kinetic.Line({ 8 | points: [73,160,340,23,500,109,300,109], 9 | stroke: 'blue', 10 | strokeWidth: 10, 11 | lineCap: 'round', 12 | lineJoin: 'round', 13 | draggable: true, 14 | tension: 1 15 | }); 16 | 17 | var line2 = new Kinetic.Line({ 18 | points: [73,160,340,23,500,109], 19 | stroke: 'red', 20 | strokeWidth: 10, 21 | lineCap: 'round', 22 | lineJoin: 'round', 23 | draggable: true, 24 | tension: 1 25 | }); 26 | 27 | var line3 = new Kinetic.Line({ 28 | points: [73,160,340,23], 29 | stroke: 'green', 30 | strokeWidth: 10, 31 | lineCap: 'round', 32 | lineJoin: 'round', 33 | draggable: true, 34 | tension: 1 35 | }); 36 | 37 | layer.add(line1); 38 | layer.add(line2); 39 | layer.add(line3); 40 | stage.add(layer); 41 | 42 | assert.equal(line1.getClassName(), 'Line'); 43 | 44 | var trace = layer.getContext().getTrace(); 45 | 46 | //console.log(trace); 47 | assert.equal(trace, 'clearRect(0,0,578,200);save();lineJoin=round;transform(1,0,0,1,0,0);beginPath();moveTo(73,160);quadraticCurveTo(74.006,54.77,340,23);bezierCurveTo(501.006,3.77,519.038,68.068,500,109);quadraticCurveTo(479.038,154.068,300,109);lineCap=round;lineWidth=10;strokeStyle=blue;stroke();restore();save();lineJoin=round;transform(1,0,0,1,0,0);beginPath();moveTo(73,160);quadraticCurveTo(74.006,54.77,340,23);quadraticCurveTo(501.006,3.77,500,109);lineCap=round;lineWidth=10;strokeStyle=red;stroke();restore();save();lineJoin=round;transform(1,0,0,1,0,0);beginPath();moveTo(73,160);lineTo(340,23);lineCap=round;lineWidth=10;strokeStyle=green;stroke();restore();'); 48 | }); 49 | 50 | // ====================================================== 51 | test('update spline points', function() { 52 | var stage = addStage(); 53 | var layer = new Kinetic.Layer(); 54 | 55 | var spline = new Kinetic.Line({ 56 | points: [73,160,340,23,500,109,300,109], 57 | stroke: 'blue', 58 | strokeWidth: 10, 59 | lineCap: 'round', 60 | lineJoin: 'round', 61 | draggable: true, 62 | tension: 1 63 | }); 64 | 65 | 66 | layer.add(spline); 67 | stage.add(layer); 68 | 69 | assert.equal(spline.getTensionPoints().length, 12); 70 | 71 | spline.setPoints([73,160,340,23,500,109]); 72 | 73 | assert.equal(spline.getTensionPoints().length, 6); 74 | 75 | layer.draw(); 76 | 77 | 78 | }); 79 | 80 | // ====================================================== 81 | test('add point to spline points', function() { 82 | var stage = addStage(); 83 | var layer = new Kinetic.Layer(); 84 | 85 | var spline = new Kinetic.Line({ 86 | points: [73,160,340,23,500,109,300,109], 87 | stroke: 'blue', 88 | strokeWidth: 10, 89 | lineCap: 'round', 90 | lineJoin: 'round', 91 | draggable: true, 92 | tension: 1 93 | }); 94 | 95 | 96 | layer.add(spline); 97 | stage.add(layer); 98 | 99 | assert.equal(spline.getPoints().length, 8); 100 | 101 | var points = spline.getPoints(); 102 | points.push(300); 103 | points.push(200); 104 | spline.clearCache(); 105 | 106 | assert.equal(spline.getPoints().length, 10); 107 | 108 | layer.draw(); 109 | }); 110 | }); -------------------------------------------------------------------------------- /src/shapes/Ellipse.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | // the 0.0001 offset fixes a bug in Chrome 27 3 | var PIx2 = (Math.PI * 2) - 0.0001, 4 | ELLIPSE = 'Ellipse'; 5 | 6 | /** 7 | * Ellipse constructor 8 | * @constructor 9 | * @augments Kinetic.Shape 10 | * @param {Object} config 11 | * @param {Object} config.radius defines x and y radius 12 | * @@ShapeParams 13 | * @@NodeParams 14 | * @example 15 | * var ellipse = new Kinetic.Ellipse({ 16 | * radius : { 17 | * x : 50, 18 | * y : 50 19 | * }, 20 | * fill: 'red' 21 | * }); 22 | */ 23 | Kinetic.Ellipse = function(config) { 24 | this.___init(config); 25 | }; 26 | 27 | Kinetic.Ellipse.prototype = { 28 | ___init: function(config) { 29 | // call super constructor 30 | Kinetic.Shape.call(this, config); 31 | this.className = ELLIPSE; 32 | this.sceneFunc(this._sceneFunc); 33 | }, 34 | _sceneFunc: function(context) { 35 | var rx = this.getRadiusX(), 36 | ry = this.getRadiusY(); 37 | 38 | context.beginPath(); 39 | context.save(); 40 | if(rx !== ry) { 41 | context.scale(1, ry / rx); 42 | } 43 | context.arc(0, 0, rx, 0, PIx2, false); 44 | context.restore(); 45 | context.closePath(); 46 | context.fillStrokeShape(this); 47 | }, 48 | // implements Shape.prototype.getWidth() 49 | getWidth: function() { 50 | return this.getRadiusX() * 2; 51 | }, 52 | // implements Shape.prototype.getHeight() 53 | getHeight: function() { 54 | return this.getRadiusY() * 2; 55 | }, 56 | // implements Shape.prototype.setWidth() 57 | setWidth: function(width) { 58 | Kinetic.Node.prototype.setWidth.call(this, width); 59 | this.setRadius({ 60 | x: width / 2 61 | }); 62 | }, 63 | // implements Shape.prototype.setHeight() 64 | setHeight: function(height) { 65 | Kinetic.Node.prototype.setHeight.call(this, height); 66 | this.setRadius({ 67 | y: height / 2 68 | }); 69 | } 70 | }; 71 | Kinetic.Util.extend(Kinetic.Ellipse, Kinetic.Shape); 72 | 73 | // add getters setters 74 | Kinetic.Factory.addComponentsGetterSetter(Kinetic.Ellipse, 'radius', ['x', 'y']); 75 | 76 | /** 77 | * get/set radius 78 | * @name radius 79 | * @method 80 | * @memberof Kinetic.Ellipse.prototype 81 | * @param {Object} radius 82 | * @param {Number} radius.x 83 | * @param {Number} radius.y 84 | * @returns {Object} 85 | * @example 86 | * // get radius 87 | * var radius = ellipse.radius(); 88 | * 89 | * // set radius 90 | * ellipse.radius({ 91 | * x: 200, 92 | * y: 100 93 | * }); 94 | */ 95 | 96 | Kinetic.Factory.addGetterSetter(Kinetic.Ellipse, 'radiusX', 0); 97 | /** 98 | * get/set radius x 99 | * @name radiusX 100 | * @method 101 | * @memberof Kinetic.Ellipse.prototype 102 | * @param {Number} x 103 | * @returns {Number} 104 | * @example 105 | * // get radius x 106 | * var radiusX = ellipse.radiusX(); 107 | * 108 | * // set radius x 109 | * ellipse.radiusX(200); 110 | */ 111 | 112 | Kinetic.Factory.addGetterSetter(Kinetic.Ellipse, 'radiusY', 0); 113 | /** 114 | * get/set radius y 115 | * @name radiusY 116 | * @method 117 | * @memberof Kinetic.Ellipse.prototype 118 | * @param {Number} y 119 | * @returns {Number} 120 | * @example 121 | * // get radius y 122 | * var radiusY = ellipse.radiusY(); 123 | * 124 | * // set radius y 125 | * ellipse.radiusY(200); 126 | */ 127 | 128 | Kinetic.Collection.mapMethods(Kinetic.Ellipse); 129 | 130 | })(); -------------------------------------------------------------------------------- /test/unit/DragAndDrop-test.js: -------------------------------------------------------------------------------- 1 | suite('DragAndDrop', function() { 2 | 3 | // ====================================================== 4 | test('test drag and drop properties and methods', function(done) { 5 | var stage = addStage(); 6 | var layer = new Kinetic.Layer(); 7 | var circle = new Kinetic.Circle({ 8 | x: stage.getWidth() / 2, 9 | y: stage.getHeight() / 2, 10 | radius: 70, 11 | fill: 'green', 12 | stroke: 'black', 13 | strokeWidth: 4, 14 | name: 'myCircle' 15 | }); 16 | 17 | stage.add(layer); 18 | layer.add(circle); 19 | 20 | setTimeout(function() { 21 | 22 | layer.draw(); 23 | 24 | // test defaults 25 | assert.equal(circle.draggable(), false); 26 | 27 | //change properties 28 | circle.setDraggable(true); 29 | 30 | 31 | //circle.on('click', function(){}); 32 | 33 | layer.draw(); 34 | 35 | showHit(layer); 36 | 37 | // test new properties 38 | assert.equal(circle.getDraggable(), true); 39 | 40 | done(); 41 | 42 | }, 50); 43 | }); 44 | 45 | // ====================================================== 46 | test('multiple drag and drop sets with setDraggable()', function() { 47 | var stage = addStage(); 48 | var layer = new Kinetic.Layer(); 49 | var circle = new Kinetic.Circle({ 50 | x: 380, 51 | y: stage.getHeight() / 2, 52 | radius: 70, 53 | strokeWidth: 4, 54 | fill: 'red', 55 | stroke: 'black' 56 | }); 57 | 58 | circle.setDraggable(true); 59 | assert.equal(circle.getDraggable(), true); 60 | circle.setDraggable(true); 61 | assert.equal(circle.getDraggable(), true); 62 | circle.setDraggable(false); 63 | assert.equal(!circle.getDraggable(), true); 64 | 65 | layer.add(circle); 66 | stage.add(layer); 67 | }); 68 | 69 | // ====================================================== 70 | test('right click is not for dragging', function() { 71 | var stage = addStage(); 72 | 73 | var top = stage.content.getBoundingClientRect().top; 74 | 75 | var layer = new Kinetic.Layer(); 76 | 77 | var circle = new Kinetic.Circle({ 78 | x: stage.getWidth() / 2, 79 | y: stage.getHeight() / 2, 80 | radius: 70, 81 | fill: 'green', 82 | stroke: 'black', 83 | strokeWidth: 4, 84 | name: 'myCircle', 85 | draggable: true 86 | }); 87 | 88 | layer.add(circle); 89 | stage.add(layer); 90 | 91 | stage._mousedown({ 92 | clientX: 291, 93 | clientY: 112 + top, 94 | }); 95 | 96 | stage._mousemove({ 97 | clientX: 311, 98 | clientY: 112 + top, 99 | }); 100 | 101 | assert(circle.isDragging(), 'dragging is ok'); 102 | 103 | Kinetic.DD._endDragBefore(); 104 | stage._mouseup({ 105 | clientX: 291, 106 | clientY: 112 + top 107 | }); 108 | Kinetic.DD._endDragAfter({dragEndNode:circle}); 109 | 110 | 111 | 112 | stage._mousedown({ 113 | clientX: 291, 114 | clientY: 112 + top, 115 | button: 2 116 | }); 117 | 118 | stage._mousemove({ 119 | clientX: 311, 120 | clientY: 112 + top, 121 | button: 2 122 | }); 123 | 124 | assert(circle.isDragging() === false, 'no dragging with right click'); 125 | 126 | Kinetic.DD._endDragBefore(); 127 | stage._mouseup({ 128 | clientX: 291, 129 | clientY: 112 + top, 130 | button: 2 131 | }); 132 | Kinetic.DD._endDragAfter({dragEndNode:circle}); 133 | }); 134 | }); -------------------------------------------------------------------------------- /test/unit/plugins/Label-test.js: -------------------------------------------------------------------------------- 1 | suite('Label', function() { 2 | // ====================================================== 3 | test('add label', function() { 4 | var stage = addStage(); 5 | var layer = new Kinetic.Layer(); 6 | 7 | var label = new Kinetic.Label({ 8 | x: 100, 9 | y: 100, 10 | draggable: true 11 | }); 12 | 13 | // add a tag to the label 14 | label.add(new Kinetic.Tag({ 15 | fill: '#bbb', 16 | stroke: '#333', 17 | shadowColor: 'black', 18 | shadowBlur: 10, 19 | shadowOffset: [10, 10], 20 | shadowOpacity: 0.2, 21 | lineJoin: 'round', 22 | pointerDirection: 'up', 23 | pointerWidth: 20, 24 | pointerHeight: 20, 25 | cornerRadius: 5 26 | })); 27 | 28 | // add text to the label 29 | label.add(new Kinetic.Text({ 30 | text: '', 31 | fontSize: 50, 32 | //fontFamily: 'Calibri', 33 | //fontStyle: 'normal', 34 | lineHeight: 1.2, 35 | //padding: 10, 36 | fill: 'green' 37 | })); 38 | 39 | layer.add(label); 40 | stage.add(layer); 41 | 42 | 43 | label.getText().setFontSize(100); 44 | 45 | label.getText().setFontSize(50); 46 | 47 | label.getText().setText('Hello big world'); 48 | 49 | layer.draw(); 50 | 51 | 52 | assert.equal(label.getType(), 'Group'); 53 | assert.equal(label.getClassName(), 'Label'); 54 | 55 | 56 | // use relaxed trace because text can be a slightly different size in different browsers, 57 | // resulting in slightly different tag dimensions 58 | var relaxedTrace = layer.getContext().getTrace(true); 59 | 60 | assert.equal(relaxedTrace, 'clearRect();save();save();globalAlpha;shadowColor;shadowBlur;shadowOffsetX;shadowOffsetY;drawImage();restore();drawImage();restore();save();transform();font;textBaseline;textAlign;save();translate();translate();save();fillStyle;fillText();restore();translate();restore();restore();clearRect();save();save();globalAlpha;shadowColor;shadowBlur;shadowOffsetX;shadowOffsetY;drawImage();restore();drawImage();restore();save();transform();font;textBaseline;textAlign;save();translate();translate();save();fillStyle;fillText();restore();translate();restore();restore();'); 61 | 62 | }); 63 | 64 | // ====================================================== 65 | test('create label from json', function() { 66 | var stage = addStage(); 67 | 68 | var json = '{"attrs":{"x":100,"y":100,"draggable":true},"className":"Label","children":[{"attrs":{"fill":"#bbb","stroke":"#333","shadowColor":"black","shadowBlur":10,"shadowOffsetX":10,"shadowOffsetY":10,"shadowOpacity":0.2,"lineJoin":"round","pointerDirection":"up","pointerWidth":20,"pointerHeight":20,"cornerRadius":5,"x":-151.5,"y":20,"width":303,"height":60},"className":"Tag"},{"attrs":{"text":"Hello big world","fontSize":50,"lineHeight":1.2,"fill":"green","width":"auto","height":"auto","x":-151.5,"y":20},"className":"Text"}]}'; 69 | var layer = new Kinetic.Layer(); 70 | 71 | var label = Kinetic.Node.create(json); 72 | 73 | layer.add(label); 74 | stage.add(layer); 75 | }); 76 | 77 | test('find label class', function() { 78 | var stage = addStage(); 79 | var layer = new Kinetic.Layer(); 80 | 81 | var label = new Kinetic.Label({ 82 | x: 100, 83 | y: 100 84 | }); 85 | 86 | // add a tag to the label 87 | label.add(new Kinetic.Tag({ 88 | fill: '#bbb' 89 | })); 90 | 91 | // add text to the label 92 | label.add(new Kinetic.Text({ 93 | text: 'Test Label', 94 | fill: 'green' 95 | })); 96 | 97 | layer.add(label); 98 | stage.add(layer); 99 | 100 | assert.equal(stage.find('Label')[0], label); 101 | }); 102 | 103 | }); -------------------------------------------------------------------------------- /test/unit/filters/HSL-test.js: -------------------------------------------------------------------------------- 1 | suite('HSL', function() { 2 | 3 | 4 | // ====================================================== 5 | test('hue shift tween transparancy', function(done) { 6 | var stage = addStage(); 7 | 8 | var imageObj = new Image(); 9 | imageObj.onload = function() { 10 | 11 | var layer = new Kinetic.Layer(); 12 | darth = new Kinetic.Image({ 13 | x: 10, 14 | y: 10, 15 | image: imageObj, 16 | draggable: true 17 | }); 18 | 19 | layer.add(darth); 20 | stage.add(layer); 21 | 22 | darth.cache(); 23 | darth.filters([Kinetic.Filters.HSL]); 24 | darth.hue(360); 25 | layer.draw(); 26 | 27 | var tween = new Kinetic.Tween({ 28 | node: darth, 29 | duration: 1.0, 30 | hue: 0, 31 | easing: Kinetic.Easings.EaseInOut 32 | }); 33 | 34 | darth.on('mouseover', function() { 35 | tween.play(); 36 | }); 37 | 38 | darth.on('mouseout', function() { 39 | tween.reverse(); 40 | }); 41 | 42 | done(); 43 | }; 44 | imageObj.src = 'assets/lion.png'; 45 | 46 | }); 47 | 48 | // ====================================================== 49 | test('HSL luminance tween transparancy', function(done) { 50 | var stage = addStage(); 51 | 52 | var imageObj = new Image(); 53 | imageObj.onload = function() { 54 | 55 | var layer = new Kinetic.Layer(); 56 | darth = new Kinetic.Image({ 57 | x: 10, 58 | y: 10, 59 | image: imageObj, 60 | draggable: true 61 | }); 62 | 63 | layer.add(darth); 64 | stage.add(layer); 65 | 66 | darth.cache(); 67 | darth.filters([Kinetic.Filters.HSL]); 68 | darth.luminance(1.0); 69 | layer.draw(); 70 | 71 | var tween = new Kinetic.Tween({ 72 | node: darth, 73 | duration: 1.0, 74 | luminance: -1.0, 75 | easing: Kinetic.Easings.EaseInOut 76 | }); 77 | 78 | darth.on('mouseover', function() { 79 | tween.play(); 80 | }); 81 | 82 | darth.on('mouseout', function() { 83 | tween.reverse(); 84 | }); 85 | 86 | done(); 87 | }; 88 | imageObj.src = 'assets/lion.png'; 89 | 90 | }); 91 | 92 | // ====================================================== 93 | test('HSL saturation tween transparancy', function(done) { 94 | var stage = addStage(); 95 | 96 | var imageObj = new Image(); 97 | imageObj.onload = function() { 98 | 99 | var layer = new Kinetic.Layer(); 100 | darth = new Kinetic.Image({ 101 | x: 10, 102 | y: 10, 103 | image: imageObj, 104 | draggable: true 105 | }); 106 | 107 | layer.add(darth); 108 | stage.add(layer); 109 | 110 | darth.cache(); 111 | darth.filters([Kinetic.Filters.HSL]); 112 | darth.saturation(1.0); 113 | layer.draw(); 114 | 115 | var tween = new Kinetic.Tween({ 116 | node: darth, 117 | duration: 1.0, 118 | saturation: -1.0, 119 | easing: Kinetic.Easings.EaseInOut 120 | }); 121 | 122 | darth.on('mouseover', function() { 123 | tween.play(); 124 | }); 125 | 126 | darth.on('mouseout', function() { 127 | tween.reverse(); 128 | }); 129 | 130 | done(); 131 | }; 132 | imageObj.src = 'assets/lion.png'; 133 | 134 | }); 135 | }); 136 | -------------------------------------------------------------------------------- /test/unit/Tween-test.js: -------------------------------------------------------------------------------- 1 | suite('Tween', function() { 2 | 3 | // ====================================================== 4 | test('tween node', function(done) { 5 | var stage = addStage(); 6 | 7 | var layer = new Kinetic.Layer(); 8 | 9 | var circle = new Kinetic.Circle({ 10 | x: 100, 11 | y: stage.getHeight() / 2, 12 | radius: 70, 13 | fill: 'green', 14 | stroke: 'blue', 15 | strokeWidth: 4 16 | }); 17 | 18 | layer.add(circle); 19 | stage.add(layer); 20 | 21 | var finishCount = 0; 22 | var onFinish = function() { 23 | assert(++finishCount <= 1, 'finishCount should not exceed 1'); 24 | done(); 25 | } 26 | 27 | var tweens = 0; 28 | var attrs = 0; 29 | 30 | for (var key in Kinetic.Tween.tweens) { 31 | tweens++; 32 | } 33 | for (var key in Kinetic.Tween.attrs) { 34 | attrs++; 35 | } 36 | 37 | assert.equal(tweens, 0); 38 | assert.equal(attrs, 0); 39 | 40 | var tween = new Kinetic.Tween({ 41 | node: circle, 42 | duration: 0.2, 43 | x: 200, 44 | y: 100, 45 | onFinish: onFinish 46 | }).play(); 47 | 48 | var tweens = 0; 49 | var attrs = 0; 50 | for (var key in Kinetic.Tween.tweens) { 51 | tweens++; 52 | } 53 | for (var key in Kinetic.Tween.attrs[circle._id][tween._id]) { 54 | attrs++; 55 | } 56 | 57 | assert.equal(tweens, 1); 58 | assert.equal(attrs, 2); 59 | 60 | assert.notEqual(Kinetic.Tween.attrs[circle._id][tween._id].x, undefined); 61 | assert.notEqual(Kinetic.Tween.attrs[circle._id][tween._id].y, undefined); 62 | 63 | }); 64 | 65 | // ====================================================== 66 | test('destroy tween while tweening', function() { 67 | var stage = addStage(); 68 | 69 | var layer = new Kinetic.Layer(); 70 | 71 | var circle = new Kinetic.Circle({ 72 | x: 100, 73 | y: stage.getHeight() / 2, 74 | radius: 70, 75 | fill: 'green', 76 | stroke: 'blue', 77 | strokeWidth: 4 78 | }); 79 | 80 | layer.add(circle); 81 | stage.add(layer); 82 | 83 | 84 | var tween = new Kinetic.Tween({ 85 | node: circle, 86 | duration: 0.2, 87 | x: 200, 88 | y: 100 89 | }).play(); 90 | 91 | // start/diff object = attrs.nodeId.tweenId.attr 92 | // tweenId = tweens.nodeId.attr 93 | 94 | assert.notEqual(tween._id, undefined); 95 | assert.equal(Kinetic.Tween.tweens[circle._id].x, tween._id); 96 | assert.notEqual(Kinetic.Tween.attrs[circle._id][tween._id], undefined); 97 | 98 | tween.destroy(); 99 | 100 | assert.equal(Kinetic.Tween.tweens[circle._id].x, undefined); 101 | assert.equal(Kinetic.Tween.attrs[circle._id][tween._id], undefined); 102 | 103 | 104 | }); 105 | 106 | // ====================================================== 107 | test('zero duration', function(done) { 108 | var stage = addStage(); 109 | 110 | var layer = new Kinetic.Layer(); 111 | 112 | var circle = new Kinetic.Circle({ 113 | x: 100, 114 | y: stage.getHeight() / 2, 115 | radius: 70, 116 | fill: 'green', 117 | stroke: 'blue', 118 | strokeWidth: 4 119 | }); 120 | 121 | layer.add(circle); 122 | stage.add(layer); 123 | 124 | 125 | var tween = new Kinetic.Tween({ 126 | node: circle, 127 | duration: 0, 128 | x: 200, 129 | y: 100 130 | }); 131 | tween.play(); 132 | 133 | 134 | setTimeout(function(){ 135 | "use strict"; 136 | assert.equal(circle.x(), 200); 137 | assert.equal(circle.y(), 100); 138 | done(); 139 | }, 60); 140 | 141 | }); 142 | 143 | }); -------------------------------------------------------------------------------- /test/unit/filters/Ripple-test.js: -------------------------------------------------------------------------------- 1 | suite('Ripple', function() { 2 | // ====================================================== 3 | test('basic ripple', function(done) { 4 | var stage = addStage(); 5 | 6 | var imageObj = new Image(); 7 | imageObj.onload = function() { 8 | 9 | var layer = new Kinetic.Layer(); 10 | darth = new Kinetic.Image({ 11 | x: 10, 12 | y: 10, 13 | image: imageObj, 14 | draggable: true 15 | }); 16 | 17 | layer.add(darth); 18 | stage.add(layer); 19 | 20 | darth.cache(); 21 | darth.filters([Kinetic.Filters.Ripple]); 22 | darth.rippleSize(10); 23 | 24 | assert.equal(darth.rippleSize(), 10); 25 | assert.equal(darth._filterUpToDate, false); 26 | 27 | layer.draw(); 28 | 29 | assert.equal(darth._filterUpToDate, true); 30 | 31 | darth.rippleSize(20); 32 | 33 | assert.equal(darth.rippleSize(), 20); 34 | assert.equal(darth._filterUpToDate, false); 35 | 36 | layer.draw(); 37 | 38 | assert.equal(darth._filterUpToDate, true); 39 | 40 | done(); 41 | }; 42 | imageObj.src = 'assets/lion.png'; 43 | 44 | }); 45 | 46 | // ====================================================== 47 | test('tween ripple offset', function(done) { 48 | var stage = addStage(); 49 | 50 | var imageObj = new Image(); 51 | imageObj.onload = function() { 52 | 53 | var layer = new Kinetic.Layer(); 54 | darth = new Kinetic.Image({ 55 | x: 10, 56 | y: 10, 57 | image: imageObj, 58 | draggable: true 59 | }); 60 | 61 | layer.add(darth); 62 | stage.add(layer); 63 | 64 | darth.cache(); 65 | darth.filters([Kinetic.Filters.Ripple]); 66 | darth.rippleSize(16); 67 | darth.rippleOffset(0); 68 | layer.draw(); 69 | 70 | var tween = new Kinetic.Tween({ 71 | node: darth, 72 | duration: 2.0, 73 | rippleOffset: 32, 74 | //rippleSize: 64, 75 | easing: Kinetic.Easings.EaseInOut 76 | }); 77 | 78 | darth.on('mouseover', function() { 79 | tween.play(); 80 | }); 81 | 82 | darth.on('mouseout', function() { 83 | tween.reverse(); 84 | }); 85 | 86 | done(); 87 | 88 | }; 89 | imageObj.src = 'assets/lion.png'; 90 | }); 91 | 92 | // ====================================================== 93 | test('tween ripple size', function(done) { 94 | var stage = addStage(); 95 | 96 | var imageObj = new Image(); 97 | imageObj.onload = function() { 98 | 99 | var layer = new Kinetic.Layer(); 100 | darth = new Kinetic.Image({ 101 | x: 10, 102 | y: 10, 103 | image: imageObj, 104 | draggable: true 105 | }); 106 | 107 | layer.add(darth); 108 | stage.add(layer); 109 | 110 | darth.cache(); 111 | darth.filters([Kinetic.Filters.Ripple]); 112 | darth.rippleSize(16); 113 | darth.rippleOffset(0); 114 | layer.draw(); 115 | 116 | var tween = new Kinetic.Tween({ 117 | node: darth, 118 | duration: 2.0, 119 | rippleSize: 64, 120 | easing: Kinetic.Easings.EaseInOut 121 | }); 122 | 123 | darth.on('mouseover', function() { 124 | tween.play(); 125 | }); 126 | 127 | darth.on('mouseout', function() { 128 | tween.reverse(); 129 | }); 130 | 131 | done(); 132 | 133 | }; 134 | imageObj.src = 'assets/lion.png'; 135 | }); 136 | 137 | }); -------------------------------------------------------------------------------- /test/unit/filters/Kaleidoscope-test.js: -------------------------------------------------------------------------------- 1 | suite('Kaleidoscope', function() { 2 | // ====================================================== 3 | test('basic', function(done) { 4 | var stage = addStage(); 5 | 6 | var imageObj = new Image(); 7 | imageObj.onload = function() { 8 | 9 | var layer = new Kinetic.Layer(); 10 | darth = new Kinetic.Image({ 11 | x: 10, 12 | y: 10, 13 | image: imageObj, 14 | draggable: true 15 | }); 16 | 17 | layer.add(darth); 18 | stage.add(layer); 19 | 20 | darth.cache(); 21 | darth.filters([Kinetic.Filters.Kaleidoscope]); 22 | darth.kaleidoscopePower(2); 23 | 24 | assert.equal(darth.kaleidoscopePower(), 2); 25 | assert.equal(darth._filterUpToDate, false); 26 | 27 | layer.draw(); 28 | 29 | assert.equal(darth._filterUpToDate, true); 30 | 31 | darth.kaleidoscopePower(3); 32 | 33 | assert.equal(darth.kaleidoscopePower(), 3); 34 | assert.equal(darth._filterUpToDate, false); 35 | 36 | layer.draw(); 37 | 38 | assert.equal(darth._filterUpToDate, true); 39 | 40 | done(); 41 | }; 42 | imageObj.src = 'assets/lion.png'; 43 | 44 | }); 45 | 46 | // ====================================================== 47 | test('tween angle', function(done) { 48 | var stage = addStage(); 49 | 50 | var imageObj = new Image(); 51 | imageObj.onload = function() { 52 | 53 | var layer = new Kinetic.Layer(); 54 | darth = new Kinetic.Image({ 55 | x: 10, 56 | y: 10, 57 | image: imageObj, 58 | draggable: true 59 | }); 60 | 61 | layer.add(darth); 62 | stage.add(layer); 63 | 64 | darth.cache(); 65 | darth.filters([Kinetic.Filters.Kaleidoscope]); 66 | darth.kaleidoscopePower(3); 67 | darth.kaleidoscopeAngle(0); 68 | layer.draw(); 69 | 70 | var tween = new Kinetic.Tween({ 71 | node: darth, 72 | duration: 10.0, 73 | kaleidoscopeAngle: 720, 74 | easing: Kinetic.Easings.EaseInOut 75 | }); 76 | 77 | darth.on('mouseover', function() { 78 | tween.play(); 79 | }); 80 | 81 | darth.on('mouseout', function() { 82 | tween.reverse(); 83 | }); 84 | 85 | done(); 86 | 87 | }; 88 | imageObj.src = 'assets/lion.png'; 89 | }); 90 | 91 | // ====================================================== 92 | test('tween power', function(done) { 93 | var stage = addStage(); 94 | 95 | var imageObj = new Image(); 96 | imageObj.onload = function() { 97 | 98 | var layer = new Kinetic.Layer(); 99 | darth = new Kinetic.Image({ 100 | x: 10, 101 | y: 10, 102 | image: imageObj, 103 | draggable: true 104 | }); 105 | 106 | layer.add(darth); 107 | stage.add(layer); 108 | 109 | darth.cache(); 110 | darth.filters([Kinetic.Filters.Kaleidoscope]); 111 | darth.kaleidoscopePower(0); 112 | darth.kaleidoscopeAngle(0); 113 | layer.draw(); 114 | 115 | var tween = new Kinetic.Tween({ 116 | node: darth, 117 | duration: 2.0, 118 | kaleidoscopePower: 8, 119 | easing: Kinetic.EasingsEaseInOut 120 | }); 121 | 122 | darth.on('mouseover', function() { 123 | tween.play(); 124 | }); 125 | 126 | darth.on('mouseout', function() { 127 | tween.reverse(); 128 | }); 129 | 130 | done(); 131 | 132 | }; 133 | imageObj.src = 'assets/lion.png'; 134 | }); 135 | 136 | }); -------------------------------------------------------------------------------- /src/plugins/Arrow.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | /** 3 | * Arrow constructor 4 | * @constructor 5 | * @memberof Kinetic 6 | * @augments Kinetic.Shape 7 | * @param {Object} config 8 | * @param {Array} config.points 9 | * @param {Number} [config.tension] Higher values will result in a more curvy line. A value of 0 will result in no interpolation. 10 | * The default is 0 11 | * @param {Number} config.pointerLength 12 | * @param {Number} config.pointerWidth 13 | * @@shapeParams 14 | * @@nodeParams 15 | * @example 16 | * var line = new Kinetic.Line({ 17 | * points: [73, 70, 340, 23, 450, 60, 500, 20], 18 | * stroke: 'red', 19 | * tension: 1, 20 | * pointerLength : 10, 21 | * pointerWidth : 12 22 | * }); 23 | */ 24 | Kinetic.Arrow = function(config) { 25 | this.____init(config); 26 | }; 27 | 28 | Kinetic.Arrow.prototype = { 29 | ____init : function(config) { 30 | // call super constructor 31 | Kinetic.Line.call(this, config); 32 | this.className = 'Arrow'; 33 | }, 34 | _sceneFunc : function(ctx) { 35 | var PI2 = Math.PI * 2; 36 | var points = this.points(); 37 | var n = points.length; 38 | var dx = points[n-2] - points[n-4]; 39 | var dy = points[n-1] - points[n-3]; 40 | var radians = (Math.atan2(dy, dx) + PI2) % PI2; 41 | var length = this.pointerLength(); 42 | var width = this.pointerWidth(); 43 | 44 | ctx.save(); 45 | ctx.beginPath(); 46 | ctx.translate(points[n-2], points[n-1]); 47 | ctx.rotate(radians); 48 | ctx.moveTo(0, 0); 49 | ctx.lineTo(-length, width / 2); 50 | ctx.lineTo(-length, -width / 2); 51 | ctx.closePath(); 52 | ctx.restore(); 53 | 54 | if (this.pointerAtBeginning()) { 55 | ctx.save(); 56 | ctx.translate(points[0], points[1]); 57 | dx = points[2] - points[0]; 58 | dy = points[3] - points[1]; 59 | ctx.rotate((Math.atan2(-dy, -dx) + PI2) % PI2); 60 | ctx.moveTo(0, 0); 61 | ctx.lineTo(-10, 6); 62 | ctx.lineTo(-10, -6); 63 | ctx.closePath(); 64 | ctx.restore(); 65 | } 66 | 67 | ctx.fillStrokeShape(this); 68 | Kinetic.Line.prototype._sceneFunc.apply(this, arguments); 69 | } 70 | }; 71 | 72 | Kinetic.Util.extend(Kinetic.Arrow, Kinetic.Line); 73 | /** 74 | * get/set pointerLength 75 | * @name pointerLength 76 | * @method 77 | * @memberof Kinetic.Arrow.prototype 78 | * @param {Number} Length of pointer of arrow. 79 | * The default is 10. 80 | * @returns {Number} 81 | * @example 82 | * // get tension 83 | * var pointerLength = line.pointerLength(); 84 | * 85 | * // set tension 86 | * line.pointerLength(15); 87 | */ 88 | 89 | Kinetic.Factory.addGetterSetter(Kinetic.Arrow, 'pointerLength', 10); 90 | /** 91 | * get/set pointerWidth 92 | * @name pointerWidth 93 | * @method 94 | * @memberof Kinetic.Arrow.prototype 95 | * @param {Number} Width of pointer of arrow. 96 | * The default is 10. 97 | * @returns {Number} 98 | * @example 99 | * // get tension 100 | * var pointerWidth = line.pointerWidth(); 101 | * 102 | * // set tension 103 | * line.pointerWidth(15); 104 | */ 105 | 106 | Kinetic.Factory.addGetterSetter(Kinetic.Arrow, 'pointerWidth', 10); 107 | /** 108 | * get/set pointerAtBeginning 109 | * @name pointerAtBeginning 110 | * @method 111 | * @memberof Kinetic.Arrow.prototype 112 | * @param {Number} Should pointer displayed at beginning of arrow. 113 | * The default is false. 114 | * @returns {Boolean} 115 | * @example 116 | * // get tension 117 | * var pointerAtBeginning = line.pointerAtBeginning(); 118 | * 119 | * // set tension 120 | * line.pointerAtBeginning(true); 121 | */ 122 | 123 | Kinetic.Factory.addGetterSetter(Kinetic.Arrow, 'pointerAtBeginning', false); 124 | Kinetic.Collection.mapMethods(Kinetic.Arrow); 125 | 126 | })(); 127 | 128 | -------------------------------------------------------------------------------- /test/node-runner.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'), 2 | Kinetic = require('../dist/kinetic-dev'); 3 | 4 | 5 | 6 | global.Kinetic = Kinetic; 7 | Kinetic.enableTrace = true; 8 | 9 | // Config MINIMAL test environment 10 | global.suite = function(title, func) { 11 | console.log('Suite : ' + title); 12 | func(); 13 | }; 14 | 15 | global.test = function(title, func) { 16 | try { 17 | console.log('Run test: ' + title); 18 | func(function(){}); 19 | } catch (e) { 20 | console.log('Error at ' + title, e); 21 | throw e; 22 | } 23 | 24 | }; 25 | test.skip = function(){}; 26 | global.assert = function(condtion, message){ 27 | if (!condtion) { 28 | throw 'assert throw:' + message; 29 | } 30 | }; 31 | global.assert.equal = function(left, right){ 32 | if (left !== right) { 33 | 34 | } 35 | }; 36 | global.assert.notEqual = function(left, right){ 37 | if (left === right) { 38 | throw 'assert throw'; 39 | } 40 | }; 41 | 42 | global.addStage = function(){ 43 | return new Kinetic.Stage({ 44 | width: 578, 45 | height: 200 46 | }); 47 | }; 48 | 49 | // Some utils for testing 50 | global.kineticContainer = Kinetic.document.createElement('div'); 51 | Kinetic.document.body.appendChild(kineticContainer); 52 | global.showHit = global.addContainer = function(){ 53 | }; 54 | global.Image = Kinetic._nodeCanvas.Image; 55 | Image.prototype.style = {}; 56 | eval(fs.readFileSync('./test/assets/tiger.js')+""); 57 | eval(fs.readFileSync('./test/assets/worldMap.js')+""); 58 | global.tiger = tiger; 59 | global.worldMap = worldMap; 60 | 61 | 62 | 63 | 64 | // now load all tests 65 | require('./unit/Global-test.js'); 66 | require('./unit/Node-test.js'); 67 | require('./unit/Global-test.js'); 68 | require('./unit/Util-test.js'); 69 | require('./unit/Canvas-test.js'); 70 | require('./unit/Node-test.js'); 71 | require('./unit/Container-test.js'); 72 | require('./unit/Stage-test.js'); 73 | require('./unit/Layer-test.js'); 74 | require('./unit/Shape-test.js'); 75 | require('./unit/Collection-test.js'); 76 | 77 | // shapes --> 78 | require('./unit/shapes/Rect-test.js'); 79 | require('./unit/shapes/Circle-test.js'); 80 | require('./unit/shapes/Image-test.js'); 81 | require('./unit/shapes/Line-test.js'); 82 | require('./unit/shapes/Text-test.js'); 83 | require('./unit/shapes/Blob-test.js'); 84 | require('./unit/shapes/Ellipse-test.js'); 85 | require('./unit/shapes/Polygon-test.js'); 86 | require('./unit/shapes/Spline-test.js'); 87 | require('./unit/shapes/Sprite-test.js'); 88 | require('./unit/shapes/Wedge-test.js'); 89 | require('./unit/shapes/Arc-test.js'); 90 | require('./unit/shapes/Ring-test.js'); 91 | 92 | // // extensions --> 93 | require('./unit/Animation-test.js'); 94 | require('./unit/DragAndDrop-test.js'); 95 | require('./unit/Tween-test.js'); 96 | 97 | // // plugins --> 98 | require('./unit/plugins/Label-test.js'); 99 | require('./unit/plugins/Star-test.js'); 100 | require('./unit/plugins/RegularPolygon-test.js'); 101 | require('./unit/plugins/Path-test.js'); 102 | require('./unit/plugins/TextPath-test.js'); 103 | 104 | // // filters --> 105 | require('./unit/filters/Blur-test.js'); 106 | require('./unit/filters/Brighten-test.js'); 107 | require('./unit/filters/RGB-test.js'); 108 | require('./unit/filters/HSV-test.js'); 109 | require('./unit/filters/HSL-test.js'); 110 | require('./unit/filters/Invert-test.js'); 111 | require('./unit/filters/Mask-test.js'); 112 | require('./unit/filters/Grayscale-test.js'); 113 | require('./unit/filters/Enhance-test.js'); 114 | require('./unit/filters/Pixelate-test.js'); 115 | require('./unit/filters/Noise-test.js'); 116 | require('./unit/filters/Threshold-test.js'); 117 | require('./unit/filters/Posterize-test.js'); 118 | require('./unit/filters/Sepia-test.js'); 119 | require('./unit/filters/Emboss-test.js'); 120 | require('./unit/filters/Solarize-test.js'); 121 | require('./unit/filters/Kaleidoscope-test.js'); 122 | 123 | // //=============== functional tests ================--> 124 | 125 | require('./functional/MouseEvents-test.js'); 126 | require('./functional/TouchEvents-test.js'); 127 | //require('./functional/DragAndDropEvents-test.js'); disabled because of simplest test configuration 128 | 129 | // //=============== manual tests ================--> 130 | // require('./manual/manual-test.js'); disabled because of unlimited animation 131 | -------------------------------------------------------------------------------- /test/unit/shapes/Line-test.js: -------------------------------------------------------------------------------- 1 | suite('Line', function() { 2 | // ====================================================== 3 | test('add line', function() { 4 | var stage = addStage(); 5 | var layer = new Kinetic.Layer(); 6 | 7 | var line = new Kinetic.Line({ 8 | points: [73,160,340,23], 9 | stroke: 'blue', 10 | strokeWidth: 20, 11 | lineCap: 'round', 12 | lineJoin: 'round', 13 | draggable: true, 14 | tension: 0 15 | }); 16 | 17 | layer.add(line); 18 | stage.add(layer); 19 | 20 | line.setPoints([1, 2, 3, 4]); 21 | assert.equal(line.getPoints()[0], 1); 22 | 23 | line.setPoints([5,6,7,8]); 24 | assert.equal(line.getPoints()[0], 5); 25 | 26 | line.setPoints([73, 160, 340, 23, 340, 80]); 27 | assert.equal(line.getPoints()[0], 73); 28 | 29 | assert.equal(line.getClassName(), 'Line'); 30 | 31 | layer.draw(); 32 | showHit(layer); 33 | }); 34 | 35 | // ====================================================== 36 | test('test default ponts array for two lines', function() { 37 | var stage = addStage(); 38 | var layer = new Kinetic.Layer(); 39 | 40 | var line = new Kinetic.Line({ 41 | stroke: 'blue', 42 | strokeWidth: 20, 43 | lineCap: 'round', 44 | lineJoin: 'round', 45 | draggable: true 46 | }); 47 | 48 | var redLine = new Kinetic.Line({ 49 | x: 50, 50 | stroke: 'red', 51 | strokeWidth: 20, 52 | lineCap: 'round', 53 | lineJoin: 'round', 54 | draggable: true 55 | }); 56 | 57 | line.setPoints([0,1,2,3]); 58 | redLine.setPoints([4,5,6,7]); 59 | 60 | layer.add(line).add(redLine); 61 | stage.add(layer); 62 | 63 | assert.equal(line.getPoints()[0], 0); 64 | assert.equal(redLine.getPoints()[0], 4); 65 | 66 | }); 67 | 68 | // ====================================================== 69 | test('add dashed line', function() { 70 | var stage = addStage(); 71 | var layer = new Kinetic.Layer(); 72 | 73 | /* 74 | var points = [{ 75 | x: 73, 76 | y: 160 77 | }, { 78 | x: 340, 79 | y: 23 80 | }, { 81 | x: 500, 82 | y: 109 83 | }, { 84 | x: 500, 85 | y: 180 86 | }]; 87 | */ 88 | 89 | var line = new Kinetic.Line({ 90 | points: [73, 160, 340, 23, 500, 109, 500, 180], 91 | stroke: 'blue', 92 | 93 | strokeWidth: 10, 94 | lineCap: 'round', 95 | lineJoin: 'round', 96 | draggable: true, 97 | dash: [30, 10, 0, 10, 10, 20], 98 | shadowColor: '#aaa', 99 | shadowBlur: 10, 100 | shadowOffset: {x:20, y:20} 101 | //opacity: 0.2 102 | }); 103 | 104 | layer.add(line); 105 | stage.add(layer); 106 | 107 | assert.equal(line.dash().length, 6); 108 | line.dash([10, 10]); 109 | assert.equal(line.dash().length, 2); 110 | 111 | assert.equal(line.getPoints().length, 8); 112 | 113 | }); 114 | 115 | // ====================================================== 116 | test('add line with shadow', function() { 117 | var stage = addStage(); 118 | var layer = new Kinetic.Layer(); 119 | 120 | var line = new Kinetic.Line({ 121 | points: [73,160,340,23], 122 | stroke: 'blue', 123 | strokeWidth: 20, 124 | lineCap: 'round', 125 | lineJoin: 'round', 126 | shadowColor: 'black', 127 | shadowBlur: 20, 128 | shadowOffset: {x: 10, y: 10}, 129 | shadowOpacity: 0.5, 130 | draggable: true 131 | }); 132 | 133 | layer.add(line); 134 | stage.add(layer); 135 | 136 | 137 | var trace = layer.getContext().getTrace(); 138 | //console.log(trace); 139 | assert.equal(trace, 'clearRect(0,0,578,200);save();lineJoin=round;transform(1,0,0,1,0,0);save();globalAlpha=0.5;shadowColor=black;shadowBlur=20;shadowOffsetX=10;shadowOffsetY=10;beginPath();moveTo(73,160);lineTo(340,23);lineCap=round;lineWidth=20;strokeStyle=blue;stroke();restore();beginPath();moveTo(73,160);lineTo(340,23);lineCap=round;lineWidth=20;strokeStyle=blue;stroke();restore();'); 140 | 141 | }); 142 | }); -------------------------------------------------------------------------------- /src/filters/Enhance.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | function remap(fromValue, fromMin, fromMax, toMin, toMax) { 3 | // Compute the range of the data 4 | var fromRange = fromMax - fromMin, 5 | toRange = toMax - toMin, 6 | toValue; 7 | 8 | // If either range is 0, then the value can only be mapped to 1 value 9 | if (fromRange === 0) { 10 | return toMin + toRange / 2; 11 | } 12 | if (toRange === 0) { 13 | return toMin; 14 | } 15 | 16 | // (1) untranslate, (2) unscale, (3) rescale, (4) retranslate 17 | toValue = (fromValue - fromMin) / fromRange; 18 | toValue = (toRange * toValue) + toMin; 19 | 20 | return toValue; 21 | } 22 | 23 | 24 | /** 25 | * Enhance Filter. Adjusts the colors so that they span the widest 26 | * possible range (ie 0-255). Performs w*h pixel reads and w*h pixel 27 | * writes. 28 | * @function 29 | * @name Enhance 30 | * @memberof Kinetic.Filters 31 | * @param {Object} imageData 32 | * @author ippo615 33 | * @example 34 | * node.cache(); 35 | * node.filters([Kinetic.Filters.Enhance]); 36 | * node.enhance(0.4); 37 | */ 38 | Kinetic.Filters.Enhance = function (imageData) { 39 | var data = imageData.data, 40 | nSubPixels = data.length, 41 | rMin = data[0], rMax = rMin, r, 42 | gMin = data[1], gMax = gMin, g, 43 | bMin = data[2], bMax = bMin, b, 44 | i; 45 | 46 | // If we are not enhancing anything - don't do any computation 47 | var enhanceAmount = this.enhance(); 48 | if( enhanceAmount === 0 ){ return; } 49 | 50 | // 1st Pass - find the min and max for each channel: 51 | for (i = 0; i < nSubPixels; i += 4) { 52 | r = data[i + 0]; 53 | if (r < rMin) { rMin = r; } 54 | else if (r > rMax) { rMax = r; } 55 | g = data[i + 1]; 56 | if (g < gMin) { gMin = g; } else 57 | if (g > gMax) { gMax = g; } 58 | b = data[i + 2]; 59 | if (b < bMin) { bMin = b; } else 60 | if (b > bMax) { bMax = b; } 61 | //a = data[i + 3]; 62 | //if (a < aMin) { aMin = a; } else 63 | //if (a > aMax) { aMax = a; } 64 | } 65 | 66 | // If there is only 1 level - don't remap 67 | if( rMax === rMin ){ rMax = 255; rMin = 0; } 68 | if( gMax === gMin ){ gMax = 255; gMin = 0; } 69 | if( bMax === bMin ){ bMax = 255; bMin = 0; } 70 | 71 | var rMid, rGoalMax,rGoalMin, 72 | gMid, gGoalMax,gGoalMin, 73 | bMid, bGoalMax,bGoalMin; 74 | 75 | // If the enhancement is positive - stretch the histogram 76 | if ( enhanceAmount > 0 ){ 77 | rGoalMax = rMax + enhanceAmount*(255-rMax); 78 | rGoalMin = rMin - enhanceAmount*(rMin-0); 79 | gGoalMax = gMax + enhanceAmount*(255-gMax); 80 | gGoalMin = gMin - enhanceAmount*(gMin-0); 81 | bGoalMax = bMax + enhanceAmount*(255-bMax); 82 | bGoalMin = bMin - enhanceAmount*(bMin-0); 83 | // If the enhancement is negative - compress the histogram 84 | } else { 85 | rMid = (rMax + rMin)*0.5; 86 | rGoalMax = rMax + enhanceAmount*(rMax-rMid); 87 | rGoalMin = rMin + enhanceAmount*(rMin-rMid); 88 | gMid = (gMax + gMin)*0.5; 89 | gGoalMax = gMax + enhanceAmount*(gMax-gMid); 90 | gGoalMin = gMin + enhanceAmount*(gMin-gMid); 91 | bMid = (bMax + bMin)*0.5; 92 | bGoalMax = bMax + enhanceAmount*(bMax-bMid); 93 | bGoalMin = bMin + enhanceAmount*(bMin-bMid); 94 | } 95 | 96 | // Pass 2 - remap everything, except the alpha 97 | for (i = 0; i < nSubPixels; i += 4) { 98 | data[i + 0] = remap(data[i + 0], rMin, rMax, rGoalMin, rGoalMax); 99 | data[i + 1] = remap(data[i + 1], gMin, gMax, gGoalMin, gGoalMax); 100 | data[i + 2] = remap(data[i + 2], bMin, bMax, bGoalMin, bGoalMax); 101 | //data[i + 3] = remap(data[i + 3], aMin, aMax, aGoalMin, aGoalMax); 102 | } 103 | }; 104 | 105 | Kinetic.Factory.addGetterSetter(Kinetic.Node, 'enhance', 0, null, Kinetic.Factory.afterSetFilter); 106 | 107 | /** 108 | * get/set enhance. Use with {@link Kinetic.Filters.Enhance} filter. 109 | * @name enhance 110 | * @method 111 | * @memberof Kinetic.Node.prototype 112 | * @param {Float} amount 113 | * @returns {Float} 114 | */ 115 | })(); 116 | -------------------------------------------------------------------------------- /test/unit/filters/Brighten-test.js: -------------------------------------------------------------------------------- 1 | suite('Brighten', function() { 2 | // ====================================================== 3 | test('basic', function(done) { 4 | var stage = addStage(); 5 | 6 | var imageObj = new Image(); 7 | imageObj.onload = function() { 8 | 9 | var layer = new Kinetic.Layer(); 10 | darth = new Kinetic.Image({ 11 | x: 10, 12 | y: 10, 13 | image: imageObj, 14 | draggable: true 15 | }); 16 | 17 | layer.add(darth); 18 | stage.add(layer); 19 | 20 | darth.cache(); 21 | darth.filters([Kinetic.Filters.Brighten]); 22 | darth.brightness(0.3); 23 | layer.draw(); 24 | 25 | assert.equal(darth.brightness(), 0.3); 26 | 27 | done(); 28 | }; 29 | imageObj.src = 'assets/darth-vader.jpg'; 30 | 31 | }); 32 | 33 | // ====================================================== 34 | test('tween', function(done) { 35 | var stage = addStage(); 36 | 37 | var imageObj = new Image(); 38 | imageObj.onload = function() { 39 | 40 | var layer = new Kinetic.Layer(); 41 | darth = new Kinetic.Image({ 42 | x: 10, 43 | y: 10, 44 | image: imageObj, 45 | draggable: true 46 | }); 47 | 48 | layer.add(darth); 49 | stage.add(layer); 50 | 51 | darth.cache(); 52 | darth.filters([Kinetic.Filters.Brighten]); 53 | darth.brightness(0.3); 54 | layer.draw(); 55 | 56 | var tween = new Kinetic.Tween({ 57 | node: darth, 58 | duration: 2.0, 59 | brightness: 0, 60 | easing: Kinetic.Easings.EaseInOut 61 | }); 62 | 63 | darth.on('mouseover', function() { 64 | tween.play(); 65 | }); 66 | 67 | darth.on('mouseout', function() { 68 | tween.reverse(); 69 | }); 70 | 71 | done(); 72 | 73 | }; 74 | imageObj.src = 'assets/darth-vader.jpg'; 75 | }); 76 | 77 | // ====================================================== 78 | test('crop', function(done) { 79 | var stage = addStage(); 80 | 81 | var imageObj = new Image(); 82 | imageObj.onload = function() { 83 | 84 | var layer = new Kinetic.Layer(); 85 | darth = new Kinetic.Image({ 86 | x: 10, 87 | y: 10, 88 | image: imageObj, 89 | crop: {x:128, y:48, width:256, height:128}, 90 | draggable: true 91 | }); 92 | 93 | layer.add(darth); 94 | stage.add(layer); 95 | 96 | darth.cache(); 97 | darth.filters([Kinetic.Filters.Brighten]); 98 | darth.brightness(-0.3); 99 | layer.draw(); 100 | 101 | assert.equal(darth.brightness(), -0.3); 102 | 103 | done(); 104 | 105 | }; 106 | imageObj.src = 'assets/darth-vader.jpg'; 107 | }); 108 | 109 | // ====================================================== 110 | test('tween transparency', function(done) { 111 | var stage = addStage(); 112 | 113 | var imageObj = new Image(); 114 | imageObj.onload = function() { 115 | 116 | var layer = new Kinetic.Layer(); 117 | darth = new Kinetic.Image({ 118 | x: 10, 119 | y: 10, 120 | image: imageObj, 121 | draggable: true 122 | }); 123 | 124 | layer.add(darth); 125 | stage.add(layer); 126 | 127 | darth.cache(); 128 | darth.filters([Kinetic.Filters.Brighten]); 129 | darth.brightness(0.3); 130 | layer.draw(); 131 | 132 | var tween = new Kinetic.Tween({ 133 | node: darth, 134 | duration: 2.0, 135 | brightness: -0.3, 136 | easing: Kinetic.Easings.EaseInOut 137 | }); 138 | 139 | darth.on('mouseover', function() { 140 | tween.play(); 141 | }); 142 | 143 | darth.on('mouseout', function() { 144 | tween.reverse(); 145 | }); 146 | 147 | done(); 148 | 149 | }; 150 | imageObj.src = 'assets/lion.png'; 151 | }); 152 | 153 | 154 | }); -------------------------------------------------------------------------------- /doc-includes/ShapeParams.txt: -------------------------------------------------------------------------------- 1 | @param {String} [config.fill] fill color 2 | * @param {Integer} [config.fillRed] set fill red component 3 | * @param {Integer} [config.fillGreen] set fill green component 4 | * @param {Integer} [config.fillBlue] set fill blue component 5 | * @param {Integer} [config.fillAlpha] set fill alpha component 6 | * @param {Image} [config.fillPatternImage] fill pattern image 7 | * @param {Number} [config.fillPatternX] 8 | * @param {Number} [config.fillPatternY] 9 | * @param {Object} [config.fillPatternOffset] object with x and y component 10 | * @param {Number} [config.fillPatternOffsetX] 11 | * @param {Number} [config.fillPatternOffsetY] 12 | * @param {Object} [config.fillPatternScale] object with x and y component 13 | * @param {Number} [config.fillPatternScaleX] 14 | * @param {Number} [config.fillPatternScaleY] 15 | * @param {Number} [config.fillPatternRotation] 16 | * @param {String} [config.fillPatternRepeat] can be "repeat", "repeat-x", "repeat-y", or "no-repeat". The default is "no-repeat" 17 | * @param {Object} [config.fillLinearGradientStartPoint] object with x and y component 18 | * @param {Number} [config.fillLinearGradientStartPointX] 19 | * @param {Number} [config.fillLinearGradientStartPointY] 20 | * @param {Object} [config.fillLinearGradientEndPoint] object with x and y component 21 | * @param {Number} [config.fillLinearGradientEndPointX] 22 | * @param {Number} [config.fillLinearGradientEndPointY] 23 | * @param {Array} [config.fillLinearGradientColorStops] array of color stops 24 | * @param {Object} [config.fillRadialGradientStartPoint] object with x and y component 25 | * @param {Number} [config.fillRadialGradientStartPointX] 26 | * @param {Number} [config.fillRadialGradientStartPointY] 27 | * @param {Object} [config.fillRadialGradientEndPoint] object with x and y component 28 | * @param {Number} [config.fillRadialGradientEndPointX] 29 | * @param {Number} [config.fillRadialGradientEndPointY] 30 | * @param {Number} [config.fillRadialGradientStartRadius] 31 | * @param {Number} [config.fillRadialGradientEndRadius] 32 | * @param {Array} [config.fillRadialGradientColorStops] array of color stops 33 | * @param {Boolean} [config.fillEnabled] flag which enables or disables the fill. The default value is true 34 | * @param {String} [config.fillPriority] can be color, linear-gradient, radial-graident, or pattern. The default value is color. The fillPriority property makes it really easy to toggle between different fill types. For example, if you want to toggle between a fill color style and a fill pattern style, simply set the fill property and the fillPattern properties, and then use setFillPriority('color') to render the shape with a color fill, or use setFillPriority('pattern') to render the shape with the pattern fill configuration 35 | * @param {String} [config.stroke] stroke color 36 | * @param {Integer} [config.strokeRed] set stroke red component 37 | * @param {Integer} [config.strokeGreen] set stroke green component 38 | * @param {Integer} [config.strokeBlue] set stroke blue component 39 | * @param {Integer} [config.strokeAlpha] set stroke alpha component 40 | * @param {Number} [config.strokeWidth] stroke width 41 | * @param {Boolean} [config.strokeScaleEnabled] flag which enables or disables stroke scale. The default is true 42 | * @param {Boolean} [config.strokeEnabled] flag which enables or disables the stroke. The default value is true 43 | * @param {String} [config.lineJoin] can be miter, round, or bevel. The default 44 | * is miter 45 | * @param {String} [config.lineCap] can be butt, round, or sqare. The default 46 | * is butt 47 | * @param {String} [config.shadowColor] 48 | * @param {Integer} [config.shadowRed] set shadow color red component 49 | * @param {Integer} [config.shadowGreen] set shadow color green component 50 | * @param {Integer} [config.shadowBlue] set shadow color blue component 51 | * @param {Integer} [config.shadowAlpha] set shadow color alpha component 52 | * @param {Number} [config.shadowBlur] 53 | * @param {Object} [config.shadowOffset] object with x and y component 54 | * @param {Number} [config.shadowOffsetX] 55 | * @param {Number} [config.shadowOffsetY] 56 | * @param {Number} [config.shadowOpacity] shadow opacity. Can be any real number 57 | * between 0 and 1 58 | * @param {Boolean} [config.shadowEnabled] flag which enables or disables the shadow. The default value is true 59 | * @param {Array} [config.dash] 60 | * @param {Boolean} [config.dashEnabled] flag which enables or disables the dashArray. The default value is true -------------------------------------------------------------------------------- /src/Factory.js: -------------------------------------------------------------------------------- 1 | /*jshint unused:false */ 2 | (function() { 3 | // CONSTANTS 4 | var GET = 'get', 5 | RGB = 'RGB', 6 | SET = 'set'; 7 | 8 | Kinetic.Factory = { 9 | addGetterSetter: function(constructor, attr, def, validator, after) { 10 | this.addGetter(constructor, attr, def); 11 | this.addSetter(constructor, attr, validator, after); 12 | this.addOverloadedGetterSetter(constructor, attr); 13 | }, 14 | addGetter: function(constructor, attr, def) { 15 | var method = GET + Kinetic.Util._capitalize(attr); 16 | 17 | constructor.prototype[method] = function() { 18 | var val = this.attrs[attr]; 19 | return val === undefined ? def : val; 20 | }; 21 | }, 22 | addSetter: function(constructor, attr, validator, after) { 23 | var method = SET + Kinetic.Util._capitalize(attr); 24 | 25 | constructor.prototype[method] = function(val) { 26 | if (validator) { 27 | val = validator.call(this, val); 28 | } 29 | 30 | this._setAttr(attr, val); 31 | 32 | if (after) { 33 | after.call(this); 34 | } 35 | 36 | return this; 37 | }; 38 | }, 39 | addComponentsGetterSetter: function(constructor, attr, components, validator, after) { 40 | var len = components.length, 41 | capitalize = Kinetic.Util._capitalize, 42 | getter = GET + capitalize(attr), 43 | setter = SET + capitalize(attr), 44 | n, component; 45 | 46 | // getter 47 | constructor.prototype[getter] = function() { 48 | var ret = {}; 49 | 50 | for (n=0; n 255) { 117 | return 255; 118 | } else if (val < 0) { 119 | return 0; 120 | } else { 121 | return Math.round(val); 122 | } 123 | }, 124 | alphaComponent: function(val) { 125 | if (val > 1) { 126 | return 1; 127 | } 128 | // chrome does not honor alpha values of 0 129 | else if (val < 0.0001) { 130 | return 0.0001; 131 | } 132 | else { 133 | return val; 134 | } 135 | } 136 | }; 137 | })(); -------------------------------------------------------------------------------- /test/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | KineticJS Mocha Tests 5 | 6 | 25 | 26 | 27 |

KineticJS Test

28 |
29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /test/unit/shapes/Rect-test.js: -------------------------------------------------------------------------------- 1 | suite('Rect', function(){ 2 | 3 | // ====================================================== 4 | test('add rect to stage', function(){ 5 | var stage = addStage(); 6 | 7 | var layer = new Kinetic.Layer(); 8 | 9 | var rect = new Kinetic.Rect({ 10 | x: 100, 11 | y: 50, 12 | width: 100, 13 | height: 50, 14 | fill: 'green', 15 | stroke: 'blue' 16 | }); 17 | 18 | layer.add(rect); 19 | stage.add(layer); 20 | 21 | assert.equal(rect.getX(), 100); 22 | assert.equal(rect.getY(), 50); 23 | 24 | var trace = layer.getContext().getTrace(); 25 | //console.log(trace); 26 | assert.equal(trace, 'clearRect(0,0,578,200);save();transform(1,0,0,1,100,50);beginPath();rect(0,0,100,50);closePath();fillStyle=green;fill();lineWidth=2;strokeStyle=blue;stroke();restore();'); 27 | 28 | var relaxedTrace = layer.getContext().getTrace(true); 29 | //console.log(relaxedTrace); 30 | assert.equal(relaxedTrace, 'clearRect();save();transform();beginPath();rect();closePath();fillStyle;fill();lineWidth;strokeStyle;stroke();restore();'); 31 | }); 32 | 33 | // ====================================================== 34 | test('add rect with shadow, corner radius, and opacity', function(){ 35 | var stage = addStage(); 36 | 37 | var layer = new Kinetic.Layer(); 38 | 39 | var rect = new Kinetic.Rect({ 40 | x: 100, 41 | y: 50, 42 | width: 100, 43 | height: 50, 44 | fill: 'green', 45 | stroke: 'blue', 46 | shadowColor: 'red', 47 | shadowBlur: 10, 48 | shadowOffset: {x: 5, y: 5}, 49 | shadowOpacity: 0.5, 50 | opacity: 0.4, 51 | cornerRadius: 5 52 | }); 53 | 54 | layer.add(rect); 55 | stage.add(layer); 56 | 57 | assert.equal(rect.getShadowColor(), 'red'); 58 | assert.equal(rect.getShadowBlur(), 10); 59 | assert.equal(rect.getShadowOffsetX(), 5); 60 | assert.equal(rect.getShadowOffsetY(), 5); 61 | assert.equal(rect.getShadowOpacity(), 0.5); 62 | assert.equal(rect.getOpacity(), 0.4); 63 | assert.equal(rect.getCornerRadius(), 5); 64 | 65 | var trace = layer.getContext().getTrace(); 66 | //console.log(trace); 67 | assert.equal(trace, 'clearRect(0,0,578,200);save();save();globalAlpha=0.2;shadowColor=red;shadowBlur=10;shadowOffsetX=5;shadowOffsetY=5;drawImage([object HTMLCanvasElement],0,0);restore();globalAlpha=0.4;drawImage([object HTMLCanvasElement],0,0);restore();'); 68 | 69 | }); 70 | 71 | 72 | 73 | // ====================================================== 74 | test('draw rect', function() { 75 | var stage = addStage(); 76 | var layer = new Kinetic.Layer(); 77 | var rect = new Kinetic.Rect({ 78 | x: 200, 79 | y: 90, 80 | width: 100, 81 | height: 50, 82 | fill: 'green', 83 | stroke: 'black', 84 | strokeWidth: 4, 85 | center: {x: 50, y: 0}, 86 | scale: {x: 2, y: 2}, 87 | cornerRadius: 15, 88 | draggable: true 89 | }); 90 | 91 | layer.add(rect); 92 | stage.add(layer); 93 | 94 | assert.equal(rect.getClassName(), 'Rect'); 95 | }); 96 | 97 | // ====================================================== 98 | test('add fill stroke rect', function() { 99 | var stage = addStage(); 100 | var layer = new Kinetic.Layer(); 101 | var rect = new Kinetic.Rect({ 102 | x: 200, 103 | y: 100, 104 | width: 100, 105 | height: 50, 106 | fill: 'blue', 107 | stroke: 'green', 108 | strokeWidth: 4 109 | }); 110 | 111 | layer.add(rect); 112 | stage.add(layer); 113 | }); 114 | 115 | // ====================================================== 116 | test('add stroke rect', function() { 117 | var stage = addStage(); 118 | var layer = new Kinetic.Layer(); 119 | var rect = new Kinetic.Rect({ 120 | x: 200, 121 | y: 100, 122 | width: 100, 123 | height: 50, 124 | stroke: 'green', 125 | strokeWidth: 4 126 | }); 127 | 128 | layer.add(rect); 129 | stage.add(layer); 130 | }); 131 | 132 | // ====================================================== 133 | test('use default stroke (stroke color should be black)', function() { 134 | var stage = addStage(); 135 | var layer = new Kinetic.Layer(); 136 | var rect = new Kinetic.Rect({ 137 | x: 200, 138 | y: 100, 139 | width: 100, 140 | height: 50, 141 | strokeWidth: 4 142 | }); 143 | 144 | layer.add(rect); 145 | stage.add(layer); 146 | }); 147 | 148 | // ====================================================== 149 | test('use default stroke width (stroke width should be 2)', function() { 150 | var stage = addStage(); 151 | var layer = new Kinetic.Layer(); 152 | var rect = new Kinetic.Rect({ 153 | x: 200, 154 | y: 100, 155 | width: 100, 156 | height: 50, 157 | stroke: 'blue' 158 | }); 159 | 160 | layer.add(rect); 161 | stage.add(layer); 162 | }); 163 | 164 | }); -------------------------------------------------------------------------------- /test/unit/filters/HSV-test.js: -------------------------------------------------------------------------------- 1 | suite('HSV', function() { 2 | 3 | 4 | // ====================================================== 5 | test('hue shift tween transparancy', function(done) { 6 | var stage = addStage(); 7 | 8 | var imageObj = new Image(); 9 | imageObj.onload = function() { 10 | 11 | var layer = new Kinetic.Layer(); 12 | darth = new Kinetic.Image({ 13 | x: 10, 14 | y: 10, 15 | image: imageObj, 16 | draggable: true 17 | }); 18 | 19 | layer.add(darth); 20 | stage.add(layer); 21 | 22 | darth.cache(); 23 | darth.filters([Kinetic.Filters.HSV]); 24 | darth.hue(360); 25 | layer.draw(); 26 | 27 | var tween = new Kinetic.Tween({ 28 | node: darth, 29 | duration: 1.0, 30 | hue: 0, 31 | easing: Kinetic.Easings.EaseInOut 32 | }); 33 | 34 | darth.on('mouseover', function() { 35 | tween.play(); 36 | }); 37 | 38 | darth.on('mouseout', function() { 39 | tween.reverse(); 40 | }); 41 | 42 | done(); 43 | }; 44 | imageObj.src = 'assets/lion.png'; 45 | 46 | }); 47 | 48 | // ====================================================== 49 | test('saturate image', function(done) { 50 | var stage = addStage(); 51 | 52 | var imageObj = new Image(); 53 | imageObj.onload = function() { 54 | 55 | var layer = new Kinetic.Layer(); 56 | darth = new Kinetic.Image({ 57 | x: 10, 58 | y: 10, 59 | image: imageObj, 60 | draggable: true 61 | }); 62 | 63 | layer.add(darth); 64 | stage.add(layer); 65 | 66 | darth.cache(); 67 | darth.filters([Kinetic.Filters.HSV]); 68 | 69 | darth.saturation(1.0); 70 | layer.draw(); 71 | 72 | var tween = new Kinetic.Tween({ 73 | node: darth, 74 | duration: 1.0, 75 | saturation: -1.0, 76 | easing: Kinetic.Easings.EaseInOut 77 | }); 78 | 79 | darth.on('mouseover', function() { 80 | tween.play(); 81 | }); 82 | 83 | darth.on('mouseout', function() { 84 | tween.reverse(); 85 | }); 86 | 87 | done(); 88 | }; 89 | imageObj.src = 'assets/bamoon.jpg'; 90 | 91 | }); 92 | 93 | // ====================================================== 94 | test('saturation tween transparancy', function(done) { 95 | var stage = addStage(); 96 | 97 | var imageObj = new Image(); 98 | imageObj.onload = function() { 99 | 100 | var layer = new Kinetic.Layer(); 101 | darth = new Kinetic.Image({ 102 | x: 10, 103 | y: 10, 104 | image: imageObj, 105 | draggable: true 106 | }); 107 | 108 | layer.add(darth); 109 | stage.add(layer); 110 | 111 | darth.cache(); 112 | darth.filters([Kinetic.Filters.HSV]); 113 | darth.saturation(1.0); 114 | layer.draw(); 115 | 116 | var tween = new Kinetic.Tween({ 117 | node: darth, 118 | duration: 1.0, 119 | saturation: -1, 120 | easing: Kinetic.Easings.EaseInOut 121 | }); 122 | 123 | darth.on('mouseover', function() { 124 | tween.play(); 125 | }); 126 | 127 | darth.on('mouseout', function() { 128 | tween.reverse(); 129 | }); 130 | 131 | done(); 132 | }; 133 | imageObj.src = 'assets/lion.png'; 134 | 135 | }); 136 | 137 | // ====================================================== 138 | test('value tween transparancy', function(done) { 139 | var stage = addStage(); 140 | 141 | var imageObj = new Image(); 142 | imageObj.onload = function() { 143 | 144 | var layer = new Kinetic.Layer(); 145 | darth = new Kinetic.Image({ 146 | x: 10, 147 | y: 10, 148 | image: imageObj, 149 | draggable: true 150 | }); 151 | 152 | layer.add(darth); 153 | stage.add(layer); 154 | 155 | darth.cache(); 156 | darth.filters([Kinetic.Filters.HSV]); 157 | darth.value(1.0); 158 | layer.draw(); 159 | 160 | var tween = new Kinetic.Tween({ 161 | node: darth, 162 | duration: 1.0, 163 | value: -1.0, 164 | easing: Kinetic.Easings.EaseInOut 165 | }); 166 | 167 | darth.on('mouseover', function() { 168 | tween.play(); 169 | }); 170 | 171 | darth.on('mouseout', function() { 172 | tween.reverse(); 173 | }); 174 | 175 | done(); 176 | }; 177 | imageObj.src = 'assets/lion.png'; 178 | 179 | }); 180 | 181 | }); 182 | --------------------------------------------------------------------------------