├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE.md └── workflows │ ├── release.yml │ ├── test-browser.yml │ └── test-node.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── eslint.config.mjs ├── gulpfile.mjs ├── konva-node ├── demo.js ├── index.js └── package.json ├── package.json ├── release.sh ├── rename-imports.mjs ├── resources ├── doc-includes │ ├── ContainerParams.txt │ ├── NodeParams.txt │ └── ShapeParams.txt └── jsdoc.conf.json ├── rollup.config.mjs ├── src ├── Animation.ts ├── BezierFunctions.ts ├── Canvas.ts ├── Container.ts ├── Context.ts ├── Core.ts ├── DragAndDrop.ts ├── Factory.ts ├── FastLayer.ts ├── Global.ts ├── Group.ts ├── Layer.ts ├── Node.ts ├── PointerEvents.ts ├── Shape.ts ├── Stage.ts ├── Tween.ts ├── Util.ts ├── Validators.ts ├── _CoreInternals.ts ├── _FullInternals.ts ├── filters │ ├── Blur.ts │ ├── Brighten.ts │ ├── Contrast.ts │ ├── Emboss.ts │ ├── Enhance.ts │ ├── Grayscale.ts │ ├── HSL.ts │ ├── HSV.ts │ ├── Invert.ts │ ├── Kaleidoscope.ts │ ├── Mask.ts │ ├── Noise.ts │ ├── Pixelate.ts │ ├── Posterize.ts │ ├── RGB.ts │ ├── RGBA.ts │ ├── Sepia.ts │ ├── Solarize.ts │ └── Threshold.ts ├── index-node.ts ├── index-types.d.ts ├── index.ts ├── shapes │ ├── Arc.ts │ ├── Arrow.ts │ ├── Circle.ts │ ├── Ellipse.ts │ ├── Image.ts │ ├── Label.ts │ ├── Line.ts │ ├── Path.ts │ ├── Rect.ts │ ├── RegularPolygon.ts │ ├── Ring.ts │ ├── Sprite.ts │ ├── Star.ts │ ├── Text.ts │ ├── TextPath.ts │ ├── Transformer.ts │ └── Wedge.ts └── types.ts ├── test ├── assets │ ├── bunny.png │ ├── darth-vader.jpg │ ├── lion.png │ ├── scorpion-sprite.png │ ├── tiger.ts │ └── worldMap.ts ├── bunnies.html ├── ifame.html ├── import-test.cjs ├── import-test.mjs ├── manual-tests.html ├── manual │ ├── Blur-test.ts │ ├── Brighten-test.ts │ ├── Contrast-test.ts │ ├── Emboss-test.ts │ ├── Enhance-test.ts │ ├── Filter-test.ts │ ├── Grayscale-test.ts │ ├── HSL-test.ts │ ├── HSV-test.ts │ ├── Invert-test.ts │ ├── Kaleidoscope-test.ts │ ├── Manual-test.ts │ ├── Mask-test.ts │ ├── Noise-test.ts │ ├── Pixelate-test.ts │ ├── Posterize-test.ts │ ├── RGB-test.ts │ ├── RGBA-test.ts │ ├── Sepia-test.ts │ ├── Solarize-test.ts │ └── Threshold-test.ts ├── node-global-setup.mjs ├── package.json ├── performance │ ├── bunnies_native.html │ ├── creating_elements.html │ └── jump-shape.html ├── runner.js ├── sandbox.html ├── text-paths.html ├── tsconfig.json ├── unit-tests.html └── unit │ ├── Animation-test.ts │ ├── Arc-test.ts │ ├── Arrow-test.ts │ ├── AutoDraw-test.ts │ ├── Blob-test.ts │ ├── Canvas-test.ts │ ├── Circle-test.ts │ ├── Container-test.ts │ ├── Context-test.ts │ ├── DragAndDrop-test.ts │ ├── DragAndDropEvents-test.ts │ ├── Ellipse-test.ts │ ├── Global-test.ts │ ├── Group-test.ts │ ├── Image-test.ts │ ├── Label-test.ts │ ├── Layer-test.ts │ ├── Line-test.ts │ ├── MouseEvents-test.ts │ ├── Node-cache-test.ts │ ├── Node-test.ts │ ├── Path-test.ts │ ├── PointerEvents-test.ts │ ├── Polygon-test.ts │ ├── Rect-test.ts │ ├── RegularPolygon-test.ts │ ├── Ring-test.ts │ ├── Shape-test.ts │ ├── Spline-test.ts │ ├── Sprite-test.ts │ ├── Stage-test.ts │ ├── Star-test.ts │ ├── Text-test.ts │ ├── TextPath-test.ts │ ├── TouchEvents-test.ts │ ├── Transformer-test.ts │ ├── Tween-test.ts │ ├── Util-test.ts │ ├── Wedge-test.ts │ ├── imagediff.ts │ └── test-utils.ts ├── tsconfig.json └── tsconfig.testing.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [lavrton] 4 | patreon: lavrton 5 | open_collective: konva 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Thank you for submitting an issue! 2 | 3 | Please make sure to check current open and closed issues to see if your question has been asked or answered before. 4 | If you have just a question (not a bug or a feature request) it is better to ask it in [Stackoverflow](http://stackoverflow.com/questions/tagged/konvajs). 5 | 6 | If you have a bug, please, try to create a reproducible example with jsfiddle (or any similar service). 7 | You can use [this JSBIN](https://jsbin.com/necojavuma/edit?js,output) as a template. 8 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: 'tagged-release' 3 | 4 | on: 5 | push: 6 | tags: 7 | - '*' 8 | 9 | jobs: 10 | tagged-release: 11 | name: 'Tagged Release' 12 | runs-on: 'ubuntu-latest' 13 | 14 | steps: 15 | # ... 16 | - name: 'Build & test' 17 | run: | 18 | echo "done!" 19 | 20 | - uses: 'marvinpinto/action-automatic-releases@latest' 21 | with: 22 | repo_token: '${{ secrets.GITHUB_TOKEN }}' 23 | prerelease: false 24 | -------------------------------------------------------------------------------- /.github/workflows/test-browser.yml: -------------------------------------------------------------------------------- 1 | name: Test Browser 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [20.x] 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Use Node.js ${{ matrix.node-version }} 20 | uses: actions/setup-node@v1 21 | with: 22 | node-version: ${{ matrix.node-version }} 23 | - run: npm install 24 | - run: npm run test:browser 25 | -------------------------------------------------------------------------------- /.github/workflows/test-node.yml: -------------------------------------------------------------------------------- 1 | name: Test NodeJS 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [23.x] 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Use Node.js ${{ matrix.node-version }} 20 | uses: actions/setup-node@v1 21 | with: 22 | node-version: ${{ matrix.node-version }} 23 | - run: npm install 24 | - run: npm run test:node 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | es 3 | .parcel-cache 4 | test-build 5 | documentation 6 | analysis 7 | node_modules 8 | bower_components 9 | phantomjs.exe 10 | docs 11 | homedocs 12 | jsdoc-template 13 | api 14 | package-lock.json 15 | lib 16 | src_old 17 | *.zip 18 | *_cache 19 | types 20 | out.png 21 | cmj 22 | .test-temp 23 | 24 | # Numerous always-ignore extensions 25 | *.diff 26 | *.err 27 | *.orig 28 | *.log 29 | *.rej 30 | *.swo 31 | *.swp 32 | *.vi 33 | *~ 34 | *.sass-cache 35 | 36 | # OS or Editor folders 37 | .DS_Store 38 | Thumbs.db 39 | .cache 40 | .project 41 | .settings 42 | .tmproj 43 | *.esproj 44 | nbproject 45 | *.sublime-project 46 | *.sublime-workspace 47 | *.md.html 48 | .vscode 49 | 50 | # Dreamweaver added files 51 | _notes 52 | dwsync.xml 53 | 54 | # Komodo 55 | *.komodoproject 56 | .komodotools 57 | 58 | # Folders to ignore 59 | .hg 60 | .svn 61 | .CVS 62 | intermediate 63 | publish 64 | .idea 65 | 66 | konva.js 67 | konva.min.js -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Original work Copyright (C) 2011 - 2013 by Eric Rowell (KineticJS) 4 | Modified work Copyright (C) 2014 - present by Anton Lavrenov (Konva) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import tseslint from 'typescript-eslint'; 2 | 3 | export default tseslint.config(...tseslint.configs.recommended); 4 | -------------------------------------------------------------------------------- /gulpfile.mjs: -------------------------------------------------------------------------------- 1 | import gulp from 'gulp'; 2 | import rename from 'gulp-rename'; 3 | import uglify from 'gulp-uglify-es'; 4 | import replace from 'gulp-replace'; 5 | import jsdoc from 'gulp-jsdoc3'; 6 | import connect from 'gulp-connect'; 7 | import gutil from 'gulp-util'; 8 | 9 | import fs from 'fs'; 10 | var NodeParams = fs 11 | .readFileSync('./resources/doc-includes/NodeParams.txt') 12 | .toString(); 13 | var ContainerParams = fs 14 | .readFileSync('./resources/doc-includes/ContainerParams.txt') 15 | .toString(); 16 | var ShapeParams = fs 17 | .readFileSync('./resources/doc-includes/ShapeParams.txt') 18 | .toString(); 19 | 20 | const conf = JSON.parse(fs.readFileSync('./package.json')); 21 | 22 | function build() { 23 | return gulp 24 | .src(['./konva.js']) 25 | .pipe(replace('@@shapeParams', ShapeParams)) 26 | .pipe(replace('@@nodeParams', NodeParams)) 27 | .pipe(replace('@@containerParams', ContainerParams)) 28 | .pipe(replace('@@version', conf.version)) 29 | .pipe(replace('@@date', new Date().toDateString())); 30 | } 31 | 32 | gulp.task('update-version-lib', function () { 33 | return gulp 34 | .src(['./lib/Global.js']) 35 | .pipe(replace('@@version', conf.version)) 36 | .pipe(rename('Global.js')) 37 | .pipe(gulp.dest('./lib')); 38 | }); 39 | 40 | gulp.task('update-version-cmj', function () { 41 | return gulp 42 | .src(['./cmj/Global.js']) 43 | .pipe(replace('@@version', conf.version)) 44 | .pipe(rename('Global.js')) 45 | .pipe(gulp.dest('./cmj')); 46 | }); 47 | 48 | gulp.task('update-version-es-to-cmj-index', function () { 49 | return gulp 50 | .src(['./lib/index.js']) 51 | .pipe( 52 | replace(`import { Konva } from './_F`, `import { Konva } from '../cmj/_F`) 53 | ) 54 | .pipe(rename('index.js')) 55 | .pipe(gulp.dest('./lib')); 56 | }); 57 | 58 | gulp.task('update-version-es-to-cmj-node', function () { 59 | return gulp 60 | .src(['./lib/index-node.js']) 61 | .pipe( 62 | replace(`import { Konva } from './_F`, `import { Konva } from '../cmj/_F`) 63 | ) 64 | .pipe(rename('index-node.js')) 65 | .pipe(gulp.dest('./lib')); 66 | }); 67 | 68 | // create usual build konva.js and konva.min.js 69 | gulp.task('pre-build', function () { 70 | return build() 71 | .pipe(rename('konva.js')) 72 | .pipe(gulp.dest('./')) 73 | .pipe( 74 | uglify.default({ output: { comments: /^!|@preserve|@license|@cc_on/i } }) 75 | ) 76 | .on('error', function (err) { 77 | gutil.log(gutil.colors.red('[Error]'), err.toString()); 78 | }) 79 | .pipe(rename('konva.min.js')) 80 | .pipe(gulp.dest('./')); 81 | }); 82 | 83 | gulp.task( 84 | 'build', 85 | gulp.parallel([ 86 | 'update-version-lib', 87 | // 'update-version-cmj', 88 | // 'update-version-es-to-cmj-index', 89 | // 'update-version-es-to-cmj-node', 90 | 'pre-build', 91 | ]) 92 | ); 93 | 94 | // local server for better development 95 | gulp.task('server', function () { 96 | connect.server(); 97 | }); 98 | 99 | // // generate documentation 100 | gulp.task('api', function () { 101 | return gulp.src('./konva.js').pipe( 102 | jsdoc({ 103 | opts: { 104 | destination: './api', 105 | }, 106 | }) 107 | ); 108 | }); 109 | 110 | gulp.task('default', gulp.parallel(['server'])); 111 | -------------------------------------------------------------------------------- /konva-node/demo.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | 3 | // relative path here 4 | // but you will need just require('konva-node'); 5 | import Konva from '../'; 6 | 7 | // Create stage. Container parameter is not required in NodeJS. 8 | var stage = new Konva.Stage({ 9 | width: 100, 10 | height: 100, 11 | }); 12 | 13 | var layer = new Konva.Layer(); 14 | stage.add(layer); 15 | 16 | var rect = new Konva.Rect({ 17 | width: 100, 18 | height: 100, 19 | x: 50, 20 | y: 50, 21 | fill: 'white', 22 | }); 23 | var text = new Konva.Text({ 24 | text: 'Generated inside node js', 25 | x: 20, 26 | y: 20, 27 | fill: 'black', 28 | }); 29 | layer.add(rect).add(text); 30 | layer.draw(); 31 | stage.setSize({ 32 | width: 200, 33 | height: 200, 34 | }); 35 | 36 | // check tween works 37 | var tween = new Konva.Tween({ 38 | node: rect, 39 | duration: 1, 40 | x: -50, 41 | }); 42 | tween.play(); 43 | 44 | // After tween we want to convert stage to dataURL 45 | setTimeout(function () { 46 | stage.toDataURL({ 47 | callback: function (data) { 48 | // Then add result to stage 49 | var img = new Konva.window.Image(); 50 | img.onload = function () { 51 | var image = new Konva.Image({ 52 | image: img, 53 | x: 10, 54 | y: 50, 55 | }); 56 | layer.add(image); 57 | layer.draw(); 58 | // save stage image as file 59 | stage.toDataURL({ 60 | callback: function (data) { 61 | var base64Data = data.replace(/^data:image\/png;base64,/, ''); 62 | fs.writeFile('./out.png', base64Data, 'base64', function (err) { 63 | err && console.log(err); 64 | console.log('See out.png'); 65 | }); 66 | // now try to create image from url 67 | Konva.Image.fromURL(data, () => { 68 | console.log('image loaded'); 69 | // shoul'd throw 70 | }); 71 | }, 72 | }); 73 | }; 74 | img.src = data; 75 | }, 76 | }); 77 | }, 1050); 78 | -------------------------------------------------------------------------------- /konva-node/index.js: -------------------------------------------------------------------------------- 1 | var Konva = require('konva'); 2 | var canvas = require('canvas'); 3 | 4 | // mock window 5 | Konva.window = { 6 | Image: canvas.Image, 7 | devicePixelRatio: 1, 8 | }; 9 | // mock document 10 | Konva.document = { 11 | createElement: function () {}, 12 | documentElement: { 13 | addEventListener: function () {}, 14 | }, 15 | }; 16 | 17 | // make some global injections 18 | global.requestAnimationFrame = (cb) => { 19 | setImmediate(cb); 20 | }; 21 | 22 | // create canvas in Node env 23 | Konva.Util.createCanvasElement = () => { 24 | const node = new canvas.Canvas(); 25 | node.style = {}; 26 | return node; 27 | }; 28 | 29 | // create image in Node env 30 | Konva.Util.createImageElement = () => { 31 | const node = new canvas.Image(); 32 | node.style = {}; 33 | return node; 34 | }; 35 | 36 | // _checkVisibility use dom element, in node we can skip it 37 | Konva.Stage.prototype._checkVisibility = () => {}; 38 | 39 | module.exports = Konva; 40 | -------------------------------------------------------------------------------- /konva-node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "konva-node", 3 | "version": "0.11.2", 4 | "description": "Konva framework for NodeJS env", 5 | "main": "index.js", 6 | "files": [ 7 | "index.js" 8 | ], 9 | "type": "module", 10 | "typings": "./node_modules/konva/konva.d.ts", 11 | "scripts": {}, 12 | "keywords": [ 13 | "canvas", 14 | "animations", 15 | "graphic", 16 | "html5" 17 | ], 18 | "author": "Anton Lavrenov", 19 | "bugs": { 20 | "url": "https://github.com/konvajs/konva/issues" 21 | }, 22 | "homepage": "http://konvajs.org/", 23 | "repository": { 24 | "type": "git", 25 | "url": "git://github.com/konvajs/konva.git" 26 | }, 27 | "license": "MIT", 28 | "dependencies": { 29 | "canvas": "^2.5.0", 30 | "konva": "^7" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "konva", 3 | "version": "9.3.20", 4 | "description": "HTML5 2d canvas library.", 5 | "author": "Anton Lavrenov", 6 | "files": [ 7 | "README.md", 8 | "konva.js", 9 | "konva.min.js", 10 | "lib", 11 | "cmj" 12 | ], 13 | "main": "./lib/index-node.js", 14 | "browser": "./lib/index.js", 15 | "typings": "./lib/index-types.d.ts", 16 | "scripts": { 17 | "start": "npm run test:watch", 18 | "compile": "npm run clean && npm run tsc && cp ./src/index-types.d.ts ./lib/index-types.d.ts && npm run rollup", 19 | "build": "npm run compile && cp ./src/index-types.d.ts ./lib && gulp build && node ./rename-imports.mjs", 20 | "test:import": "npm run build && node ./test/import-test.cjs && node ./test/import-test.mjs", 21 | "test": "npm run test:browser && npm run test:node", 22 | "test:build": "PARCEL_WORKER_BACKEND=process parcel build ./test/unit-tests.html --dist-dir ./test-build --target none --public-url ./ --no-source-maps", 23 | "test:browser": "npm run test:build && mocha-headless-chrome -f ./test-build/unit-tests.html -a disable-web-security -a no-sandbox -a disable-setuid-sandbox", 24 | "test:watch": "rm -rf ./.parcel-cache && PARCEL_WORKERS=0 parcel serve ./test/unit-tests.html ./test/manual-tests.html ./test/sandbox.html ./test/text-paths.html ./test/bunnies.html", 25 | "test:node:compiled": "rm -rf ./.test-temp && mkdir ./.test-temp && (tsc -p ./test/tsconfig.json --outDir ./.test-temp || true) && mocha './.test-temp/test/unit/**/*.js' -r ./test/node-global-setup.mjs --exit && rm -rf ./.test-temp && npm run test:import", 26 | "test:node": "npm run test:node:compiled", 27 | "tsc": "tsc --removeComments", 28 | "rollup": "rollup -c --bundleConfigAsCjs", 29 | "clean": "rm -rf ./lib && rm -rf ./types && rm -rf ./cmj && rm -rf ./test-build", 30 | "watch": "rollup -c -w", 31 | "size": "size-limit" 32 | }, 33 | "targets": { 34 | "none": {} 35 | }, 36 | "funding": [ 37 | { 38 | "type": "patreon", 39 | "url": "https://www.patreon.com/lavrton" 40 | }, 41 | { 42 | "type": "opencollective", 43 | "url": "https://opencollective.com/konva" 44 | }, 45 | { 46 | "type": "github", 47 | "url": "https://github.com/sponsors/lavrton" 48 | } 49 | ], 50 | "size-limit": [ 51 | { 52 | "limit": "45 KB", 53 | "path": "./lib/index.js" 54 | }, 55 | { 56 | "limit": "26 KB", 57 | "path": "./lib/Core.js" 58 | }, 59 | { 60 | "path": "./konva.min.js" 61 | } 62 | ], 63 | "devDependencies": { 64 | "@parcel/transformer-image": "2.13.2", 65 | "@size-limit/preset-big-lib": "^11.1.6", 66 | "@types/mocha": "^10.0.10", 67 | "canvas": "^3.1.0", 68 | "chai": "5.1.2", 69 | "filehound": "^1.17.6", 70 | "gulp": "^5.0.0", 71 | "gulp-concat": "^2.6.1", 72 | "gulp-connect": "^5.7.0", 73 | "gulp-exec": "^5.0.0", 74 | "gulp-jsdoc3": "^3.0.0", 75 | "gulp-rename": "^2.0.0", 76 | "gulp-replace": "^1.1.4", 77 | "gulp-typescript": "^5.0.1", 78 | "gulp-uglify": "^3.0.2", 79 | "gulp-uglify-es": "^3.0.0", 80 | "gulp-util": "^3.0.8", 81 | "mocha": "10.2.0", 82 | "mocha-headless-chrome": "^4.0.0", 83 | "parcel": "2.13.3", 84 | "process": "^0.11.10", 85 | "rollup": "^4.31.0", 86 | "rollup-plugin-typescript2": "^0.36.0", 87 | "size-limit": "^11.1.6", 88 | "ts-mocha": "^10.0.0", 89 | "ts-node": "^10.9.2", 90 | "typescript": "^5.7.3" 91 | }, 92 | "keywords": [ 93 | "canvas", 94 | "animations", 95 | "graphic", 96 | "html5" 97 | ], 98 | "prettier": { 99 | "singleQuote": true 100 | }, 101 | "bugs": { 102 | "url": "https://github.com/konvajs/konva/issues" 103 | }, 104 | "homepage": "http://konvajs.org/", 105 | "readmeFilename": "README.md", 106 | "repository": { 107 | "type": "git", 108 | "url": "git://github.com/konvajs/konva.git" 109 | }, 110 | "license": "MIT" 111 | } 112 | -------------------------------------------------------------------------------- /release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | old_version="$(git describe --abbrev=0 --tags)" 5 | new_version=$1 6 | 7 | 8 | old_cdn="https://unpkg.com/konva@${old_version}/konva.js" 9 | new_cdn="https://unpkg.com/konva@${new_version}/konva.js" 10 | 11 | old_cdn_min="https://unpkg.com/konva@${old_version}/konva.min.js" 12 | new_cdn_min="https://unpkg.com/konva@${new_version}/konva.min.js" 13 | 14 | # make sure new version parameter is passed 15 | if [ -z "$1" ] 16 | then 17 | echo "ERROR - pass new version. usage release.sh 0.11.0" 18 | exit 2 19 | fi 20 | 21 | # make sure changle log updated 22 | while true; do 23 | read -p "Did you update CHANGELOG.md? " yn 24 | case $yn in 25 | [Yy]* ) break;; 26 | [Nn]* ) exit;; 27 | * ) echo "Please answer yes or no.";; 28 | esac 29 | done 30 | 31 | echo "Old version: ${old_version}" 32 | echo "New version: ${new_version}" 33 | 34 | echo "Pulling" 35 | git pull >/dev/null 36 | 37 | echo "build and test" 38 | npm run build >/dev/null 39 | # npm run test 40 | 41 | 42 | echo "commit change log updates" 43 | git commit -am "update CHANGELOG with new version" --allow-empty >/dev/null 44 | 45 | echo "npm version $1 --no-git-tag-version" 46 | npm version $1 --no-git-tag-version --allow-same-version >/dev/null 47 | 48 | echo "build for $1" 49 | npm run build >/dev/null 50 | git commit -am "build for $1" --allow-empty >/dev/null 51 | 52 | echo "update CDN link in README" 53 | perl -i -pe "s|${old_cdn_min}|${new_cdn_min}|g" ./README.md >/dev/null 54 | git commit -am "update cdn link" --allow-empty >/dev/null 55 | 56 | echo "create new git tag" 57 | git tag $1 >/dev/null 58 | 59 | cd ../konva 60 | git push >/dev/null 61 | git push --tags >/dev/null 62 | npm publish 63 | 64 | # echo "copy konva.js into konva-site" 65 | # cp ./konva.js ../konva-site/ 66 | # cd ../konva-site 67 | 68 | # echo "replace CDN links" 69 | 70 | 71 | # find source themes/hexo3/layout react-demos vue-demos main-demo -name "*.json" -exec perl -i -pe "s|${old_version}|${new_version}|g" {} + >/dev/null 72 | # find source themes/hexo3/layout react-demos vue-demos main-demo -name "*.html" -exec perl -i -pe "s|${old_version}|${new_version}|g" {} + >/dev/null 73 | 74 | # echo "regenerate site" 75 | # ./deploy.sh >/dev/null 76 | 77 | echo "DONE!" 78 | -------------------------------------------------------------------------------- /rename-imports.mjs: -------------------------------------------------------------------------------- 1 | import FileHound from 'filehound'; 2 | import fs from 'fs'; 3 | 4 | const files = FileHound.create().paths('./lib').ext(['js', 'ts']).find(); 5 | 6 | files.then((filePaths) => { 7 | filePaths.forEach((filepath) => { 8 | fs.readFile(filepath, 'utf8', (err, text) => { 9 | if (!text.match(/import .* from/g)) { 10 | return; 11 | } 12 | text = text.replace(/(import .* from\s+['"])(.*)(?=['"])/g, '$1$2.js'); 13 | if (text.match(/export .* from/g)) { 14 | text = text.replace(/(export .* from\s+['"])(.*)(?=['"])/g, '$1$2.js'); 15 | } 16 | 17 | if (err) throw err; 18 | 19 | // stupid replacement back 20 | text = text.replace( 21 | "import * as Canvas from 'canvas.js';", 22 | "import * as Canvas from 'canvas';" 23 | ); 24 | 25 | // Handle import("./x/y/z") syntax. 26 | text = text.replace(/(import\s*\(\s*['"])(.*)(?=['"])/g, '$1$2.js'); 27 | 28 | fs.writeFile(filepath, text, function (err) { 29 | if (err) { 30 | throw err; 31 | } 32 | }); 33 | }); 34 | }); 35 | }); 36 | 37 | const indexFiles = ['lib/index.js', 'lib/index-node.js', 'lib/Core.js']; 38 | indexFiles.forEach((filepath) => { 39 | fs.readFile(filepath, 'utf8', (err, text) => { 40 | text = text.replace('exports.default =', 'module.exports ='); 41 | fs.writeFile(filepath, text, function (err) { 42 | if (err) { 43 | throw err; 44 | } 45 | }); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /resources/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 | * @param {Function} [config.clipFunc] set clip func 7 | -------------------------------------------------------------------------------- /resources/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] -------------------------------------------------------------------------------- /resources/doc-includes/ShapeParams.txt: -------------------------------------------------------------------------------- 1 | @param {String} [config.fill] fill color 2 | * @param {Image} [config.fillPatternImage] fill pattern image 3 | * @param {Number} [config.fillPatternX] 4 | * @param {Number} [config.fillPatternY] 5 | * @param {Object} [config.fillPatternOffset] object with x and y component 6 | * @param {Number} [config.fillPatternOffsetX] 7 | * @param {Number} [config.fillPatternOffsetY] 8 | * @param {Object} [config.fillPatternScale] object with x and y component 9 | * @param {Number} [config.fillPatternScaleX] 10 | * @param {Number} [config.fillPatternScaleY] 11 | * @param {Number} [config.fillPatternRotation] 12 | * @param {String} [config.fillPatternRepeat] can be "repeat", "repeat-x", "repeat-y", or "no-repeat". The default is "no-repeat" 13 | * @param {Object} [config.fillLinearGradientStartPoint] object with x and y component 14 | * @param {Number} [config.fillLinearGradientStartPointX] 15 | * @param {Number} [config.fillLinearGradientStartPointY] 16 | * @param {Object} [config.fillLinearGradientEndPoint] object with x and y component 17 | * @param {Number} [config.fillLinearGradientEndPointX] 18 | * @param {Number} [config.fillLinearGradientEndPointY] 19 | * @param {Array} [config.fillLinearGradientColorStops] array of color stops 20 | * @param {Object} [config.fillRadialGradientStartPoint] object with x and y component 21 | * @param {Number} [config.fillRadialGradientStartPointX] 22 | * @param {Number} [config.fillRadialGradientStartPointY] 23 | * @param {Object} [config.fillRadialGradientEndPoint] object with x and y component 24 | * @param {Number} [config.fillRadialGradientEndPointX] 25 | * @param {Number} [config.fillRadialGradientEndPointY] 26 | * @param {Number} [config.fillRadialGradientStartRadius] 27 | * @param {Number} [config.fillRadialGradientEndRadius] 28 | * @param {Array} [config.fillRadialGradientColorStops] array of color stops 29 | * @param {Boolean} [config.fillEnabled] flag which enables or disables the fill. The default value is true 30 | * @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 31 | * @param {String} [config.stroke] stroke color 32 | * @param {Number} [config.strokeWidth] stroke width 33 | * @param {Boolean} [config.fillAfterStrokeEnabled]. Should we draw fill AFTER stroke? Default is false. 34 | * @param {Number} [config.hitStrokeWidth] size of the stroke on hit canvas. The default is "auto" - equals to strokeWidth 35 | * @param {Boolean} [config.strokeHitEnabled] flag which enables or disables stroke hit region. The default is true 36 | * @param {Boolean} [config.perfectDrawEnabled] flag which enables or disables using buffer canvas. The default is true 37 | * @param {Boolean} [config.shadowForStrokeEnabled] flag which enables or disables shadow for stroke. The default is true 38 | * @param {Boolean} [config.strokeScaleEnabled] flag which enables or disables stroke scale. The default is true 39 | * @param {Boolean} [config.strokeEnabled] flag which enables or disables the stroke. The default value is true 40 | * @param {String} [config.lineJoin] can be miter, round, or bevel. The default 41 | * is miter 42 | * @param {String} [config.lineCap] can be butt, round, or square. The default 43 | * is butt 44 | * @param {String} [config.shadowColor] 45 | * @param {Number} [config.shadowBlur] 46 | * @param {Object} [config.shadowOffset] object with x and y component 47 | * @param {Number} [config.shadowOffsetX] 48 | * @param {Number} [config.shadowOffsetY] 49 | * @param {Number} [config.shadowOpacity] shadow opacity. Can be any real number 50 | * between 0 and 1 51 | * @param {Boolean} [config.shadowEnabled] flag which enables or disables the shadow. The default value is true 52 | * @param {Array} [config.dash] 53 | * @param {Boolean} [config.dashEnabled] flag which enables or disables the dashArray. The default value is true 54 | -------------------------------------------------------------------------------- /resources/jsdoc.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "path" : "ink-docstrap", 3 | "tags" : { 4 | "allowUnknownTags" : true 5 | }, 6 | "plugins" : ["plugins/markdown"], 7 | 8 | "templates" : { 9 | "cleverLinks" : false, 10 | "monospaceLinks" : false, 11 | "dateFormat" : "ddd MMM Do YYYY", 12 | "outputSourceFiles" : true, 13 | "outputSourcePath" : true, 14 | "systemName" : "Konva", 15 | "footer" : "", 16 | "copyright" : "Konva Copyright © 2015 The contributors to the Konva project.", 17 | "navType" : "vertical", 18 | "theme" : "cosmo", 19 | "linenums" : true, 20 | "collapseSymbols" : false, 21 | "inverseNav" : true, 22 | "highlightTutorialCode" : true 23 | }, 24 | "markdown" : { 25 | "parser" : "gfm", 26 | "hardwrap" : true 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /rollup.config.mjs: -------------------------------------------------------------------------------- 1 | // import resolve from 'rollup-plugin-node-resolve'; 2 | import typescript from 'rollup-plugin-typescript2'; 3 | 4 | export default { 5 | input: `src/index.ts`, 6 | output: [ 7 | { 8 | file: 'konva.js', 9 | name: 'Konva', 10 | format: 'umd', 11 | sourcemap: false, 12 | freeze: false, 13 | }, 14 | // { file: pkg.module, format: 'es', sourcemap: true } 15 | ], 16 | // Indicate here external modules you don't wanna include in your bundle (i.e.: 'lodash') 17 | external: [], 18 | watch: { 19 | include: 'src/**', 20 | }, 21 | plugins: [ 22 | // Allow json resolution 23 | // json(), 24 | // Compile TypeScript files 25 | typescript({ 26 | useTsconfigDeclarationDir: true, 27 | abortOnError: false, 28 | removeComments: false, 29 | tsconfigOverride: { 30 | compilerOptions: { 31 | module: 'ES2020', 32 | }, 33 | }, 34 | }), 35 | // // Allow bundling cjs modules (unlike webpack, rollup doesn't understand cjs) 36 | // commonjs(), 37 | // // Allow node_modules resolution, so you can use 'external' to control 38 | // // which external modules to include in the bundle 39 | // // https://github.com/rollup/rollup-plugin-node-resolve#usage 40 | // resolve(), 41 | 42 | // Resolve source maps to the original source 43 | // sourceMaps() 44 | ], 45 | }; 46 | -------------------------------------------------------------------------------- /src/Core.ts: -------------------------------------------------------------------------------- 1 | // enter file of limited Konva version with only core functions 2 | export { Konva } from './_CoreInternals'; 3 | import { Konva } from './_CoreInternals'; 4 | 5 | export default Konva; 6 | -------------------------------------------------------------------------------- /src/DragAndDrop.ts: -------------------------------------------------------------------------------- 1 | import { Container } from './Container'; 2 | import { Konva } from './Global'; 3 | import { Node } from './Node'; 4 | import { Vector2d } from './types'; 5 | import { Util } from './Util'; 6 | 7 | export const DD = { 8 | get isDragging() { 9 | let flag = false; 10 | DD._dragElements.forEach((elem) => { 11 | if (elem.dragStatus === 'dragging') { 12 | flag = true; 13 | } 14 | }); 15 | return flag; 16 | }, 17 | justDragged: false, 18 | get node() { 19 | // return first dragging node 20 | let node: Node | undefined; 21 | DD._dragElements.forEach((elem) => { 22 | node = elem.node; 23 | }); 24 | return node; 25 | }, 26 | _dragElements: new Map< 27 | number, 28 | { 29 | node: Node; 30 | startPointerPos: Vector2d; 31 | offset: Vector2d; 32 | pointerId?: number; 33 | // when we just put pointer down on a node 34 | // it will create drag element 35 | dragStatus: 'ready' | 'dragging' | 'stopped'; 36 | // dragStarted: boolean; 37 | // isDragging: boolean; 38 | // dragStopped: boolean; 39 | } 40 | >(), 41 | 42 | // methods 43 | _drag(evt) { 44 | const nodesToFireEvents: Array = []; 45 | DD._dragElements.forEach((elem, key) => { 46 | const { node } = elem; 47 | // we need to find pointer relative to that node 48 | const stage = node.getStage()!; 49 | stage.setPointersPositions(evt); 50 | 51 | // it is possible that user call startDrag without any event 52 | // it that case we need to detect first movable pointer and attach it into the node 53 | if (elem.pointerId === undefined) { 54 | elem.pointerId = Util._getFirstPointerId(evt); 55 | } 56 | const pos = stage._changedPointerPositions.find( 57 | (pos) => pos.id === elem.pointerId 58 | ); 59 | 60 | // not related pointer 61 | if (!pos) { 62 | return; 63 | } 64 | if (elem.dragStatus !== 'dragging') { 65 | const dragDistance = node.dragDistance(); 66 | const distance = Math.max( 67 | Math.abs(pos.x - elem.startPointerPos.x), 68 | Math.abs(pos.y - elem.startPointerPos.y) 69 | ); 70 | if (distance < dragDistance) { 71 | return; 72 | } 73 | node.startDrag({ evt }); 74 | // a user can stop dragging inside `dragstart` 75 | if (!node.isDragging()) { 76 | return; 77 | } 78 | } 79 | node._setDragPosition(evt, elem); 80 | nodesToFireEvents.push(node); 81 | }); 82 | // call dragmove only after ALL positions are changed 83 | nodesToFireEvents.forEach((node) => { 84 | node.fire( 85 | 'dragmove', 86 | { 87 | type: 'dragmove', 88 | target: node, 89 | evt: evt, 90 | }, 91 | true 92 | ); 93 | }); 94 | }, 95 | 96 | // dragBefore and dragAfter allows us to set correct order of events 97 | // setup all in dragbefore, and stop dragging only after pointerup triggered. 98 | _endDragBefore(evt?) { 99 | const drawNodes: Array = []; 100 | DD._dragElements.forEach((elem) => { 101 | const { node } = elem; 102 | // we need to find pointer relative to that node 103 | const stage = node.getStage()!; 104 | if (evt) { 105 | stage.setPointersPositions(evt); 106 | } 107 | 108 | const pos = stage._changedPointerPositions.find( 109 | (pos) => pos.id === elem.pointerId 110 | ); 111 | 112 | // that pointer is not related 113 | if (!pos) { 114 | return; 115 | } 116 | 117 | if (elem.dragStatus === 'dragging' || elem.dragStatus === 'stopped') { 118 | // if a node is stopped manually we still need to reset events: 119 | DD.justDragged = true; 120 | Konva._mouseListenClick = false; 121 | Konva._touchListenClick = false; 122 | Konva._pointerListenClick = false; 123 | elem.dragStatus = 'stopped'; 124 | } 125 | 126 | const drawNode = 127 | elem.node.getLayer() || 128 | ((elem.node instanceof Konva['Stage'] && elem.node) as any); 129 | 130 | if (drawNode && drawNodes.indexOf(drawNode) === -1) { 131 | drawNodes.push(drawNode); 132 | } 133 | }); 134 | // draw in a sync way 135 | // because mousemove event may trigger BEFORE batch draw is called 136 | // but as we have not hit canvas updated yet, it will trigger incorrect mouseover/mouseout events 137 | drawNodes.forEach((drawNode) => { 138 | drawNode.draw(); 139 | }); 140 | }, 141 | _endDragAfter(evt) { 142 | DD._dragElements.forEach((elem, key) => { 143 | if (elem.dragStatus === 'stopped') { 144 | elem.node.fire( 145 | 'dragend', 146 | { 147 | type: 'dragend', 148 | target: elem.node, 149 | evt: evt, 150 | }, 151 | true 152 | ); 153 | } 154 | if (elem.dragStatus !== 'dragging') { 155 | DD._dragElements.delete(key); 156 | } 157 | }); 158 | }, 159 | }; 160 | 161 | if (Konva.isBrowser) { 162 | window.addEventListener('mouseup', DD._endDragBefore, true); 163 | window.addEventListener('touchend', DD._endDragBefore, true); 164 | // add touchcancel to fix this: https://github.com/konvajs/konva/issues/1843 165 | window.addEventListener('touchcancel', DD._endDragBefore, true); 166 | 167 | window.addEventListener('mousemove', DD._drag); 168 | window.addEventListener('touchmove', DD._drag); 169 | 170 | window.addEventListener('mouseup', DD._endDragAfter, false); 171 | window.addEventListener('touchend', DD._endDragAfter, false); 172 | window.addEventListener('touchcancel', DD._endDragAfter, false); 173 | } 174 | -------------------------------------------------------------------------------- /src/FastLayer.ts: -------------------------------------------------------------------------------- 1 | import { Util } from './Util'; 2 | import { Layer } from './Layer'; 3 | import { _registerNode } from './Global'; 4 | 5 | /** 6 | * FastLayer constructor. **DEPRECATED!** Please use `Konva.Layer({ listening: false})` instead. Layers are tied to their own canvas element and are used 7 | * to contain shapes only. If you don't need node nesting, mouse and touch interactions, 8 | * or event pub/sub, you should use FastLayer instead of Layer to create your layers. 9 | * It renders about 2x faster than normal layers. 10 | * 11 | * @constructor 12 | * @memberof Konva 13 | * @augments Konva.Layer 14 | @@containerParams 15 | * @example 16 | * var layer = new Konva.FastLayer(); 17 | */ 18 | export class FastLayer extends Layer { 19 | constructor(attrs) { 20 | super(attrs); 21 | this.listening(false); 22 | Util.warn( 23 | 'Konva.Fast layer is deprecated. Please use "new Konva.Layer({ listening: false })" instead.' 24 | ); 25 | } 26 | } 27 | 28 | FastLayer.prototype.nodeType = 'FastLayer'; 29 | _registerNode(FastLayer); 30 | -------------------------------------------------------------------------------- /src/Group.ts: -------------------------------------------------------------------------------- 1 | import { Util } from './Util'; 2 | import { Container, ContainerConfig } from './Container'; 3 | import { _registerNode } from './Global'; 4 | import { Node } from './Node'; 5 | import { Shape } from './Shape'; 6 | 7 | export interface GroupConfig extends ContainerConfig {} 8 | 9 | /** 10 | * Group constructor. Groups are used to contain shapes or other groups. 11 | * @constructor 12 | * @memberof Konva 13 | * @augments Konva.Container 14 | * @param {Object} config 15 | * @@nodeParams 16 | * @@containerParams 17 | * @example 18 | * var group = new Konva.Group(); 19 | */ 20 | export class Group extends Container { 21 | _validateAdd(child: Node) { 22 | const type = child.getType(); 23 | if (type !== 'Group' && type !== 'Shape') { 24 | Util.throw('You may only add groups and shapes to groups.'); 25 | } 26 | } 27 | } 28 | 29 | Group.prototype.nodeType = 'Group'; 30 | _registerNode(Group); 31 | -------------------------------------------------------------------------------- /src/PointerEvents.ts: -------------------------------------------------------------------------------- 1 | import { KonvaEventObject } from './Node'; 2 | import { Konva } from './Global'; 3 | 4 | import { Shape } from './Shape'; 5 | import { Stage } from './Stage'; 6 | 7 | const Captures = new Map(); 8 | 9 | // we may use this module for capturing touch events too 10 | // so make sure we don't do something super specific to pointer 11 | const SUPPORT_POINTER_EVENTS = Konva._global['PointerEvent'] !== undefined; 12 | 13 | export interface KonvaPointerEvent extends KonvaEventObject { 14 | pointerId: number; 15 | } 16 | 17 | export function getCapturedShape(pointerId: number) { 18 | return Captures.get(pointerId); 19 | } 20 | 21 | export function createEvent(evt: PointerEvent): KonvaPointerEvent { 22 | return { 23 | evt, 24 | pointerId: evt.pointerId, 25 | } as any; 26 | } 27 | 28 | export function hasPointerCapture(pointerId: number, shape: Shape | Stage) { 29 | return Captures.get(pointerId) === shape; 30 | } 31 | 32 | export function setPointerCapture(pointerId: number, shape: Shape | Stage) { 33 | releaseCapture(pointerId); 34 | 35 | const stage = shape.getStage(); 36 | if (!stage) return; 37 | 38 | Captures.set(pointerId, shape); 39 | 40 | if (SUPPORT_POINTER_EVENTS) { 41 | shape._fire( 42 | 'gotpointercapture', 43 | createEvent(new PointerEvent('gotpointercapture')) 44 | ); 45 | } 46 | } 47 | 48 | export function releaseCapture(pointerId: number, target?: Shape | Stage) { 49 | const shape = Captures.get(pointerId); 50 | 51 | if (!shape) return; 52 | 53 | const stage = shape.getStage(); 54 | 55 | if (stage && stage.content) { 56 | // stage.content.releasePointerCapture(pointerId); 57 | } 58 | 59 | Captures.delete(pointerId); 60 | 61 | if (SUPPORT_POINTER_EVENTS) { 62 | shape._fire( 63 | 'lostpointercapture', 64 | createEvent(new PointerEvent('lostpointercapture')) 65 | ); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/_CoreInternals.ts: -------------------------------------------------------------------------------- 1 | // what is core parts of Konva? 2 | import { Konva as Global } from './Global'; 3 | 4 | import { Util, Transform } from './Util'; 5 | import { Node } from './Node'; 6 | import { Container } from './Container'; 7 | 8 | import { Stage, stages } from './Stage'; 9 | 10 | import { Layer } from './Layer'; 11 | import { FastLayer } from './FastLayer'; 12 | 13 | import { Group } from './Group'; 14 | 15 | import { DD } from './DragAndDrop'; 16 | 17 | import { Shape, shapes } from './Shape'; 18 | 19 | import { Animation } from './Animation'; 20 | import { Tween, Easings } from './Tween'; 21 | 22 | import { Context } from './Context'; 23 | import { Canvas } from './Canvas'; 24 | 25 | export const Konva = Util._assign(Global, { 26 | Util, 27 | Transform, 28 | Node, 29 | Container, 30 | Stage, 31 | stages, 32 | Layer, 33 | FastLayer, 34 | Group, 35 | DD, 36 | Shape, 37 | shapes, 38 | Animation, 39 | Tween, 40 | Easings, 41 | Context, 42 | Canvas, 43 | }); 44 | 45 | export default Konva; 46 | -------------------------------------------------------------------------------- /src/_FullInternals.ts: -------------------------------------------------------------------------------- 1 | // we need to import core of the Konva and then extend it with all additional objects 2 | 3 | import { Konva as Core } from './_CoreInternals'; 4 | 5 | // shapes 6 | import { Arc } from './shapes/Arc'; 7 | import { Arrow } from './shapes/Arrow'; 8 | import { Circle } from './shapes/Circle'; 9 | import { Ellipse } from './shapes/Ellipse'; 10 | import { Image } from './shapes/Image'; 11 | import { Label, Tag } from './shapes/Label'; 12 | import { Line } from './shapes/Line'; 13 | import { Path } from './shapes/Path'; 14 | import { Rect } from './shapes/Rect'; 15 | import { RegularPolygon } from './shapes/RegularPolygon'; 16 | import { Ring } from './shapes/Ring'; 17 | import { Sprite } from './shapes/Sprite'; 18 | import { Star } from './shapes/Star'; 19 | import { Text } from './shapes/Text'; 20 | import { TextPath } from './shapes/TextPath'; 21 | import { Transformer } from './shapes/Transformer'; 22 | import { Wedge } from './shapes/Wedge'; 23 | 24 | // filters 25 | import { Blur } from './filters/Blur'; 26 | import { Brighten } from './filters/Brighten'; 27 | import { Contrast } from './filters/Contrast'; 28 | import { Emboss } from './filters/Emboss'; 29 | import { Enhance } from './filters/Enhance'; 30 | import { Grayscale } from './filters/Grayscale'; 31 | import { HSL } from './filters/HSL'; 32 | import { HSV } from './filters/HSV'; 33 | import { Invert } from './filters/Invert'; 34 | import { Kaleidoscope } from './filters/Kaleidoscope'; 35 | import { Mask } from './filters/Mask'; 36 | import { Noise } from './filters/Noise'; 37 | import { Pixelate } from './filters/Pixelate'; 38 | import { Posterize } from './filters/Posterize'; 39 | import { RGB } from './filters/RGB'; 40 | import { RGBA } from './filters/RGBA'; 41 | import { Sepia } from './filters/Sepia'; 42 | import { Solarize } from './filters/Solarize'; 43 | import { Threshold } from './filters/Threshold'; 44 | 45 | export const Konva = Core.Util._assign(Core, { 46 | Arc, 47 | Arrow, 48 | Circle, 49 | Ellipse, 50 | Image, 51 | Label, 52 | Tag, 53 | Line, 54 | Path, 55 | Rect, 56 | RegularPolygon, 57 | Ring, 58 | Sprite, 59 | Star, 60 | Text, 61 | TextPath, 62 | Transformer, 63 | Wedge, 64 | /** 65 | * @namespace Filters 66 | * @memberof Konva 67 | */ 68 | Filters: { 69 | Blur, 70 | Brighten, 71 | Contrast, 72 | Emboss, 73 | Enhance, 74 | Grayscale, 75 | HSL, 76 | HSV, 77 | Invert, 78 | Kaleidoscope, 79 | Mask, 80 | Noise, 81 | Pixelate, 82 | Posterize, 83 | RGB, 84 | RGBA, 85 | Sepia, 86 | Solarize, 87 | Threshold, 88 | }, 89 | }); 90 | -------------------------------------------------------------------------------- /src/filters/Brighten.ts: -------------------------------------------------------------------------------- 1 | import { Factory } from '../Factory'; 2 | import { Node, Filter } from '../Node'; 3 | import { getNumberValidator } from '../Validators'; 4 | 5 | /** 6 | * Brighten Filter. 7 | * @function 8 | * @memberof Konva.Filters 9 | * @param {Object} imageData 10 | * @example 11 | * node.cache(); 12 | * node.filters([Konva.Filters.Brighten]); 13 | * node.brightness(0.8); 14 | */ 15 | export const Brighten: Filter = function (imageData) { 16 | const brightness = this.brightness() * 255, 17 | data = imageData.data, 18 | len = data.length; 19 | 20 | for (let i = 0; i < len; i += 4) { 21 | // red 22 | data[i] += brightness; 23 | // green 24 | data[i + 1] += brightness; 25 | // blue 26 | data[i + 2] += brightness; 27 | } 28 | }; 29 | 30 | Factory.addGetterSetter( 31 | Node, 32 | 'brightness', 33 | 0, 34 | getNumberValidator(), 35 | Factory.afterSetFilter 36 | ); 37 | /** 38 | * get/set filter brightness. The brightness is a number between -1 and 1.  Positive values 39 | * brighten the pixels and negative values darken them. Use with {@link Konva.Filters.Brighten} filter. 40 | * @name Konva.Node#brightness 41 | * @method 42 | 43 | * @param {Number} brightness value between -1 and 1 44 | * @returns {Number} 45 | */ 46 | -------------------------------------------------------------------------------- /src/filters/Contrast.ts: -------------------------------------------------------------------------------- 1 | import { Factory } from '../Factory'; 2 | import { Node, Filter } from '../Node'; 3 | import { getNumberValidator } from '../Validators'; 4 | /** 5 | * Contrast Filter. 6 | * @function 7 | * @memberof Konva.Filters 8 | * @param {Object} imageData 9 | * @example 10 | * node.cache(); 11 | * node.filters([Konva.Filters.Contrast]); 12 | * node.contrast(10); 13 | */ 14 | 15 | export const Contrast: Filter = function (imageData) { 16 | const adjust = Math.pow((this.contrast() + 100) / 100, 2); 17 | 18 | const data = imageData.data, 19 | nPixels = data.length; 20 | let red = 150, 21 | green = 150, 22 | blue = 150; 23 | 24 | for (let i = 0; i < nPixels; i += 4) { 25 | red = data[i]; 26 | green = data[i + 1]; 27 | blue = data[i + 2]; 28 | 29 | //Red channel 30 | red /= 255; 31 | red -= 0.5; 32 | red *= adjust; 33 | red += 0.5; 34 | red *= 255; 35 | 36 | //Green channel 37 | green /= 255; 38 | green -= 0.5; 39 | green *= adjust; 40 | green += 0.5; 41 | green *= 255; 42 | 43 | //Blue channel 44 | blue /= 255; 45 | blue -= 0.5; 46 | blue *= adjust; 47 | blue += 0.5; 48 | blue *= 255; 49 | 50 | red = red < 0 ? 0 : red > 255 ? 255 : red; 51 | green = green < 0 ? 0 : green > 255 ? 255 : green; 52 | blue = blue < 0 ? 0 : blue > 255 ? 255 : blue; 53 | 54 | data[i] = red; 55 | data[i + 1] = green; 56 | data[i + 2] = blue; 57 | } 58 | }; 59 | 60 | /** 61 | * get/set filter contrast. The contrast is a number between -100 and 100. 62 | * Use with {@link Konva.Filters.Contrast} filter. 63 | * @name Konva.Node#contrast 64 | * @method 65 | * @param {Number} contrast value between -100 and 100 66 | * @returns {Number} 67 | */ 68 | Factory.addGetterSetter( 69 | Node, 70 | 'contrast', 71 | 0, 72 | getNumberValidator(), 73 | Factory.afterSetFilter 74 | ); 75 | -------------------------------------------------------------------------------- /src/filters/Emboss.ts: -------------------------------------------------------------------------------- 1 | import { Factory } from '../Factory'; 2 | import { Node, Filter } from '../Node'; 3 | import { Util } from '../Util'; 4 | import { getNumberValidator } from '../Validators'; 5 | /** 6 | * Emboss Filter. 7 | * Pixastic Lib - Emboss filter - v0.1.0 8 | * Copyright (c) 2008 Jacob Seidelin, jseidelin@nihilogic.dk, http://blog.nihilogic.dk/ 9 | * License: [http://www.pixastic.com/lib/license.txt] 10 | * @function 11 | * @memberof Konva.Filters 12 | * @param {Object} imageData 13 | * @example 14 | * node.cache(); 15 | * node.filters([Konva.Filters.Emboss]); 16 | * node.embossStrength(0.8); 17 | * node.embossWhiteLevel(0.3); 18 | * node.embossDirection('right'); 19 | * node.embossBlend(true); 20 | */ 21 | export const Emboss: Filter = function (imageData) { 22 | // pixastic strength is between 0 and 10. I want it between 0 and 1 23 | // pixastic greyLevel is between 0 and 255. I want it between 0 and 1. Also, 24 | // a max value of greyLevel yields a white emboss, and the min value yields a black 25 | // emboss. Therefore, I changed greyLevel to whiteLevel 26 | const strength = this.embossStrength() * 10, 27 | greyLevel = this.embossWhiteLevel() * 255, 28 | direction = this.embossDirection(), 29 | blend = this.embossBlend(), 30 | data = imageData.data, 31 | w = imageData.width, 32 | h = imageData.height, 33 | w4 = w * 4; 34 | let dirY = 0, 35 | dirX = 0, 36 | y = h; 37 | 38 | switch (direction) { 39 | case 'top-left': 40 | dirY = -1; 41 | dirX = -1; 42 | break; 43 | case 'top': 44 | dirY = -1; 45 | dirX = 0; 46 | break; 47 | case 'top-right': 48 | dirY = -1; 49 | dirX = 1; 50 | break; 51 | case 'right': 52 | dirY = 0; 53 | dirX = 1; 54 | break; 55 | case 'bottom-right': 56 | dirY = 1; 57 | dirX = 1; 58 | break; 59 | case 'bottom': 60 | dirY = 1; 61 | dirX = 0; 62 | break; 63 | case 'bottom-left': 64 | dirY = 1; 65 | dirX = -1; 66 | break; 67 | case 'left': 68 | dirY = 0; 69 | dirX = -1; 70 | break; 71 | default: 72 | Util.error('Unknown emboss direction: ' + direction); 73 | } 74 | 75 | do { 76 | const offsetY = (y - 1) * w4; 77 | 78 | let otherY = dirY; 79 | if (y + otherY < 1) { 80 | otherY = 0; 81 | } 82 | if (y + otherY > h) { 83 | otherY = 0; 84 | } 85 | 86 | const offsetYOther = (y - 1 + otherY) * w * 4; 87 | 88 | let x = w; 89 | do { 90 | const offset = offsetY + (x - 1) * 4; 91 | 92 | let otherX = dirX; 93 | if (x + otherX < 1) { 94 | otherX = 0; 95 | } 96 | if (x + otherX > w) { 97 | otherX = 0; 98 | } 99 | 100 | const offsetOther = offsetYOther + (x - 1 + otherX) * 4; 101 | 102 | const dR = data[offset] - data[offsetOther]; 103 | const dG = data[offset + 1] - data[offsetOther + 1]; 104 | const dB = data[offset + 2] - data[offsetOther + 2]; 105 | 106 | let dif = dR; 107 | const absDif = dif > 0 ? dif : -dif; 108 | 109 | const absG = dG > 0 ? dG : -dG; 110 | const absB = dB > 0 ? dB : -dB; 111 | 112 | if (absG > absDif) { 113 | dif = dG; 114 | } 115 | if (absB > absDif) { 116 | dif = dB; 117 | } 118 | 119 | dif *= strength; 120 | 121 | if (blend) { 122 | const r = data[offset] + dif; 123 | const g = data[offset + 1] + dif; 124 | const b = data[offset + 2] + dif; 125 | 126 | data[offset] = r > 255 ? 255 : r < 0 ? 0 : r; 127 | data[offset + 1] = g > 255 ? 255 : g < 0 ? 0 : g; 128 | data[offset + 2] = b > 255 ? 255 : b < 0 ? 0 : b; 129 | } else { 130 | let grey = greyLevel - dif; 131 | if (grey < 0) { 132 | grey = 0; 133 | } else if (grey > 255) { 134 | grey = 255; 135 | } 136 | 137 | data[offset] = data[offset + 1] = data[offset + 2] = grey; 138 | } 139 | } while (--x); 140 | } while (--y); 141 | }; 142 | 143 | Factory.addGetterSetter( 144 | Node, 145 | 'embossStrength', 146 | 0.5, 147 | getNumberValidator(), 148 | Factory.afterSetFilter 149 | ); 150 | /** 151 | * get/set emboss strength. Use with {@link Konva.Filters.Emboss} filter. 152 | * @name Konva.Node#embossStrength 153 | * @method 154 | * @param {Number} level between 0 and 1. Default is 0.5 155 | * @returns {Number} 156 | */ 157 | 158 | Factory.addGetterSetter( 159 | Node, 160 | 'embossWhiteLevel', 161 | 0.5, 162 | getNumberValidator(), 163 | Factory.afterSetFilter 164 | ); 165 | /** 166 | * get/set emboss white level. Use with {@link Konva.Filters.Emboss} filter. 167 | * @name Konva.Node#embossWhiteLevel 168 | * @method 169 | * @param {Number} embossWhiteLevel between 0 and 1. Default is 0.5 170 | * @returns {Number} 171 | */ 172 | 173 | Factory.addGetterSetter( 174 | Node, 175 | 'embossDirection', 176 | 'top-left', 177 | undefined, 178 | Factory.afterSetFilter 179 | ); 180 | /** 181 | * get/set emboss direction. Use with {@link Konva.Filters.Emboss} filter. 182 | * @name Konva.Node#embossDirection 183 | * @method 184 | * @param {String} embossDirection can be top-left, top, top-right, right, bottom-right, bottom, bottom-left or left 185 | * The default is top-left 186 | * @returns {String} 187 | */ 188 | 189 | Factory.addGetterSetter( 190 | Node, 191 | 'embossBlend', 192 | false, 193 | undefined, 194 | Factory.afterSetFilter 195 | ); 196 | /** 197 | * get/set emboss blend. Use with {@link Konva.Filters.Emboss} filter. 198 | * @name Konva.Node#embossBlend 199 | * @method 200 | * @param {Boolean} embossBlend 201 | * @returns {Boolean} 202 | */ 203 | -------------------------------------------------------------------------------- /src/filters/Enhance.ts: -------------------------------------------------------------------------------- 1 | import { Factory } from '../Factory'; 2 | import { Node, Filter } from '../Node'; 3 | import { getNumberValidator } from '../Validators'; 4 | 5 | function remap(fromValue: number, fromMin: number, fromMax: number, toMin: number, toMax: number) { 6 | // Compute the range of the data 7 | const fromRange = fromMax - fromMin, 8 | toRange = toMax - toMin; 9 | 10 | // If either range is 0, then the value can only be mapped to 1 value 11 | if (fromRange === 0) { 12 | return toMin + toRange / 2; 13 | } 14 | if (toRange === 0) { 15 | return toMin; 16 | } 17 | 18 | // (1) untranslate, (2) unscale, (3) rescale, (4) retranslate 19 | let toValue = (fromValue - fromMin) / fromRange; 20 | toValue = toRange * toValue + toMin; 21 | 22 | return toValue; 23 | } 24 | 25 | /** 26 | * Enhance Filter. Adjusts the colors so that they span the widest 27 | * possible range (ie 0-255). Performs w*h pixel reads and w*h pixel 28 | * writes. 29 | * @function 30 | * @name Enhance 31 | * @memberof Konva.Filters 32 | * @param {Object} imageData 33 | * @author ippo615 34 | * @example 35 | * node.cache(); 36 | * node.filters([Konva.Filters.Enhance]); 37 | * node.enhance(0.4); 38 | */ 39 | export const Enhance: Filter = function (imageData) { 40 | const data = imageData.data, 41 | nSubPixels = data.length; 42 | let rMin = data[0], 43 | rMax = rMin, 44 | r, 45 | gMin = data[1], 46 | gMax = gMin, 47 | g, 48 | bMin = data[2], 49 | bMax = bMin, 50 | b; 51 | 52 | // If we are not enhancing anything - don't do any computation 53 | const enhanceAmount = this.enhance(); 54 | if (enhanceAmount === 0) { 55 | return; 56 | } 57 | 58 | // 1st Pass - find the min and max for each channel: 59 | for (let i = 0; i < nSubPixels; i += 4) { 60 | r = data[i + 0]; 61 | if (r < rMin) { 62 | rMin = r; 63 | } else if (r > rMax) { 64 | rMax = r; 65 | } 66 | g = data[i + 1]; 67 | if (g < gMin) { 68 | gMin = g; 69 | } else if (g > gMax) { 70 | gMax = g; 71 | } 72 | b = data[i + 2]; 73 | if (b < bMin) { 74 | bMin = b; 75 | } else if (b > bMax) { 76 | bMax = b; 77 | } 78 | //a = data[i + 3]; 79 | //if (a < aMin) { aMin = a; } else 80 | //if (a > aMax) { aMax = a; } 81 | } 82 | 83 | // If there is only 1 level - don't remap 84 | if (rMax === rMin) { 85 | rMax = 255; 86 | rMin = 0; 87 | } 88 | if (gMax === gMin) { 89 | gMax = 255; 90 | gMin = 0; 91 | } 92 | if (bMax === bMin) { 93 | bMax = 255; 94 | bMin = 0; 95 | } 96 | 97 | let rGoalMax: number, 98 | rGoalMin: number, 99 | gGoalMax: number, 100 | gGoalMin: number, 101 | bGoalMax: number, 102 | bGoalMin: number; 103 | 104 | // If the enhancement is positive - stretch the histogram 105 | if (enhanceAmount > 0) { 106 | rGoalMax = rMax + enhanceAmount * (255 - rMax); 107 | rGoalMin = rMin - enhanceAmount * (rMin - 0); 108 | gGoalMax = gMax + enhanceAmount * (255 - gMax); 109 | gGoalMin = gMin - enhanceAmount * (gMin - 0); 110 | bGoalMax = bMax + enhanceAmount * (255 - bMax); 111 | bGoalMin = bMin - enhanceAmount * (bMin - 0); 112 | // If the enhancement is negative - compress the histogram 113 | } else { 114 | const rMid = (rMax + rMin) * 0.5; 115 | rGoalMax = rMax + enhanceAmount * (rMax - rMid); 116 | rGoalMin = rMin + enhanceAmount * (rMin - rMid); 117 | const gMid = (gMax + gMin) * 0.5; 118 | gGoalMax = gMax + enhanceAmount * (gMax - gMid); 119 | gGoalMin = gMin + enhanceAmount * (gMin - gMid); 120 | const bMid = (bMax + bMin) * 0.5; 121 | bGoalMax = bMax + enhanceAmount * (bMax - bMid); 122 | bGoalMin = bMin + enhanceAmount * (bMin - bMid); 123 | } 124 | 125 | // Pass 2 - remap everything, except the alpha 126 | for (let i = 0; i < nSubPixels; i += 4) { 127 | data[i + 0] = remap(data[i + 0], rMin, rMax, rGoalMin, rGoalMax); 128 | data[i + 1] = remap(data[i + 1], gMin, gMax, gGoalMin, gGoalMax); 129 | data[i + 2] = remap(data[i + 2], bMin, bMax, bGoalMin, bGoalMax); 130 | //data[i + 3] = remap(data[i + 3], aMin, aMax, aGoalMin, aGoalMax); 131 | } 132 | }; 133 | 134 | /** 135 | * get/set enhance. Use with {@link Konva.Filters.Enhance} filter. -1 to 1 values 136 | * @name Konva.Node#enhance 137 | * @method 138 | * @param {Float} amount 139 | * @returns {Float} 140 | */ 141 | Factory.addGetterSetter( 142 | Node, 143 | 'enhance', 144 | 0, 145 | getNumberValidator(), 146 | Factory.afterSetFilter 147 | ); 148 | -------------------------------------------------------------------------------- /src/filters/Grayscale.ts: -------------------------------------------------------------------------------- 1 | import { Filter } from '../Node'; 2 | 3 | /** 4 | * Grayscale Filter 5 | * @function 6 | * @memberof Konva.Filters 7 | * @param {Object} imageData 8 | * @example 9 | * node.cache(); 10 | * node.filters([Konva.Filters.Grayscale]); 11 | */ 12 | export const Grayscale: Filter = function (imageData) { 13 | const data = imageData.data, 14 | len = data.length; 15 | 16 | for (let i = 0; i < len; i += 4) { 17 | const 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 | -------------------------------------------------------------------------------- /src/filters/HSL.ts: -------------------------------------------------------------------------------- 1 | import { Factory } from '../Factory'; 2 | import { Node, Filter } from '../Node'; 3 | import { getNumberValidator } from '../Validators'; 4 | 5 | Factory.addGetterSetter( 6 | Node, 7 | 'hue', 8 | 0, 9 | getNumberValidator(), 10 | Factory.afterSetFilter 11 | ); 12 | /** 13 | * get/set hsv hue in degrees. Use with {@link Konva.Filters.HSV} or {@link Konva.Filters.HSL} filter. 14 | * @name Konva.Node#hue 15 | * @method 16 | * @param {Number} hue value between 0 and 359 17 | * @returns {Number} 18 | */ 19 | 20 | Factory.addGetterSetter( 21 | Node, 22 | 'saturation', 23 | 0, 24 | getNumberValidator(), 25 | Factory.afterSetFilter 26 | ); 27 | /** 28 | * get/set hsv saturation. Use with {@link Konva.Filters.HSV} or {@link Konva.Filters.HSL} filter. 29 | * @name Konva.Node#saturation 30 | * @method 31 | * @param {Number} saturation 0 is no change, -1.0 halves the saturation, 1.0 doubles, etc.. 32 | * @returns {Number} 33 | */ 34 | 35 | Factory.addGetterSetter( 36 | Node, 37 | 'luminance', 38 | 0, 39 | getNumberValidator(), 40 | Factory.afterSetFilter 41 | ); 42 | /** 43 | * get/set hsl luminance. Use with {@link Konva.Filters.HSL} filter. 44 | * @name Konva.Node#luminance 45 | * @method 46 | * @param {Number} value from -1 to 1 47 | * @returns {Number} 48 | */ 49 | 50 | /** 51 | * HSL Filter. Adjusts the hue, saturation and luminance (or lightness) 52 | * @function 53 | * @memberof Konva.Filters 54 | * @param {Object} imageData 55 | * @author ippo615 56 | * @example 57 | * image.filters([Konva.Filters.HSL]); 58 | * image.luminance(0.2); 59 | */ 60 | 61 | export const HSL: Filter = function (imageData) { 62 | const data = imageData.data, 63 | nPixels = data.length, 64 | v = 1, 65 | s = Math.pow(2, this.saturation()), 66 | h = Math.abs(this.hue() + 360) % 360, 67 | l = this.luminance() * 127; 68 | 69 | // Basis for the technique used: 70 | // http://beesbuzz.biz/code/hsv_color_transforms.php 71 | // V is the value multiplier (1 for none, 2 for double, 0.5 for half) 72 | // S is the saturation multiplier (1 for none, 2 for double, 0.5 for half) 73 | // H is the hue shift in degrees (0 to 360) 74 | // vsu = V*S*cos(H*PI/180); 75 | // vsw = V*S*sin(H*PI/180); 76 | //[ .299V+.701vsu+.168vsw .587V-.587vsu+.330vsw .114V-.114vsu-.497vsw ] [R] 77 | //[ .299V-.299vsu-.328vsw .587V+.413vsu+.035vsw .114V-.114vsu+.292vsw ]*[G] 78 | //[ .299V-.300vsu+1.25vsw .587V-.588vsu-1.05vsw .114V+.886vsu-.203vsw ] [B] 79 | 80 | // Precompute the values in the matrix: 81 | const vsu = v * s * Math.cos((h * Math.PI) / 180), 82 | vsw = v * s * Math.sin((h * Math.PI) / 180); 83 | // (result spot)(source spot) 84 | const rr = 0.299 * v + 0.701 * vsu + 0.167 * vsw, 85 | rg = 0.587 * v - 0.587 * vsu + 0.33 * vsw, 86 | rb = 0.114 * v - 0.114 * vsu - 0.497 * vsw; 87 | const gr = 0.299 * v - 0.299 * vsu - 0.328 * vsw, 88 | gg = 0.587 * v + 0.413 * vsu + 0.035 * vsw, 89 | gb = 0.114 * v - 0.114 * vsu + 0.293 * vsw; 90 | const br = 0.299 * v - 0.3 * vsu + 1.25 * vsw, 91 | bg = 0.587 * v - 0.586 * vsu - 1.05 * vsw, 92 | bb = 0.114 * v + 0.886 * vsu - 0.2 * vsw; 93 | 94 | let r: number, g: number, b: number, a: number; 95 | 96 | for (let i = 0; i < nPixels; i += 4) { 97 | r = data[i + 0]; 98 | g = data[i + 1]; 99 | b = data[i + 2]; 100 | a = data[i + 3]; 101 | 102 | data[i + 0] = rr * r + rg * g + rb * b + l; 103 | data[i + 1] = gr * r + gg * g + gb * b + l; 104 | data[i + 2] = br * r + bg * g + bb * b + l; 105 | data[i + 3] = a; // alpha 106 | } 107 | }; 108 | -------------------------------------------------------------------------------- /src/filters/HSV.ts: -------------------------------------------------------------------------------- 1 | import { Factory } from '../Factory'; 2 | import { Filter, Node } from '../Node'; 3 | import { getNumberValidator } from '../Validators'; 4 | 5 | /** 6 | * HSV Filter. Adjusts the hue, saturation and value 7 | * @function 8 | * @name HSV 9 | * @memberof Konva.Filters 10 | * @param {Object} imageData 11 | * @author ippo615 12 | * @example 13 | * image.filters([Konva.Filters.HSV]); 14 | * image.value(200); 15 | */ 16 | 17 | export const HSV: Filter = function (imageData) { 18 | const data = imageData.data, 19 | nPixels = data.length, 20 | v = Math.pow(2, this.value()), 21 | s = Math.pow(2, this.saturation()), 22 | h = Math.abs(this.hue() + 360) % 360; 23 | 24 | // Basis for the technique used: 25 | // http://beesbuzz.biz/code/hsv_color_transforms.php 26 | // V is the value multiplier (1 for none, 2 for double, 0.5 for half) 27 | // S is the saturation multiplier (1 for none, 2 for double, 0.5 for half) 28 | // H is the hue shift in degrees (0 to 360) 29 | // vsu = V*S*cos(H*PI/180); 30 | // vsw = V*S*sin(H*PI/180); 31 | //[ .299V+.701vsu+.168vsw .587V-.587vsu+.330vsw .114V-.114vsu-.497vsw ] [R] 32 | //[ .299V-.299vsu-.328vsw .587V+.413vsu+.035vsw .114V-.114vsu+.292vsw ]*[G] 33 | //[ .299V-.300vsu+1.25vsw .587V-.588vsu-1.05vsw .114V+.886vsu-.203vsw ] [B] 34 | 35 | // Precompute the values in the matrix: 36 | const vsu = v * s * Math.cos((h * Math.PI) / 180), 37 | vsw = v * s * Math.sin((h * Math.PI) / 180); 38 | // (result spot)(source spot) 39 | const rr = 0.299 * v + 0.701 * vsu + 0.167 * vsw, 40 | rg = 0.587 * v - 0.587 * vsu + 0.33 * vsw, 41 | rb = 0.114 * v - 0.114 * vsu - 0.497 * vsw; 42 | const gr = 0.299 * v - 0.299 * vsu - 0.328 * vsw, 43 | gg = 0.587 * v + 0.413 * vsu + 0.035 * vsw, 44 | gb = 0.114 * v - 0.114 * vsu + 0.293 * vsw; 45 | const br = 0.299 * v - 0.3 * vsu + 1.25 * vsw, 46 | bg = 0.587 * v - 0.586 * vsu - 1.05 * vsw, 47 | bb = 0.114 * v + 0.886 * vsu - 0.2 * vsw; 48 | 49 | for (let i = 0; i < nPixels; i += 4) { 50 | const r = data[i + 0]; 51 | const g = data[i + 1]; 52 | const b = data[i + 2]; 53 | const a = data[i + 3]; 54 | 55 | data[i + 0] = rr * r + rg * g + rb * b; 56 | data[i + 1] = gr * r + gg * g + gb * b; 57 | data[i + 2] = br * r + bg * g + bb * b; 58 | data[i + 3] = a; // alpha 59 | } 60 | }; 61 | 62 | Factory.addGetterSetter( 63 | Node, 64 | 'hue', 65 | 0, 66 | getNumberValidator(), 67 | Factory.afterSetFilter 68 | ); 69 | /** 70 | * get/set hsv hue in degrees. Use with {@link Konva.Filters.HSV} or {@link Konva.Filters.HSL} filter. 71 | * @name Konva.Node#hue 72 | * @method 73 | * @param {Number} hue value between 0 and 359 74 | * @returns {Number} 75 | */ 76 | 77 | Factory.addGetterSetter( 78 | Node, 79 | 'saturation', 80 | 0, 81 | getNumberValidator(), 82 | Factory.afterSetFilter 83 | ); 84 | /** 85 | * get/set hsv saturation. Use with {@link Konva.Filters.HSV} or {@link Konva.Filters.HSL} filter. 86 | * @name Konva.Node#saturation 87 | * @method 88 | * @param {Number} saturation 0 is no change, -1.0 halves the saturation, 1.0 doubles, etc.. 89 | * @returns {Number} 90 | */ 91 | 92 | Factory.addGetterSetter( 93 | Node, 94 | 'value', 95 | 0, 96 | getNumberValidator(), 97 | Factory.afterSetFilter 98 | ); 99 | /** 100 | * get/set hsv value. Use with {@link Konva.Filters.HSV} filter. 101 | * @name Konva.Node#value 102 | * @method 103 | * @param {Number} value 0 is no change, -1.0 halves the value, 1.0 doubles, etc.. 104 | * @returns {Number} 105 | */ 106 | -------------------------------------------------------------------------------- /src/filters/Invert.ts: -------------------------------------------------------------------------------- 1 | import { Filter } from '../Node'; 2 | /** 3 | * Invert Filter 4 | * @function 5 | * @memberof Konva.Filters 6 | * @param {Object} imageData 7 | * @example 8 | * node.cache(); 9 | * node.filters([Konva.Filters.Invert]); 10 | */ 11 | export const Invert: Filter = function (imageData) { 12 | const data = imageData.data, 13 | len = data.length; 14 | 15 | for (let i = 0; i < len; i += 4) { 16 | // red 17 | data[i] = 255 - data[i]; 18 | // green 19 | data[i + 1] = 255 - data[i + 1]; 20 | // blue 21 | data[i + 2] = 255 - data[i + 2]; 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /src/filters/Noise.ts: -------------------------------------------------------------------------------- 1 | import { Factory } from '../Factory'; 2 | import { Filter, Node } from '../Node'; 3 | import { getNumberValidator } from '../Validators'; 4 | 5 | /** 6 | * Noise Filter. Randomly adds or substracts to the color channels 7 | * @function 8 | * @name Noise 9 | * @memberof Konva.Filters 10 | * @param {Object} imageData 11 | * @author ippo615 12 | * @example 13 | * node.cache(); 14 | * node.filters([Konva.Filters.Noise]); 15 | * node.noise(0.8); 16 | */ 17 | export const Noise: Filter = function (imageData) { 18 | const amount = this.noise() * 255, 19 | data = imageData.data, 20 | nPixels = data.length, 21 | half = amount / 2; 22 | 23 | for (let i = 0; i < nPixels; i += 4) { 24 | data[i + 0] += half - 2 * half * Math.random(); 25 | data[i + 1] += half - 2 * half * Math.random(); 26 | data[i + 2] += half - 2 * half * Math.random(); 27 | } 28 | }; 29 | 30 | Factory.addGetterSetter( 31 | Node, 32 | 'noise', 33 | 0.2, 34 | getNumberValidator(), 35 | Factory.afterSetFilter 36 | ); 37 | /** 38 | * get/set noise amount. Must be a value between 0 and 1. Use with {@link Konva.Filters.Noise} filter. 39 | * @name Konva.Node#noise 40 | * @method 41 | * @param {Number} noise 42 | * @returns {Number} 43 | */ 44 | -------------------------------------------------------------------------------- /src/filters/Pixelate.ts: -------------------------------------------------------------------------------- 1 | import { Factory } from '../Factory'; 2 | import { Util } from '../Util'; 3 | import { Node, Filter } from '../Node'; 4 | import { getNumberValidator } from '../Validators'; 5 | 6 | /** 7 | * Pixelate Filter. Averages groups of pixels and redraws 8 | * them as larger pixels 9 | * @function 10 | * @name Pixelate 11 | * @memberof Konva.Filters 12 | * @param {Object} imageData 13 | * @author ippo615 14 | * @example 15 | * node.cache(); 16 | * node.filters([Konva.Filters.Pixelate]); 17 | * node.pixelSize(10); 18 | */ 19 | 20 | export const Pixelate: Filter = function (imageData) { 21 | let pixelSize = Math.ceil(this.pixelSize()), 22 | width = imageData.width, 23 | height = imageData.height, 24 | //pixelsPerBin = pixelSize * pixelSize, 25 | nBinsX = Math.ceil(width / pixelSize), 26 | nBinsY = Math.ceil(height / pixelSize), 27 | data = imageData.data; 28 | 29 | if (pixelSize <= 0) { 30 | Util.error('pixelSize value can not be <= 0'); 31 | return; 32 | } 33 | 34 | for (let xBin = 0; xBin < nBinsX; xBin += 1) { 35 | for (let yBin = 0; yBin < nBinsY; yBin += 1) { 36 | // Initialize the color accumlators to 0 37 | let red = 0; 38 | let green = 0; 39 | let blue = 0; 40 | let alpha = 0; 41 | 42 | // Determine which pixels are included in this bin 43 | const xBinStart = xBin * pixelSize; 44 | const xBinEnd = xBinStart + pixelSize; 45 | const yBinStart = yBin * pixelSize; 46 | const yBinEnd = yBinStart + pixelSize; 47 | 48 | // Add all of the pixels to this bin! 49 | let pixelsInBin = 0; 50 | for (let x = xBinStart; x < xBinEnd; x += 1) { 51 | if (x >= width) { 52 | continue; 53 | } 54 | for (let y = yBinStart; y < yBinEnd; y += 1) { 55 | if (y >= height) { 56 | continue; 57 | } 58 | const i = (width * y + x) * 4; 59 | red += data[i + 0]; 60 | green += data[i + 1]; 61 | blue += data[i + 2]; 62 | alpha += data[i + 3]; 63 | pixelsInBin += 1; 64 | } 65 | } 66 | 67 | // Make sure the channels are between 0-255 68 | red = red / pixelsInBin; 69 | green = green / pixelsInBin; 70 | blue = blue / pixelsInBin; 71 | alpha = alpha / pixelsInBin; 72 | 73 | // Draw this bin 74 | for (let x = xBinStart; x < xBinEnd; x += 1) { 75 | if (x >= width) { 76 | continue; 77 | } 78 | for (let y = yBinStart; y < yBinEnd; y += 1) { 79 | if (y >= height) { 80 | continue; 81 | } 82 | const i = (width * y + x) * 4; 83 | data[i + 0] = red; 84 | data[i + 1] = green; 85 | data[i + 2] = blue; 86 | data[i + 3] = alpha; 87 | } 88 | } 89 | } 90 | } 91 | }; 92 | 93 | Factory.addGetterSetter( 94 | Node, 95 | 'pixelSize', 96 | 8, 97 | getNumberValidator(), 98 | Factory.afterSetFilter 99 | ); 100 | /** 101 | * get/set pixel size. Use with {@link Konva.Filters.Pixelate} filter. 102 | * @name Konva.Node#pixelSize 103 | * @method 104 | * @param {Integer} pixelSize 105 | * @returns {Integer} 106 | */ 107 | -------------------------------------------------------------------------------- /src/filters/Posterize.ts: -------------------------------------------------------------------------------- 1 | import { Factory } from '../Factory'; 2 | import { Filter, Node } from '../Node'; 3 | import { getNumberValidator } from '../Validators'; 4 | 5 | /** 6 | * Posterize Filter. Adjusts the channels so that there are no more 7 | * than n different values for that channel. This is also applied 8 | * to the alpha channel. 9 | * @function 10 | * @name Posterize 11 | * @author ippo615 12 | * @memberof Konva.Filters 13 | * @param {Object} imageData 14 | * @example 15 | * node.cache(); 16 | * node.filters([Konva.Filters.Posterize]); 17 | * node.levels(0.8); // between 0 and 1 18 | */ 19 | export const Posterize: Filter = function (imageData) { 20 | // level must be between 1 and 255 21 | const levels = Math.round(this.levels() * 254) + 1, 22 | data = imageData.data, 23 | len = data.length, 24 | scale = 255 / levels; 25 | 26 | for (let i = 0; i < len; i += 1) { 27 | data[i] = Math.floor(data[i] / scale) * scale; 28 | } 29 | }; 30 | 31 | Factory.addGetterSetter( 32 | Node, 33 | 'levels', 34 | 0.5, 35 | getNumberValidator(), 36 | Factory.afterSetFilter 37 | ); 38 | 39 | /** 40 | * get/set levels. Must be a number between 0 and 1. Use with {@link Konva.Filters.Posterize} filter. 41 | * @name Konva.Node#levels 42 | * @method 43 | * @param {Number} level between 0 and 1 44 | * @returns {Number} 45 | */ 46 | -------------------------------------------------------------------------------- /src/filters/RGB.ts: -------------------------------------------------------------------------------- 1 | import { Factory } from '../Factory'; 2 | import { Node, Filter } from '../Node'; 3 | import { RGBComponent } from '../Validators'; 4 | 5 | /** 6 | * RGB Filter 7 | * @function 8 | * @name RGB 9 | * @memberof Konva.Filters 10 | * @param {Object} imageData 11 | * @author ippo615 12 | * @example 13 | * node.cache(); 14 | * node.filters([Konva.Filters.RGB]); 15 | * node.blue(120); 16 | * node.green(200); 17 | */ 18 | export const RGB: Filter = function (imageData) { 19 | const data = imageData.data, 20 | nPixels = data.length, 21 | red = this.red(), 22 | green = this.green(), 23 | blue = this.blue(); 24 | 25 | for (let i = 0; i < nPixels; i += 4) { 26 | const brightness = 27 | (0.34 * data[i] + 0.5 * data[i + 1] + 0.16 * data[i + 2]) / 255; 28 | data[i] = brightness * red; // r 29 | data[i + 1] = brightness * green; // g 30 | data[i + 2] = brightness * blue; // b 31 | data[i + 3] = data[i + 3]; // alpha 32 | } 33 | }; 34 | 35 | Factory.addGetterSetter(Node, 'red', 0, function (this: Node, val) { 36 | this._filterUpToDate = false; 37 | if (val > 255) { 38 | return 255; 39 | } else if (val < 0) { 40 | return 0; 41 | } else { 42 | return Math.round(val); 43 | } 44 | }); 45 | /** 46 | * get/set filter red value. Use with {@link Konva.Filters.RGB} filter. 47 | * @name red 48 | * @method 49 | * @memberof Konva.Node.prototype 50 | * @param {Integer} red value between 0 and 255 51 | * @returns {Integer} 52 | */ 53 | 54 | Factory.addGetterSetter(Node, 'green', 0, function (this: Node, val) { 55 | this._filterUpToDate = false; 56 | if (val > 255) { 57 | return 255; 58 | } else if (val < 0) { 59 | return 0; 60 | } else { 61 | return Math.round(val); 62 | } 63 | }); 64 | /** 65 | * get/set filter green value. Use with {@link Konva.Filters.RGB} filter. 66 | * @name green 67 | * @method 68 | * @memberof Konva.Node.prototype 69 | * @param {Integer} green value between 0 and 255 70 | * @returns {Integer} 71 | */ 72 | 73 | Factory.addGetterSetter(Node, 'blue', 0, RGBComponent, Factory.afterSetFilter); 74 | /** 75 | * get/set filter blue value. Use with {@link Konva.Filters.RGB} filter. 76 | * @name blue 77 | * @method 78 | * @memberof Konva.Node.prototype 79 | * @param {Integer} blue value between 0 and 255 80 | * @returns {Integer} 81 | */ 82 | -------------------------------------------------------------------------------- /src/filters/RGBA.ts: -------------------------------------------------------------------------------- 1 | import { Factory } from '../Factory'; 2 | import { Node, Filter } from '../Node'; 3 | import { RGBComponent } from '../Validators'; 4 | 5 | /** 6 | * RGBA Filter 7 | * @function 8 | * @name RGBA 9 | * @memberof Konva.Filters 10 | * @param {Object} imageData 11 | * @author codefo 12 | * @example 13 | * node.cache(); 14 | * node.filters([Konva.Filters.RGBA]); 15 | * node.blue(120); 16 | * node.green(200); 17 | * node.alpha(0.3); 18 | */ 19 | 20 | export const RGBA: Filter = function (imageData) { 21 | const data = imageData.data, 22 | nPixels = data.length, 23 | red = this.red(), 24 | green = this.green(), 25 | blue = this.blue(), 26 | alpha = this.alpha(); 27 | 28 | for (let i = 0; i < nPixels; i += 4) { 29 | const ia = 1 - alpha; 30 | 31 | data[i] = red * alpha + data[i] * ia; // r 32 | data[i + 1] = green * alpha + data[i + 1] * ia; // g 33 | data[i + 2] = blue * alpha + data[i + 2] * ia; // b 34 | } 35 | }; 36 | 37 | Factory.addGetterSetter(Node, 'red', 0, function (this: Node, val: number) { 38 | this._filterUpToDate = false; 39 | if (val > 255) { 40 | return 255; 41 | } else if (val < 0) { 42 | return 0; 43 | } else { 44 | return Math.round(val); 45 | } 46 | }); 47 | /** 48 | * get/set filter red value. Use with {@link Konva.Filters.RGBA} filter. 49 | * @name red 50 | * @method 51 | * @memberof Konva.Node.prototype 52 | * @param {Integer} red value between 0 and 255 53 | * @returns {Integer} 54 | */ 55 | 56 | Factory.addGetterSetter(Node, 'green', 0, function (this: Node, val) { 57 | this._filterUpToDate = false; 58 | if (val > 255) { 59 | return 255; 60 | } else if (val < 0) { 61 | return 0; 62 | } else { 63 | return Math.round(val); 64 | } 65 | }); 66 | /** 67 | * get/set filter green value. Use with {@link Konva.Filters.RGBA} filter. 68 | * @name green 69 | * @method 70 | * @memberof Konva.Node.prototype 71 | * @param {Integer} green value between 0 and 255 72 | * @returns {Integer} 73 | */ 74 | 75 | Factory.addGetterSetter(Node, 'blue', 0, RGBComponent, Factory.afterSetFilter); 76 | /** 77 | * get/set filter blue value. Use with {@link Konva.Filters.RGBA} filter. 78 | * @name blue 79 | * @method 80 | * @memberof Konva.Node.prototype 81 | * @param {Integer} blue value between 0 and 255 82 | * @returns {Integer} 83 | */ 84 | 85 | Factory.addGetterSetter(Node, 'alpha', 1, function (this: Node, val) { 86 | this._filterUpToDate = false; 87 | if (val > 1) { 88 | return 1; 89 | } else if (val < 0) { 90 | return 0; 91 | } else { 92 | return val; 93 | } 94 | }); 95 | /** 96 | * get/set filter alpha value. Use with {@link Konva.Filters.RGBA} filter. 97 | * @name alpha 98 | * @method 99 | * @memberof Konva.Node.prototype 100 | * @param {Float} alpha value between 0 and 1 101 | * @returns {Float} 102 | */ 103 | -------------------------------------------------------------------------------- /src/filters/Sepia.ts: -------------------------------------------------------------------------------- 1 | import { Filter } from '../Node'; 2 | 3 | // based on https://stackoverflow.com/questions/1061093/how-is-a-sepia-tone-created 4 | 5 | /** 6 | * @function 7 | * @name Sepia 8 | * @memberof Konva.Filters 9 | * @param {Object} imageData 10 | * @example 11 | * node.cache(); 12 | * node.filters([Konva.Filters.Sepia]); 13 | */ 14 | export const Sepia: Filter = function (imageData) { 15 | const data = imageData.data, 16 | nPixels = data.length; 17 | 18 | for (let i = 0; i < nPixels; i += 4) { 19 | const r = data[i + 0]; 20 | const g = data[i + 1]; 21 | const b = data[i + 2]; 22 | 23 | data[i + 0] = Math.min(255, r * 0.393 + g * 0.769 + b * 0.189); 24 | data[i + 1] = Math.min(255, r * 0.349 + g * 0.686 + b * 0.168); 25 | data[i + 2] = Math.min(255, r * 0.272 + g * 0.534 + b * 0.131); 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /src/filters/Solarize.ts: -------------------------------------------------------------------------------- 1 | import { Filter } from '../Node'; 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 Konva.Filters 10 | * @param {Object} imageData 11 | * @example 12 | * node.cache(); 13 | * node.filters([Konva.Filters.Solarize]); 14 | */ 15 | 16 | export const Solarize: Filter = function (imageData) { 17 | const data = imageData.data, 18 | w = imageData.width, 19 | h = imageData.height, 20 | w4 = w * 4; 21 | 22 | let y = h; 23 | 24 | do { 25 | const offsetY = (y - 1) * w4; 26 | let x = w; 27 | do { 28 | const offset = offsetY + (x - 1) * 4; 29 | let r = data[offset]; 30 | let g = data[offset + 1]; 31 | let b = data[offset + 2]; 32 | 33 | if (r > 127) { 34 | r = 255 - r; 35 | } 36 | if (g > 127) { 37 | g = 255 - g; 38 | } 39 | if (b > 127) { 40 | b = 255 - b; 41 | } 42 | 43 | data[offset] = r; 44 | data[offset + 1] = g; 45 | data[offset + 2] = b; 46 | } while (--x); 47 | } while (--y); 48 | }; 49 | -------------------------------------------------------------------------------- /src/filters/Threshold.ts: -------------------------------------------------------------------------------- 1 | import { Factory } from '../Factory'; 2 | import { Node, Filter } from '../Node'; 3 | import { getNumberValidator } from '../Validators'; 4 | /** 5 | * Threshold Filter. Pushes any value above the mid point to 6 | * the max and any value below the mid point to the min. 7 | * This affects the alpha channel. 8 | * @function 9 | * @name Threshold 10 | * @memberof Konva.Filters 11 | * @param {Object} imageData 12 | * @author ippo615 13 | * @example 14 | * node.cache(); 15 | * node.filters([Konva.Filters.Threshold]); 16 | * node.threshold(0.1); 17 | */ 18 | 19 | export const Threshold: Filter = function (imageData) { 20 | const level = this.threshold() * 255, 21 | data = imageData.data, 22 | len = data.length; 23 | 24 | for (let i = 0; i < len; i += 1) { 25 | data[i] = data[i] < level ? 0 : 255; 26 | } 27 | }; 28 | 29 | Factory.addGetterSetter( 30 | Node, 31 | 'threshold', 32 | 0.5, 33 | getNumberValidator(), 34 | Factory.afterSetFilter 35 | ); 36 | /** 37 | * get/set threshold. Must be a value between 0 and 1. Use with {@link Konva.Filters.Threshold} or {@link Konva.Filters.Mask} filter. 38 | * @name threshold 39 | * @method 40 | * @memberof Konva.Node.prototype 41 | * @param {Number} threshold 42 | * @returns {Number} 43 | */ 44 | -------------------------------------------------------------------------------- /src/index-node.ts: -------------------------------------------------------------------------------- 1 | // main entry for umd build for rollup 2 | import { Konva } from './_FullInternals'; 3 | import * as Canvas from 'canvas'; 4 | 5 | const canvas = Canvas['default'] || Canvas; 6 | 7 | global.DOMMatrix = canvas.DOMMatrix; 8 | 9 | const isNode = typeof global.document === 'undefined'; 10 | 11 | if (isNode) { 12 | Konva.Util['createCanvasElement'] = () => { 13 | const node = canvas.createCanvas(300, 300) as any; 14 | if (!node['style']) { 15 | node['style'] = {}; 16 | } 17 | return node; 18 | }; 19 | 20 | // create image in Node env 21 | Konva.Util.createImageElement = () => { 22 | const node = new canvas.Image() as any; 23 | return node; 24 | }; 25 | } 26 | 27 | export default Konva; 28 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { Konva } from './_FullInternals'; 2 | 3 | export default Konva; 4 | -------------------------------------------------------------------------------- /src/shapes/Arc.ts: -------------------------------------------------------------------------------- 1 | import { Factory } from '../Factory'; 2 | import { Shape, ShapeConfig } from '../Shape'; 3 | import { Konva } from '../Global'; 4 | import { GetSet } from '../types'; 5 | import { getNumberValidator, getBooleanValidator } from '../Validators'; 6 | import { _registerNode } from '../Global'; 7 | import { Context } from '../Context'; 8 | 9 | export interface ArcConfig extends ShapeConfig { 10 | angle: number; 11 | innerRadius: number; 12 | outerRadius: number; 13 | clockwise?: boolean; 14 | } 15 | 16 | /** 17 | * Arc constructor 18 | * @constructor 19 | * @memberof Konva 20 | * @augments Konva.Shape 21 | * @param {Object} config 22 | * @param {Number} config.angle in degrees 23 | * @param {Number} config.innerRadius 24 | * @param {Number} config.outerRadius 25 | * @param {Boolean} [config.clockwise] 26 | * @@shapeParams 27 | * @@nodeParams 28 | * @example 29 | * // draw a Arc that's pointing downwards 30 | * var arc = new Konva.Arc({ 31 | * innerRadius: 40, 32 | * outerRadius: 80, 33 | * fill: 'red', 34 | * stroke: 'black' 35 | * strokeWidth: 5, 36 | * angle: 60, 37 | * rotationDeg: -120 38 | * }); 39 | */ 40 | export class Arc extends Shape { 41 | _sceneFunc(context: Context) { 42 | const angle = Konva.getAngle(this.angle()), 43 | clockwise = this.clockwise(); 44 | 45 | context.beginPath(); 46 | context.arc(0, 0, this.outerRadius(), 0, angle, clockwise); 47 | context.arc(0, 0, this.innerRadius(), angle, 0, !clockwise); 48 | context.closePath(); 49 | context.fillStrokeShape(this); 50 | } 51 | getWidth() { 52 | return this.outerRadius() * 2; 53 | } 54 | getHeight() { 55 | return this.outerRadius() * 2; 56 | } 57 | setWidth(width: number) { 58 | this.outerRadius(width / 2); 59 | } 60 | setHeight(height: number) { 61 | this.outerRadius(height / 2); 62 | } 63 | 64 | getSelfRect() { 65 | const innerRadius = this.innerRadius(); 66 | const outerRadius = this.outerRadius(); 67 | const clockwise = this.clockwise(); 68 | const angle = Konva.getAngle(clockwise ? 360 - this.angle() : this.angle()); 69 | 70 | const boundLeftRatio = Math.cos(Math.min(angle, Math.PI)); 71 | const boundRightRatio = 1; 72 | const boundTopRatio = Math.sin( 73 | Math.min(Math.max(Math.PI, angle), (3 * Math.PI) / 2) 74 | ); 75 | const boundBottomRatio = Math.sin(Math.min(angle, Math.PI / 2)); 76 | const boundLeft = 77 | boundLeftRatio * (boundLeftRatio > 0 ? innerRadius : outerRadius); 78 | const boundRight = 79 | boundRightRatio * (boundRightRatio > 0 ? outerRadius : innerRadius); 80 | const boundTop = 81 | boundTopRatio * (boundTopRatio > 0 ? innerRadius : outerRadius); 82 | const boundBottom = 83 | boundBottomRatio * (boundBottomRatio > 0 ? outerRadius : innerRadius); 84 | 85 | return { 86 | x: boundLeft, 87 | y: clockwise ? -1 * boundBottom : boundTop, 88 | width: boundRight - boundLeft, 89 | height: boundBottom - boundTop, 90 | }; 91 | } 92 | 93 | innerRadius: GetSet; 94 | outerRadius: GetSet; 95 | angle: GetSet; 96 | clockwise: GetSet; 97 | } 98 | 99 | Arc.prototype._centroid = true; 100 | Arc.prototype.className = 'Arc'; 101 | Arc.prototype._attrsAffectingSize = [ 102 | 'innerRadius', 103 | 'outerRadius', 104 | 'angle', 105 | 'clockwise', 106 | ]; 107 | _registerNode(Arc); 108 | 109 | // add getters setters 110 | Factory.addGetterSetter(Arc, 'innerRadius', 0, getNumberValidator()); 111 | 112 | /** 113 | * get/set innerRadius 114 | * @name Konva.Arc#innerRadius 115 | * @method 116 | * @param {Number} innerRadius 117 | * @returns {Number} 118 | * @example 119 | * // get inner radius 120 | * var innerRadius = arc.innerRadius(); 121 | * 122 | * // set inner radius 123 | * arc.innerRadius(20); 124 | */ 125 | 126 | Factory.addGetterSetter(Arc, 'outerRadius', 0, getNumberValidator()); 127 | 128 | /** 129 | * get/set outerRadius 130 | * @name Konva.Arc#outerRadius 131 | * @method 132 | * @param {Number} outerRadius 133 | * @returns {Number} 134 | * @example 135 | * // get outer radius 136 | * var outerRadius = arc.outerRadius(); 137 | * 138 | * // set outer radius 139 | * arc.outerRadius(20); 140 | */ 141 | 142 | Factory.addGetterSetter(Arc, 'angle', 0, getNumberValidator()); 143 | 144 | /** 145 | * get/set angle in degrees 146 | * @name Konva.Arc#angle 147 | * @method 148 | * @param {Number} angle 149 | * @returns {Number} 150 | * @example 151 | * // get angle 152 | * var angle = arc.angle(); 153 | * 154 | * // set angle 155 | * arc.angle(20); 156 | */ 157 | 158 | Factory.addGetterSetter(Arc, 'clockwise', false, getBooleanValidator()); 159 | 160 | /** 161 | * get/set clockwise flag 162 | * @name Konva.Arc#clockwise 163 | * @method 164 | * @param {Boolean} clockwise 165 | * @returns {Boolean} 166 | * @example 167 | * // get clockwise flag 168 | * var clockwise = arc.clockwise(); 169 | * 170 | * // draw arc counter-clockwise 171 | * arc.clockwise(false); 172 | * 173 | * // draw arc clockwise 174 | * arc.clockwise(true); 175 | */ 176 | -------------------------------------------------------------------------------- /src/shapes/Circle.ts: -------------------------------------------------------------------------------- 1 | import { Factory } from '../Factory'; 2 | import { Shape, ShapeConfig } from '../Shape'; 3 | import { GetSet } from '../types'; 4 | import { getNumberValidator } from '../Validators'; 5 | import { _registerNode } from '../Global'; 6 | import { Context } from '../Context'; 7 | 8 | export interface CircleConfig extends ShapeConfig { 9 | radius?: number; 10 | } 11 | 12 | /** 13 | * Circle constructor 14 | * @constructor 15 | * @memberof Konva 16 | * @augments Konva.Shape 17 | * @param {Object} config 18 | * @param {Number} config.radius 19 | * @@shapeParams 20 | * @@nodeParams 21 | * @example 22 | * // create circle 23 | * var circle = new Konva.Circle({ 24 | * radius: 40, 25 | * fill: 'red', 26 | * stroke: 'black', 27 | * strokeWidth: 5 28 | * }); 29 | */ 30 | export class Circle extends Shape { 31 | _sceneFunc(context: Context) { 32 | context.beginPath(); 33 | context.arc(0, 0, this.attrs.radius || 0, 0, Math.PI * 2, false); 34 | context.closePath(); 35 | context.fillStrokeShape(this); 36 | } 37 | getWidth() { 38 | return this.radius() * 2; 39 | } 40 | getHeight() { 41 | return this.radius() * 2; 42 | } 43 | setWidth(width: number) { 44 | if (this.radius() !== width / 2) { 45 | this.radius(width / 2); 46 | } 47 | } 48 | setHeight(height: number) { 49 | if (this.radius() !== height / 2) { 50 | this.radius(height / 2); 51 | } 52 | } 53 | 54 | radius: GetSet; 55 | } 56 | 57 | Circle.prototype._centroid = true; 58 | Circle.prototype.className = 'Circle'; 59 | Circle.prototype._attrsAffectingSize = ['radius']; 60 | _registerNode(Circle); 61 | 62 | /** 63 | * get/set radius 64 | * @name Konva.Circle#radius 65 | * @method 66 | * @param {Number} radius 67 | * @returns {Number} 68 | * @example 69 | * // get radius 70 | * var radius = circle.radius(); 71 | * 72 | * // set radius 73 | * circle.radius(10); 74 | */ 75 | Factory.addGetterSetter(Circle, 'radius', 0, getNumberValidator()); 76 | -------------------------------------------------------------------------------- /src/shapes/Ellipse.ts: -------------------------------------------------------------------------------- 1 | import { Factory } from '../Factory'; 2 | import { Shape, ShapeConfig } from '../Shape'; 3 | import { getNumberValidator } from '../Validators'; 4 | import { _registerNode } from '../Global'; 5 | import { Context } from '../Context'; 6 | 7 | import { GetSet, Vector2d } from '../types'; 8 | 9 | export interface EllipseConfig extends ShapeConfig { 10 | radiusX: number; 11 | radiusY: number; 12 | } 13 | 14 | /** 15 | * Ellipse constructor 16 | * @constructor 17 | * @memberof Konva 18 | * @augments Konva.Shape 19 | * @param {Object} config 20 | * @param {Object} config.radius defines x and y radius 21 | * @@shapeParams 22 | * @@nodeParams 23 | * @example 24 | * var ellipse = new Konva.Ellipse({ 25 | * radius : { 26 | * x : 50, 27 | * y : 50 28 | * }, 29 | * fill: 'red' 30 | * }); 31 | */ 32 | export class Ellipse extends Shape { 33 | _sceneFunc(context: Context) { 34 | const rx = this.radiusX(), 35 | ry = this.radiusY(); 36 | 37 | context.beginPath(); 38 | context.save(); 39 | if (rx !== ry) { 40 | context.scale(1, ry / rx); 41 | } 42 | context.arc(0, 0, rx, 0, Math.PI * 2, false); 43 | context.restore(); 44 | context.closePath(); 45 | context.fillStrokeShape(this); 46 | } 47 | getWidth() { 48 | return this.radiusX() * 2; 49 | } 50 | getHeight() { 51 | return this.radiusY() * 2; 52 | } 53 | setWidth(width: number) { 54 | this.radiusX(width / 2); 55 | } 56 | setHeight(height: number) { 57 | this.radiusY(height / 2); 58 | } 59 | 60 | radius: GetSet; 61 | radiusX: GetSet; 62 | radiusY: GetSet; 63 | } 64 | 65 | Ellipse.prototype.className = 'Ellipse'; 66 | Ellipse.prototype._centroid = true; 67 | Ellipse.prototype._attrsAffectingSize = ['radiusX', 'radiusY']; 68 | _registerNode(Ellipse); 69 | 70 | // add getters setters 71 | Factory.addComponentsGetterSetter(Ellipse, 'radius', ['x', 'y']); 72 | 73 | /** 74 | * get/set radius 75 | * @name Konva.Ellipse#radius 76 | * @method 77 | * @param {Object} radius 78 | * @param {Number} radius.x 79 | * @param {Number} radius.y 80 | * @returns {Object} 81 | * @example 82 | * // get radius 83 | * var radius = ellipse.radius(); 84 | * 85 | * // set radius 86 | * ellipse.radius({ 87 | * x: 200, 88 | * y: 100 89 | * }); 90 | */ 91 | 92 | Factory.addGetterSetter(Ellipse, 'radiusX', 0, getNumberValidator()); 93 | /** 94 | * get/set radius x 95 | * @name Konva.Ellipse#radiusX 96 | * @method 97 | * @param {Number} x 98 | * @returns {Number} 99 | * @example 100 | * // get radius x 101 | * var radiusX = ellipse.radiusX(); 102 | * 103 | * // set radius x 104 | * ellipse.radiusX(200); 105 | */ 106 | 107 | Factory.addGetterSetter(Ellipse, 'radiusY', 0, getNumberValidator()); 108 | /** 109 | * get/set radius y 110 | * @name Konva.Ellipse#radiusY 111 | * @method 112 | * @param {Number} y 113 | * @returns {Number} 114 | * @example 115 | * // get radius y 116 | * var radiusY = ellipse.radiusY(); 117 | * 118 | * // set radius y 119 | * ellipse.radiusY(200); 120 | */ 121 | -------------------------------------------------------------------------------- /src/shapes/Rect.ts: -------------------------------------------------------------------------------- 1 | import { Factory } from '../Factory'; 2 | import { Shape, ShapeConfig } from '../Shape'; 3 | import { _registerNode } from '../Global'; 4 | 5 | import { Util } from '../Util'; 6 | import { GetSet } from '../types'; 7 | import { Context } from '../Context'; 8 | import { getNumberOrArrayOfNumbersValidator } from '../Validators'; 9 | 10 | export interface RectConfig extends ShapeConfig { 11 | cornerRadius?: number | number[]; 12 | } 13 | 14 | /** 15 | * Rect constructor 16 | * @constructor 17 | * @memberof Konva 18 | * @augments Konva.Shape 19 | * @param {Object} config 20 | * @param {Number} [config.cornerRadius] 21 | * @@shapeParams 22 | * @@nodeParams 23 | * @example 24 | * var rect = new Konva.Rect({ 25 | * width: 100, 26 | * height: 50, 27 | * fill: 'red', 28 | * stroke: 'black', 29 | * strokeWidth: 5 30 | * }); 31 | */ 32 | export class Rect extends Shape { 33 | _sceneFunc(context: Context) { 34 | const cornerRadius = this.cornerRadius(), 35 | width = this.width(), 36 | height = this.height(); 37 | 38 | context.beginPath(); 39 | 40 | if (!cornerRadius) { 41 | // simple rect - don't bother doing all that complicated maths stuff. 42 | context.rect(0, 0, width, height); 43 | } else { 44 | Util.drawRoundedRectPath(context, width, height, cornerRadius); 45 | } 46 | context.closePath(); 47 | context.fillStrokeShape(this); 48 | } 49 | 50 | cornerRadius: GetSet; 51 | } 52 | 53 | Rect.prototype.className = 'Rect'; 54 | _registerNode(Rect); 55 | 56 | /** 57 | * get/set corner radius 58 | * @method 59 | * @name Konva.Rect#cornerRadius 60 | * @param {Number} cornerRadius 61 | * @returns {Number} 62 | * @example 63 | * // get corner radius 64 | * var cornerRadius = rect.cornerRadius(); 65 | * 66 | * // set corner radius 67 | * rect.cornerRadius(10); 68 | * 69 | * // set different corner radius values 70 | * // top-left, top-right, bottom-right, bottom-left 71 | * rect.cornerRadius([0, 10, 20, 30]); 72 | */ 73 | Factory.addGetterSetter( 74 | Rect, 75 | 'cornerRadius', 76 | 0, 77 | getNumberOrArrayOfNumbersValidator(4) 78 | ); 79 | -------------------------------------------------------------------------------- /src/shapes/RegularPolygon.ts: -------------------------------------------------------------------------------- 1 | import { Factory } from '../Factory'; 2 | import { Shape, ShapeConfig } from '../Shape'; 3 | import { GetSet, Vector2d } from '../types'; 4 | import { getNumberValidator } from '../Validators'; 5 | import { _registerNode } from '../Global'; 6 | import { Context } from '../Context'; 7 | 8 | export interface RegularPolygonConfig extends ShapeConfig { 9 | sides: number; 10 | radius: number; 11 | } 12 | /** 13 | * RegularPolygon constructor. Examples include triangles, squares, pentagons, hexagons, etc. 14 | * @constructor 15 | * @memberof Konva 16 | * @augments Konva.Shape 17 | * @param {Object} config 18 | * @param {Number} config.sides 19 | * @param {Number} config.radius 20 | * @@shapeParams 21 | * @@nodeParams 22 | * @example 23 | * var hexagon = new Konva.RegularPolygon({ 24 | * x: 100, 25 | * y: 200, 26 | * sides: 6, 27 | * radius: 70, 28 | * fill: 'red', 29 | * stroke: 'black', 30 | * strokeWidth: 4 31 | * }); 32 | */ 33 | export class RegularPolygon extends Shape { 34 | _sceneFunc(context: Context) { 35 | const points = this._getPoints(); 36 | 37 | context.beginPath(); 38 | context.moveTo(points[0].x, points[0].y); 39 | 40 | for (let n = 1; n < points.length; n++) { 41 | context.lineTo(points[n].x, points[n].y); 42 | } 43 | 44 | context.closePath(); 45 | context.fillStrokeShape(this); 46 | } 47 | _getPoints() { 48 | const sides = this.attrs.sides as number; 49 | const radius = this.attrs.radius || 0; 50 | const points: Vector2d[] = []; 51 | for (let n = 0; n < sides; n++) { 52 | points.push({ 53 | x: radius * Math.sin((n * 2 * Math.PI) / sides), 54 | y: -1 * radius * Math.cos((n * 2 * Math.PI) / sides), 55 | }); 56 | } 57 | return points; 58 | } 59 | getSelfRect() { 60 | const points = this._getPoints(); 61 | 62 | let minX = points[0].x; 63 | let maxX = points[0].y; 64 | let minY = points[0].x; 65 | let maxY = points[0].y; 66 | points.forEach((point) => { 67 | minX = Math.min(minX, point.x); 68 | maxX = Math.max(maxX, point.x); 69 | minY = Math.min(minY, point.y); 70 | maxY = Math.max(maxY, point.y); 71 | }); 72 | return { 73 | x: minX, 74 | y: minY, 75 | width: maxX - minX, 76 | height: maxY - minY, 77 | }; 78 | } 79 | getWidth() { 80 | return this.radius() * 2; 81 | } 82 | getHeight() { 83 | return this.radius() * 2; 84 | } 85 | setWidth(width: number) { 86 | this.radius(width / 2); 87 | } 88 | setHeight(height: number) { 89 | this.radius(height / 2); 90 | } 91 | 92 | radius: GetSet; 93 | sides: GetSet; 94 | } 95 | 96 | RegularPolygon.prototype.className = 'RegularPolygon'; 97 | RegularPolygon.prototype._centroid = true; 98 | RegularPolygon.prototype._attrsAffectingSize = ['radius']; 99 | _registerNode(RegularPolygon); 100 | 101 | /** 102 | * get/set radius 103 | * @method 104 | * @name Konva.RegularPolygon#radius 105 | * @param {Number} radius 106 | * @returns {Number} 107 | * @example 108 | * // get radius 109 | * var radius = shape.radius(); 110 | * 111 | * // set radius 112 | * shape.radius(10); 113 | */ 114 | Factory.addGetterSetter(RegularPolygon, 'radius', 0, getNumberValidator()); 115 | 116 | /** 117 | * get/set sides 118 | * @method 119 | * @name Konva.RegularPolygon#sides 120 | * @param {Number} sides 121 | * @returns {Number} 122 | * @example 123 | * // get sides 124 | * var sides = shape.sides(); 125 | * 126 | * // set sides 127 | * shape.sides(10); 128 | */ 129 | Factory.addGetterSetter(RegularPolygon, 'sides', 0, getNumberValidator()); 130 | -------------------------------------------------------------------------------- /src/shapes/Ring.ts: -------------------------------------------------------------------------------- 1 | import { Factory } from '../Factory'; 2 | import { Shape, ShapeConfig } from '../Shape'; 3 | import { GetSet } from '../types'; 4 | import { getNumberValidator } from '../Validators'; 5 | import { _registerNode } from '../Global'; 6 | import { Context } from '../Context'; 7 | 8 | export interface RingConfig extends ShapeConfig { 9 | innerRadius: number; 10 | outerRadius: number; 11 | } 12 | 13 | const PIx2 = Math.PI * 2; 14 | /** 15 | * Ring constructor 16 | * @constructor 17 | * @augments Konva.Shape 18 | * @memberof Konva 19 | * @param {Object} config 20 | * @param {Number} config.innerRadius 21 | * @param {Number} config.outerRadius 22 | * @@shapeParams 23 | * @@nodeParams 24 | * @example 25 | * var ring = new Konva.Ring({ 26 | * innerRadius: 40, 27 | * outerRadius: 80, 28 | * fill: 'red', 29 | * stroke: 'black', 30 | * strokeWidth: 5 31 | * }); 32 | */ 33 | export class Ring extends Shape { 34 | _sceneFunc(context: Context) { 35 | context.beginPath(); 36 | context.arc(0, 0, this.innerRadius(), 0, PIx2, false); 37 | context.moveTo(this.outerRadius(), 0); 38 | context.arc(0, 0, this.outerRadius(), PIx2, 0, true); 39 | context.closePath(); 40 | context.fillStrokeShape(this); 41 | } 42 | getWidth() { 43 | return this.outerRadius() * 2; 44 | } 45 | getHeight() { 46 | return this.outerRadius() * 2; 47 | } 48 | setWidth(width: number) { 49 | this.outerRadius(width / 2); 50 | } 51 | setHeight(height: number) { 52 | this.outerRadius(height / 2); 53 | } 54 | 55 | outerRadius: GetSet; 56 | innerRadius: GetSet; 57 | } 58 | 59 | Ring.prototype.className = 'Ring'; 60 | Ring.prototype._centroid = true; 61 | Ring.prototype._attrsAffectingSize = ['innerRadius', 'outerRadius']; 62 | _registerNode(Ring); 63 | 64 | /** 65 | * get/set innerRadius 66 | * @method 67 | * @name Konva.Ring#innerRadius 68 | * @param {Number} innerRadius 69 | * @returns {Number} 70 | * @example 71 | * // get inner radius 72 | * var innerRadius = ring.innerRadius(); 73 | * 74 | * // set inner radius 75 | * ring.innerRadius(20); 76 | */ 77 | 78 | Factory.addGetterSetter(Ring, 'innerRadius', 0, getNumberValidator()); 79 | 80 | /** 81 | * get/set outerRadius 82 | * @name Konva.Ring#outerRadius 83 | * @method 84 | * @param {Number} outerRadius 85 | * @returns {Number} 86 | * @example 87 | * // get outer radius 88 | * var outerRadius = ring.outerRadius(); 89 | * 90 | * // set outer radius 91 | * ring.outerRadius(20); 92 | */ 93 | Factory.addGetterSetter(Ring, 'outerRadius', 0, getNumberValidator()); 94 | -------------------------------------------------------------------------------- /src/shapes/Star.ts: -------------------------------------------------------------------------------- 1 | import { Factory } from '../Factory'; 2 | import { Context } from '../Context'; 3 | import { Shape, ShapeConfig } from '../Shape'; 4 | import { getNumberValidator } from '../Validators'; 5 | import { _registerNode } from '../Global'; 6 | 7 | import { GetSet } from '../types'; 8 | 9 | export interface StarConfig extends ShapeConfig { 10 | numPoints: number; 11 | innerRadius: number; 12 | outerRadius: number; 13 | } 14 | 15 | /** 16 | * Star constructor 17 | * @constructor 18 | * @memberof Konva 19 | * @augments Konva.Shape 20 | * @param {Object} config 21 | * @param {Integer} config.numPoints 22 | * @param {Number} config.innerRadius 23 | * @param {Number} config.outerRadius 24 | * @@shapeParams 25 | * @@nodeParams 26 | * @example 27 | * var star = new Konva.Star({ 28 | * x: 100, 29 | * y: 200, 30 | * numPoints: 5, 31 | * innerRadius: 70, 32 | * outerRadius: 70, 33 | * fill: 'red', 34 | * stroke: 'black', 35 | * strokeWidth: 4 36 | * }); 37 | */ 38 | export class Star extends Shape { 39 | _sceneFunc(context: Context) { 40 | const innerRadius = this.innerRadius(), 41 | outerRadius = this.outerRadius(), 42 | numPoints = this.numPoints(); 43 | 44 | context.beginPath(); 45 | context.moveTo(0, 0 - outerRadius); 46 | 47 | for (let n = 1; n < numPoints * 2; n++) { 48 | const radius = n % 2 === 0 ? outerRadius : innerRadius; 49 | const x = radius * Math.sin((n * Math.PI) / numPoints); 50 | const y = -1 * radius * Math.cos((n * Math.PI) / numPoints); 51 | context.lineTo(x, y); 52 | } 53 | context.closePath(); 54 | 55 | context.fillStrokeShape(this); 56 | } 57 | getWidth() { 58 | return this.outerRadius() * 2; 59 | } 60 | getHeight() { 61 | return this.outerRadius() * 2; 62 | } 63 | setWidth(width: number) { 64 | this.outerRadius(width / 2); 65 | } 66 | setHeight(height: number) { 67 | this.outerRadius(height / 2); 68 | } 69 | 70 | outerRadius: GetSet; 71 | innerRadius: GetSet; 72 | numPoints: GetSet; 73 | } 74 | 75 | Star.prototype.className = 'Star'; 76 | Star.prototype._centroid = true; 77 | Star.prototype._attrsAffectingSize = ['innerRadius', 'outerRadius']; 78 | _registerNode(Star); 79 | 80 | /** 81 | * get/set number of points 82 | * @name Konva.Star#numPoints 83 | * @method 84 | * @param {Number} numPoints 85 | * @returns {Number} 86 | * @example 87 | * // get inner radius 88 | * var numPoints = star.numPoints(); 89 | * 90 | * // set inner radius 91 | * star.numPoints(20); 92 | */ 93 | Factory.addGetterSetter(Star, 'numPoints', 5, getNumberValidator()); 94 | 95 | /** 96 | * get/set innerRadius 97 | * @name Konva.Star#innerRadius 98 | * @method 99 | * @param {Number} innerRadius 100 | * @returns {Number} 101 | * @example 102 | * // get inner radius 103 | * var innerRadius = star.innerRadius(); 104 | * 105 | * // set inner radius 106 | * star.innerRadius(20); 107 | */ 108 | Factory.addGetterSetter(Star, 'innerRadius', 0, getNumberValidator()); 109 | 110 | /** 111 | * get/set outerRadius 112 | * @name Konva.Star#outerRadius 113 | * @method 114 | * @param {Number} outerRadius 115 | * @returns {Number} 116 | * @example 117 | * // get inner radius 118 | * var outerRadius = star.outerRadius(); 119 | * 120 | * // set inner radius 121 | * star.outerRadius(20); 122 | */ 123 | 124 | Factory.addGetterSetter(Star, 'outerRadius', 0, getNumberValidator()); 125 | -------------------------------------------------------------------------------- /src/shapes/Wedge.ts: -------------------------------------------------------------------------------- 1 | import { Factory } from '../Factory'; 2 | import { Context } from '../Context'; 3 | import { Shape, ShapeConfig } from '../Shape'; 4 | import { Konva } from '../Global'; 5 | import { getNumberValidator } from '../Validators'; 6 | import { _registerNode } from '../Global'; 7 | 8 | import { GetSet } from '../types'; 9 | 10 | export interface WedgeConfig extends ShapeConfig { 11 | angle: number; 12 | radius: number; 13 | clockwise?: boolean; 14 | } 15 | 16 | /** 17 | * Wedge constructor 18 | * @constructor 19 | * @memberof Konva 20 | * @augments Konva.Shape 21 | * @param {Object} config 22 | * @param {Number} config.angle in degrees 23 | * @param {Number} config.radius 24 | * @param {Boolean} [config.clockwise] 25 | * @@shapeParams 26 | * @@nodeParams 27 | * @example 28 | * // draw a wedge that's pointing downwards 29 | * var wedge = new Konva.Wedge({ 30 | * radius: 40, 31 | * fill: 'red', 32 | * stroke: 'black' 33 | * strokeWidth: 5, 34 | * angleDeg: 60, 35 | * rotationDeg: -120 36 | * }); 37 | */ 38 | export class Wedge extends Shape { 39 | _sceneFunc(context: Context) { 40 | context.beginPath(); 41 | context.arc( 42 | 0, 43 | 0, 44 | this.radius(), 45 | 0, 46 | Konva.getAngle(this.angle()), 47 | this.clockwise() 48 | ); 49 | context.lineTo(0, 0); 50 | context.closePath(); 51 | context.fillStrokeShape(this); 52 | } 53 | getWidth() { 54 | return this.radius() * 2; 55 | } 56 | getHeight() { 57 | return this.radius() * 2; 58 | } 59 | setWidth(width: number) { 60 | this.radius(width / 2); 61 | } 62 | setHeight(height: number) { 63 | this.radius(height / 2); 64 | } 65 | 66 | radius: GetSet; 67 | angle: GetSet; 68 | clockwise: GetSet; 69 | } 70 | 71 | Wedge.prototype.className = 'Wedge'; 72 | Wedge.prototype._centroid = true; 73 | Wedge.prototype._attrsAffectingSize = ['radius']; 74 | _registerNode(Wedge); 75 | 76 | /** 77 | * get/set radius 78 | * @name Konva.Wedge#radius 79 | * @method 80 | * @param {Number} radius 81 | * @returns {Number} 82 | * @example 83 | * // get radius 84 | * var radius = wedge.radius(); 85 | * 86 | * // set radius 87 | * wedge.radius(10); 88 | */ 89 | Factory.addGetterSetter(Wedge, 'radius', 0, getNumberValidator()); 90 | 91 | /** 92 | * get/set angle in degrees 93 | * @name Konva.Wedge#angle 94 | * @method 95 | * @param {Number} angle 96 | * @returns {Number} 97 | * @example 98 | * // get angle 99 | * var angle = wedge.angle(); 100 | * 101 | * // set angle 102 | * wedge.angle(20); 103 | */ 104 | Factory.addGetterSetter(Wedge, 'angle', 0, getNumberValidator()); 105 | 106 | /** 107 | * get/set clockwise flag 108 | * @name Konva.Wedge#clockwise 109 | * @method 110 | * @param {Number} clockwise 111 | * @returns {Number} 112 | * @example 113 | * // get clockwise flag 114 | * var clockwise = wedge.clockwise(); 115 | * 116 | * // draw wedge counter-clockwise 117 | * wedge.clockwise(false); 118 | * 119 | * // draw wedge clockwise 120 | * wedge.clockwise(true); 121 | */ 122 | Factory.addGetterSetter(Wedge, 'clockwise', false); 123 | 124 | Factory.backCompat(Wedge, { 125 | angleDeg: 'angle', 126 | getAngleDeg: 'getAngle', 127 | setAngleDeg: 'setAngle', 128 | }); 129 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | export interface GetSet { 2 | (): Type; 3 | (v: Type | null | undefined): This; 4 | } 5 | 6 | export interface Vector2d { 7 | x: number; 8 | y: number; 9 | } 10 | 11 | export interface PathSegment { 12 | command: 13 | | 'm' 14 | | 'M' 15 | | 'l' 16 | | 'L' 17 | | 'v' 18 | | 'V' 19 | | 'h' 20 | | 'H' 21 | | 'z' 22 | | 'Z' 23 | | 'c' 24 | | 'C' 25 | | 'q' 26 | | 'Q' 27 | | 't' 28 | | 'T' 29 | | 's' 30 | | 'S' 31 | | 'a' 32 | | 'A'; 33 | start: Vector2d; 34 | points: number[]; 35 | pathLength: number; 36 | } 37 | 38 | export interface IRect { 39 | x: number; 40 | y: number; 41 | width: number; 42 | height: number; 43 | } 44 | 45 | export interface IFrame { 46 | time: number; 47 | timeDiff: number; 48 | lastTime: number; 49 | frameRate: number; 50 | } 51 | 52 | export type AnimationFn = (frame?: IFrame) => boolean | void; 53 | 54 | export enum KonvaNodeEvent { 55 | mouseover = 'mouseover', 56 | mouseout = 'mouseout', 57 | mousemove = 'mousemove', 58 | mouseleave = 'mouseleave', 59 | mouseenter = 'mouseenter', 60 | mousedown = 'mousedown', 61 | mouseup = 'mouseup', 62 | wheel = 'wheel', 63 | contextmenu = 'contextmenu', 64 | click = 'click', 65 | dblclick = 'dblclick', 66 | touchstart = 'touchstart', 67 | touchmove = 'touchmove', 68 | touchend = 'touchend', 69 | tap = 'tap', 70 | dbltap = 'dbltap', 71 | dragstart = 'dragstart', 72 | dragmove = 'dragmove', 73 | dragend = 'dragend', 74 | } 75 | 76 | export interface RGB { 77 | r: number; 78 | g: number; 79 | b: number; 80 | } 81 | 82 | export interface RGBA extends RGB { 83 | a: number; 84 | } 85 | -------------------------------------------------------------------------------- /test/assets/bunny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/konvajs/konva/f00fd70756f37eefcabb41a6494863ba8818b2a2/test/assets/bunny.png -------------------------------------------------------------------------------- /test/assets/darth-vader.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/konvajs/konva/f00fd70756f37eefcabb41a6494863ba8818b2a2/test/assets/darth-vader.jpg -------------------------------------------------------------------------------- /test/assets/lion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/konvajs/konva/f00fd70756f37eefcabb41a6494863ba8818b2a2/test/assets/lion.png -------------------------------------------------------------------------------- /test/assets/scorpion-sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/konvajs/konva/f00fd70756f37eefcabb41a6494863ba8818b2a2/test/assets/scorpion-sprite.png -------------------------------------------------------------------------------- /test/ifame.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | KonvaJS Sandbox 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/import-test.cjs: -------------------------------------------------------------------------------- 1 | // try to import only core 2 | const Konva = require('../'); 3 | 4 | // just do a simple action 5 | const stage = new Konva.Stage(); 6 | stage.toDataURL(); 7 | -------------------------------------------------------------------------------- /test/import-test.mjs: -------------------------------------------------------------------------------- 1 | function equal(val1, val2, message) { 2 | if (val1 !== val2) { 3 | throw new Error('Not passed: ' + message); 4 | } 5 | } 6 | 7 | // try to import only core 8 | import Konva from '../lib/Core.js'; 9 | import { Rect } from '../lib/shapes/Rect.js'; 10 | import '../lib/index-node.js'; 11 | 12 | equal(Rect !== undefined, true, 'Rect is defined'); 13 | 14 | equal(Konva.Rect, Rect, 'Rect is injected'); 15 | 16 | // // just do a simple action 17 | const stage = new Konva.Stage(); 18 | stage.toDataURL(); 19 | -------------------------------------------------------------------------------- /test/manual-tests.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 11 | 12 | 22 | 46 | 47 | 50 |
51 | 57 | 63 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /test/manual/Brighten-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | 3 | import { addStage, Konva, loadImage } from '../unit/test-utils'; 4 | 5 | describe('Brighten', function () { 6 | // ====================================================== 7 | it('basic', function (done) { 8 | var stage = addStage(); 9 | 10 | loadImage('darth-vader.jpg', (imageObj) => { 11 | var layer = new Konva.Layer(); 12 | const darth = new Konva.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([Konva.Filters.Brighten]); 24 | darth.brightness(0.3); 25 | layer.draw(); 26 | 27 | assert.equal(darth.brightness(), 0.3); 28 | 29 | done(); 30 | }); 31 | }); 32 | 33 | // ====================================================== 34 | it('tween', function (done) { 35 | var stage = addStage(); 36 | 37 | loadImage('darth-vader.jpg', (imageObj) => { 38 | var layer = new Konva.Layer(); 39 | const darth = new Konva.Image({ 40 | x: 10, 41 | y: 10, 42 | image: imageObj, 43 | draggable: true, 44 | }); 45 | 46 | layer.add(darth); 47 | stage.add(layer); 48 | 49 | darth.cache(); 50 | darth.filters([Konva.Filters.Brighten]); 51 | darth.brightness(0.3); 52 | layer.draw(); 53 | 54 | var tween = new Konva.Tween({ 55 | node: darth, 56 | duration: 2.0, 57 | brightness: 0, 58 | easing: Konva.Easings.EaseInOut, 59 | }); 60 | 61 | darth.on('mouseover', function () { 62 | tween.play(); 63 | }); 64 | 65 | darth.on('mouseout', function () { 66 | tween.reverse(); 67 | }); 68 | 69 | done(); 70 | }); 71 | }); 72 | 73 | // ====================================================== 74 | it('crop', function (done) { 75 | var stage = addStage(); 76 | 77 | loadImage('darth-vader.jpg', (imageObj) => { 78 | var layer = new Konva.Layer(); 79 | const darth = new Konva.Image({ 80 | x: 10, 81 | y: 10, 82 | image: imageObj, 83 | crop: { x: 128, y: 48, width: 256, height: 128 }, 84 | draggable: true, 85 | }); 86 | 87 | layer.add(darth); 88 | stage.add(layer); 89 | 90 | darth.cache(); 91 | darth.filters([Konva.Filters.Brighten]); 92 | darth.brightness(-0.3); 93 | layer.draw(); 94 | 95 | assert.equal(darth.brightness(), -0.3); 96 | 97 | done(); 98 | }); 99 | }); 100 | 101 | // ====================================================== 102 | it('tween transparency', function (done) { 103 | var stage = addStage(); 104 | 105 | loadImage('lion.png', (imageObj) => { 106 | var layer = new Konva.Layer(); 107 | const darth = new Konva.Image({ 108 | x: 10, 109 | y: 10, 110 | image: imageObj, 111 | draggable: true, 112 | }); 113 | 114 | layer.add(darth); 115 | stage.add(layer); 116 | 117 | darth.cache(); 118 | darth.filters([Konva.Filters.Brighten]); 119 | darth.brightness(0.3); 120 | layer.draw(); 121 | 122 | var tween = new Konva.Tween({ 123 | node: darth, 124 | duration: 2.0, 125 | brightness: -0.3, 126 | easing: Konva.Easings.EaseInOut, 127 | }); 128 | 129 | darth.on('mouseover', function () { 130 | tween.play(); 131 | }); 132 | 133 | darth.on('mouseout', function () { 134 | tween.reverse(); 135 | }); 136 | 137 | done(); 138 | }); 139 | }); 140 | }); 141 | -------------------------------------------------------------------------------- /test/manual/Contrast-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | 3 | import { addStage, Konva, loadImage } from '../unit/test-utils'; 4 | 5 | describe('Filter Contrast', function () { 6 | // ====================================================== 7 | it('basic', function (done) { 8 | var stage = addStage(); 9 | 10 | loadImage('darth-vader.jpg', (imageObj) => { 11 | var layer = new Konva.Layer(); 12 | var darth = new Konva.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([Konva.Filters.Contrast]); 24 | darth.contrast(40); 25 | layer.draw(); 26 | 27 | assert.equal(darth.contrast(), 40); 28 | 29 | done(); 30 | }); 31 | }); 32 | 33 | // ====================================================== 34 | it('tween', function (done) { 35 | var stage = addStage(); 36 | 37 | loadImage('darth-vader.jpg', (imageObj) => { 38 | var layer = new Konva.Layer(); 39 | var darth = new Konva.Image({ 40 | x: 10, 41 | y: 10, 42 | image: imageObj, 43 | draggable: true, 44 | }); 45 | 46 | layer.add(darth); 47 | stage.add(layer); 48 | 49 | darth.cache(); 50 | darth.filters([Konva.Filters.Contrast]); 51 | darth.contrast(40); 52 | layer.draw(); 53 | 54 | var tween = new Konva.Tween({ 55 | node: darth, 56 | duration: 2.0, 57 | contrast: 0, 58 | easing: Konva.Easings.EaseInOut, 59 | }); 60 | 61 | darth.on('mouseover', function () { 62 | tween.play(); 63 | }); 64 | 65 | darth.on('mouseout', function () { 66 | tween.reverse(); 67 | }); 68 | 69 | done(); 70 | }); 71 | }); 72 | 73 | // ====================================================== 74 | it('crop', function (done) { 75 | var stage = addStage(); 76 | 77 | loadImage('darth-vader.jpg', (imageObj) => { 78 | var layer = new Konva.Layer(); 79 | var darth = new Konva.Image({ 80 | x: 10, 81 | y: 10, 82 | image: imageObj, 83 | crop: { x: 128, y: 48, width: 256, height: 128 }, 84 | draggable: true, 85 | }); 86 | 87 | layer.add(darth); 88 | stage.add(layer); 89 | 90 | darth.cache(); 91 | darth.filters([Konva.Filters.Contrast]); 92 | darth.contrast(-40); 93 | layer.draw(); 94 | 95 | assert.equal(darth.contrast(), -40); 96 | 97 | done(); 98 | }); 99 | }); 100 | }); 101 | -------------------------------------------------------------------------------- /test/manual/Emboss-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | 3 | import { addStage, Konva, loadImage } from '../unit/test-utils'; 4 | 5 | describe('Emboss', function () { 6 | // ====================================================== 7 | it('basic emboss', function (done) { 8 | var stage = addStage(); 9 | 10 | loadImage('darth-vader.jpg', (imageObj) => { 11 | var layer = new Konva.Layer(); 12 | const darth = new Konva.Image({ 13 | x: 10, 14 | y: 10, 15 | image: imageObj, 16 | draggable: true, 17 | }); 18 | 19 | layer.add(darth); 20 | stage.add(layer); 21 | darth.cache(); 22 | darth.filters([Konva.Filters.Emboss]); 23 | darth.embossStrength(0.5); 24 | darth.embossWhiteLevel(0.8); 25 | darth.embossDirection('top-right'); 26 | 27 | layer.draw(); 28 | 29 | var tween = new Konva.Tween({ 30 | node: darth, 31 | duration: 0.6, 32 | embossStrength: 10, 33 | easing: Konva.Easings.EaseInOut, 34 | }); 35 | 36 | darth.on('mouseover', function () { 37 | tween.play(); 38 | }); 39 | 40 | darth.on('mouseout', function () { 41 | tween.reverse(); 42 | }); 43 | 44 | done(); 45 | }); 46 | //imageObj.src = 'assets/lion.png'; 47 | }); 48 | 49 | // ====================================================== 50 | it('blended emboss', function (done) { 51 | var stage = addStage(); 52 | 53 | loadImage('darth-vader.jpg', (imageObj) => { 54 | var layer = new Konva.Layer(); 55 | const darth = new Konva.Image({ 56 | x: 10, 57 | y: 10, 58 | image: imageObj, 59 | draggable: true, 60 | }); 61 | 62 | layer.add(darth); 63 | stage.add(layer); 64 | darth.cache(); 65 | darth.filters([Konva.Filters.Emboss]); 66 | darth.embossStrength(0.5); 67 | darth.embossWhiteLevel(0.2); 68 | darth.embossBlend(true); 69 | 70 | layer.draw(); 71 | 72 | var tween = new Konva.Tween({ 73 | node: darth, 74 | duration: 0.6, 75 | embossStrength: 10, 76 | easing: Konva.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/lion.png'; 90 | }); 91 | }); 92 | -------------------------------------------------------------------------------- /test/manual/Enhance-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | 3 | import { addStage, Konva, loadImage } from '../unit/test-utils'; 4 | 5 | describe('Enhance', function () { 6 | // ====================================================== 7 | it('on image', function (done) { 8 | var stage = addStage(); 9 | 10 | loadImage('scorpion-sprite.png', (imageObj) => { 11 | var layer = new Konva.Layer(); 12 | var filt = new Konva.Image({ 13 | x: 10, 14 | y: 10, 15 | image: imageObj, 16 | draggable: true, 17 | }); 18 | var orig = new Konva.Image({ 19 | x: 200, 20 | y: 10, 21 | image: imageObj, 22 | draggable: true, 23 | }); 24 | 25 | layer.add(filt); 26 | layer.add(orig); 27 | stage.add(layer); 28 | 29 | filt.cache(); 30 | filt.enhance(1.0); 31 | filt.filters([Konva.Filters.Enhance]); 32 | layer.draw(); 33 | 34 | done(); 35 | }); 36 | }); 37 | 38 | // ====================================================== 39 | it('tween enhance', function (done) { 40 | var stage = addStage(); 41 | 42 | loadImage('scorpion-sprite.png', (imageObj) => { 43 | var layer = new Konva.Layer(); 44 | const darth = new Konva.Image({ 45 | x: 10, 46 | y: 10, 47 | image: imageObj, 48 | draggable: true, 49 | }); 50 | 51 | layer.add(darth); 52 | stage.add(layer); 53 | 54 | darth.cache(); 55 | darth.filters([Konva.Filters.Enhance]); 56 | darth.enhance(-1); 57 | layer.draw(); 58 | 59 | var tween = new Konva.Tween({ 60 | node: darth, 61 | duration: 2.0, 62 | enhance: 1.0, 63 | easing: Konva.Easings.EaseInOut, 64 | }); 65 | 66 | darth.on('mouseover', function () { 67 | tween.play(); 68 | }); 69 | 70 | darth.on('mouseout', function () { 71 | tween.reverse(); 72 | }); 73 | 74 | done(); 75 | }); 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /test/manual/Filter-test.ts: -------------------------------------------------------------------------------- 1 | import { addStage, Konva, cloneAndCompareLayer } from '../unit/test-utils'; 2 | 3 | describe('Filter', function () { 4 | it('pixelRaio check', function () { 5 | Konva.pixelRatio = 2; 6 | var stage = addStage(); 7 | var layer = new Konva.Layer(); 8 | 9 | var circle = new Konva.Circle({ 10 | x: stage.width() / 2, 11 | y: stage.height() / 2, 12 | fill: 'red', 13 | stroke: 'green', 14 | radius: 15, 15 | }); 16 | 17 | layer.add(circle); 18 | stage.add(layer); 19 | circle.cache(); 20 | circle.filters([Konva.Filters.Blur]); 21 | circle.blurRadius(0); 22 | layer.draw(); 23 | 24 | cloneAndCompareLayer(layer, 150); 25 | Konva.pixelRatio = 1; 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /test/manual/Grayscale-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | 3 | import { addStage, Konva, loadImage } from '../unit/test-utils'; 4 | 5 | describe('Grayscale', function () { 6 | // ====================================================== 7 | it('basic', function (done) { 8 | var stage = addStage(); 9 | 10 | loadImage('darth-vader.jpg', (imageObj) => { 11 | var layer = new Konva.Layer(); 12 | const darth = new Konva.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([Konva.Filters.Grayscale]); 24 | layer.draw(); 25 | 26 | done(); 27 | }); 28 | }); 29 | 30 | // ====================================================== 31 | it('crop', function (done) { 32 | var stage = addStage(); 33 | 34 | loadImage('darth-vader.jpg', (imageObj) => { 35 | var layer = new Konva.Layer(); 36 | const darth = new Konva.Image({ 37 | x: 10, 38 | y: 10, 39 | image: imageObj, 40 | crop: { x: 128, y: 48, width: 256, height: 128 }, 41 | draggable: true, 42 | }); 43 | 44 | layer.add(darth); 45 | stage.add(layer); 46 | 47 | darth.cache(); 48 | darth.filters([Konva.Filters.Grayscale]); 49 | layer.draw(); 50 | 51 | done(); 52 | }); 53 | }); 54 | 55 | // ====================================================== 56 | it('with transparency', function (done) { 57 | var stage = addStage(); 58 | 59 | loadImage('lion.png', (imageObj) => { 60 | var layer = new Konva.Layer(); 61 | const darth = new Konva.Image({ 62 | x: 10, 63 | y: 10, 64 | image: imageObj, 65 | draggable: true, 66 | }); 67 | 68 | layer.add(darth); 69 | stage.add(layer); 70 | 71 | darth.cache(); 72 | darth.filters([Konva.Filters.Grayscale]); 73 | layer.draw(); 74 | 75 | done(); 76 | }); 77 | }); 78 | }); 79 | -------------------------------------------------------------------------------- /test/manual/HSL-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | 3 | import { addStage, Konva, loadImage } from '../unit/test-utils'; 4 | 5 | describe('HSL', function () { 6 | // ====================================================== 7 | it('hue shift tween transparancy', function (done) { 8 | var stage = addStage(); 9 | 10 | loadImage('lion.png', (imageObj) => { 11 | var layer = new Konva.Layer(); 12 | const darth = new Konva.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([Konva.Filters.HSL]); 24 | darth.hue(360); 25 | layer.draw(); 26 | 27 | var tween = new Konva.Tween({ 28 | node: darth, 29 | duration: 1.0, 30 | hue: 0, 31 | easing: Konva.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 | 46 | // ====================================================== 47 | it('HSL luminance tween transparancy', function (done) { 48 | var stage = addStage(); 49 | 50 | loadImage('darth-vader.jpg', (imageObj) => { 51 | var layer = new Konva.Layer(); 52 | const darth = new Konva.Image({ 53 | x: 10, 54 | y: 10, 55 | image: imageObj, 56 | draggable: true, 57 | }); 58 | 59 | layer.add(darth); 60 | stage.add(layer); 61 | 62 | darth.cache(); 63 | darth.filters([Konva.Filters.HSL]); 64 | darth.luminance(1.0); 65 | layer.draw(); 66 | 67 | var tween = new Konva.Tween({ 68 | node: darth, 69 | duration: 1.0, 70 | luminance: -1.0, 71 | easing: Konva.Easings.EaseInOut, 72 | }); 73 | 74 | darth.on('mouseover', function () { 75 | tween.play(); 76 | }); 77 | 78 | darth.on('mouseout', function () { 79 | tween.reverse(); 80 | }); 81 | 82 | done(); 83 | }); 84 | }); 85 | 86 | // ====================================================== 87 | it('HSL saturation tween transparancy', function (done) { 88 | var stage = addStage(); 89 | 90 | loadImage('lion.png', (imageObj) => { 91 | var layer = new Konva.Layer(); 92 | const darth = new Konva.Image({ 93 | x: 10, 94 | y: 10, 95 | image: imageObj, 96 | draggable: true, 97 | }); 98 | 99 | layer.add(darth); 100 | stage.add(layer); 101 | 102 | darth.cache(); 103 | darth.filters([Konva.Filters.HSL]); 104 | darth.saturation(1.0); 105 | layer.draw(); 106 | 107 | var tween = new Konva.Tween({ 108 | node: darth, 109 | duration: 1.0, 110 | saturation: -1.0, 111 | easing: Konva.Easings.EaseInOut, 112 | }); 113 | 114 | darth.on('mouseover', function () { 115 | tween.play(); 116 | }); 117 | 118 | darth.on('mouseout', function () { 119 | tween.reverse(); 120 | }); 121 | 122 | done(); 123 | }); 124 | }); 125 | }); 126 | -------------------------------------------------------------------------------- /test/manual/HSV-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | 3 | import { addStage, Konva, loadImage } from '../unit/test-utils'; 4 | 5 | describe('HSV', function () { 6 | // ====================================================== 7 | it('hue shift tween transparancy', function (done) { 8 | var stage = addStage(); 9 | 10 | loadImage('darth-vader.jpg', (imageObj) => { 11 | var layer = new Konva.Layer(); 12 | const darth = new Konva.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([Konva.Filters.HSV]); 24 | darth.hue(360); 25 | layer.draw(); 26 | 27 | var tween = new Konva.Tween({ 28 | node: darth, 29 | duration: 1.0, 30 | hue: 0, 31 | easing: Konva.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 | 46 | // ====================================================== 47 | it('saturate image', function (done) { 48 | var stage = addStage(); 49 | 50 | loadImage('darth-vader.jpg', (imageObj) => { 51 | var layer = new Konva.Layer(); 52 | const darth = new Konva.Image({ 53 | x: 10, 54 | y: 10, 55 | image: imageObj, 56 | draggable: true, 57 | }); 58 | 59 | layer.add(darth); 60 | stage.add(layer); 61 | 62 | darth.cache(); 63 | darth.filters([Konva.Filters.HSV]); 64 | 65 | darth.saturation(1.0); 66 | layer.draw(); 67 | 68 | var tween = new Konva.Tween({ 69 | node: darth, 70 | duration: 1.0, 71 | saturation: -1.0, 72 | easing: Konva.Easings.EaseInOut, 73 | }); 74 | 75 | darth.on('mouseover', function () { 76 | tween.play(); 77 | }); 78 | 79 | darth.on('mouseout', function () { 80 | tween.reverse(); 81 | }); 82 | 83 | done(); 84 | }); 85 | }); 86 | 87 | // ====================================================== 88 | it('saturation tween transparancy', function (done) { 89 | var stage = addStage(); 90 | 91 | loadImage('darth-vader.jpg', (imageObj) => { 92 | var layer = new Konva.Layer(); 93 | const darth = new Konva.Image({ 94 | x: 10, 95 | y: 10, 96 | image: imageObj, 97 | draggable: true, 98 | }); 99 | 100 | layer.add(darth); 101 | stage.add(layer); 102 | 103 | darth.cache(); 104 | darth.filters([Konva.Filters.HSV]); 105 | darth.saturation(1.0); 106 | layer.draw(); 107 | 108 | var tween = new Konva.Tween({ 109 | node: darth, 110 | duration: 1.0, 111 | saturation: -1, 112 | easing: Konva.Easings.EaseInOut, 113 | }); 114 | 115 | darth.on('mouseover', function () { 116 | tween.play(); 117 | }); 118 | 119 | darth.on('mouseout', function () { 120 | tween.reverse(); 121 | }); 122 | 123 | done(); 124 | }); 125 | }); 126 | 127 | // ====================================================== 128 | it('value tween transparancy', function (done) { 129 | var stage = addStage(); 130 | 131 | loadImage('darth-vader.jpg', (imageObj) => { 132 | var layer = new Konva.Layer(); 133 | const darth = new Konva.Image({ 134 | x: 10, 135 | y: 10, 136 | image: imageObj, 137 | draggable: true, 138 | }); 139 | 140 | layer.add(darth); 141 | stage.add(layer); 142 | 143 | darth.cache(); 144 | darth.filters([Konva.Filters.HSV]); 145 | darth.value(1.0); 146 | layer.draw(); 147 | 148 | var tween = new Konva.Tween({ 149 | node: darth, 150 | duration: 1.0, 151 | value: -1.0, 152 | easing: Konva.Easings.EaseInOut, 153 | }); 154 | 155 | darth.on('mouseover', function () { 156 | tween.play(); 157 | }); 158 | 159 | darth.on('mouseout', function () { 160 | tween.reverse(); 161 | }); 162 | 163 | done(); 164 | }); 165 | }); 166 | }); 167 | -------------------------------------------------------------------------------- /test/manual/Invert-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | 3 | import { addStage, Konva, loadImage } from '../unit/test-utils'; 4 | 5 | describe('Invert', function () { 6 | // ====================================================== 7 | it('basic', function (done) { 8 | var stage = addStage(); 9 | 10 | loadImage('darth-vader.jpg', (imageObj) => { 11 | var layer = new Konva.Layer(); 12 | const darth = new Konva.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([Konva.Filters.Invert]); 24 | layer.draw(); 25 | 26 | done(); 27 | }); 28 | }); 29 | 30 | // ====================================================== 31 | it('crop', function (done) { 32 | var stage = addStage(); 33 | 34 | loadImage('darth-vader.jpg', (imageObj) => { 35 | var layer = new Konva.Layer(); 36 | const darth = new Konva.Image({ 37 | x: 10, 38 | y: 10, 39 | image: imageObj, 40 | crop: { x: 128, y: 48, width: 256, height: 128 }, 41 | draggable: true, 42 | }); 43 | 44 | layer.add(darth); 45 | stage.add(layer); 46 | 47 | darth.cache(); 48 | darth.filters([Konva.Filters.Invert]); 49 | layer.draw(); 50 | 51 | done(); 52 | }); 53 | }); 54 | 55 | // ====================================================== 56 | it('transparancy', function (done) { 57 | var stage = addStage(); 58 | 59 | loadImage('darth-vader.jpg', (imageObj) => { 60 | var layer = new Konva.Layer(); 61 | const darth = new Konva.Image({ 62 | x: 10, 63 | y: 10, 64 | image: imageObj, 65 | draggable: true, 66 | }); 67 | 68 | layer.add(darth); 69 | stage.add(layer); 70 | 71 | darth.cache(); 72 | darth.filters([Konva.Filters.Invert]); 73 | layer.draw(); 74 | 75 | done(); 76 | }); 77 | }); 78 | }); 79 | -------------------------------------------------------------------------------- /test/manual/Kaleidoscope-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | 3 | import { addStage, Konva, loadImage } from '../unit/test-utils'; 4 | 5 | describe('Kaleidoscope', function () { 6 | // ====================================================== 7 | it('basic', function (done) { 8 | var stage = addStage(); 9 | 10 | loadImage('darth-vader.jpg', (imageObj) => { 11 | var layer = new Konva.Layer(); 12 | const darth = new Konva.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([Konva.Filters.Kaleidoscope]); 24 | darth.kaleidoscopePower(2); 25 | 26 | assert.equal(darth.kaleidoscopePower(), 2); 27 | assert.equal(darth._filterUpToDate, false); 28 | 29 | layer.draw(); 30 | 31 | assert.equal(darth._filterUpToDate, true); 32 | 33 | darth.kaleidoscopePower(3); 34 | 35 | assert.equal(darth.kaleidoscopePower(), 3); 36 | assert.equal(darth._filterUpToDate, false); 37 | 38 | layer.draw(); 39 | 40 | assert.equal(darth._filterUpToDate, true); 41 | 42 | done(); 43 | }); 44 | }); 45 | 46 | // ====================================================== 47 | it('tween angle', function (done) { 48 | var stage = addStage(); 49 | 50 | loadImage('darth-vader.jpg', (imageObj) => { 51 | var layer = new Konva.Layer(); 52 | const darth = new Konva.Image({ 53 | x: 10, 54 | y: 10, 55 | image: imageObj, 56 | draggable: true, 57 | }); 58 | 59 | layer.add(darth); 60 | stage.add(layer); 61 | 62 | darth.cache(); 63 | darth.filters([Konva.Filters.Kaleidoscope]); 64 | darth.kaleidoscopePower(3); 65 | darth.kaleidoscopeAngle(0); 66 | layer.draw(); 67 | 68 | var tween = new Konva.Tween({ 69 | node: darth, 70 | duration: 10.0, 71 | kaleidoscopeAngle: 720, 72 | easing: Konva.Easings.EaseInOut, 73 | }); 74 | 75 | darth.on('mouseover', function () { 76 | tween.play(); 77 | }); 78 | 79 | darth.on('mouseout', function () { 80 | tween.reverse(); 81 | }); 82 | 83 | done(); 84 | }); 85 | }); 86 | 87 | // ====================================================== 88 | it('tween power', function (done) { 89 | var stage = addStage(); 90 | 91 | loadImage('darth-vader.jpg', (imageObj) => { 92 | var layer = new Konva.Layer(); 93 | const darth = new Konva.Image({ 94 | x: 10, 95 | y: 10, 96 | image: imageObj, 97 | draggable: true, 98 | }); 99 | 100 | layer.add(darth); 101 | stage.add(layer); 102 | 103 | darth.cache(); 104 | darth.filters([Konva.Filters.Kaleidoscope]); 105 | darth.kaleidoscopePower(0); 106 | darth.kaleidoscopeAngle(0); 107 | layer.draw(); 108 | 109 | var tween = new Konva.Tween({ 110 | node: darth, 111 | duration: 2.0, 112 | kaleidoscopePower: 8, 113 | easing: Konva.Easings.EaseInOut, 114 | }); 115 | 116 | darth.on('mouseover', function () { 117 | tween.play(); 118 | }); 119 | 120 | darth.on('mouseout', function () { 121 | tween.reverse(); 122 | }); 123 | 124 | done(); 125 | }); 126 | }); 127 | }); 128 | -------------------------------------------------------------------------------- /test/manual/Mask-test.ts: -------------------------------------------------------------------------------- 1 | import { addStage, Konva, loadImage } from '../unit/test-utils'; 2 | 3 | describe('Mask', function () { 4 | // ====================================================== 5 | it('basic', function (done) { 6 | var stage = addStage(); 7 | 8 | loadImage('darth-vader.jpg', (imageObj) => { 9 | var layer = new Konva.Layer({ 10 | throttle: 999, 11 | }); 12 | var bamoon = new Konva.Image({ 13 | x: 0, 14 | y: 0, 15 | image: imageObj, 16 | draggable: true, 17 | }), 18 | filtered = new Konva.Image({ 19 | x: 300, 20 | y: 0, 21 | image: imageObj, 22 | draggable: true, 23 | }); 24 | 25 | layer.add(bamoon); 26 | layer.add(filtered); 27 | stage.add(layer); 28 | 29 | filtered.cache(); 30 | filtered.filters([Konva.Filters.Mask]); 31 | filtered.threshold(10); 32 | 33 | layer.draw(); 34 | 35 | done(); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /test/manual/Noise-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | 3 | import { addStage, Konva, loadImage } from '../unit/test-utils'; 4 | 5 | describe('Noise', function () { 6 | // ====================================================== 7 | it('noise tween', function (done) { 8 | var stage = addStage(); 9 | 10 | loadImage('darth-vader.jpg', (imageObj) => { 11 | var layer = new Konva.Layer(); 12 | const darth = new Konva.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([Konva.Filters.Noise]); 24 | darth.noise(1); 25 | layer.draw(); 26 | 27 | var tween = new Konva.Tween({ 28 | node: darth, 29 | duration: 5.0, 30 | noise: 0, 31 | easing: Konva.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 | }); 46 | -------------------------------------------------------------------------------- /test/manual/Pixelate-test.ts: -------------------------------------------------------------------------------- 1 | import { addStage, Konva, loadImage } from '../unit/test-utils'; 2 | import { cloneAndCompareLayer } from '../unit/test-utils'; 3 | 4 | describe('Pixelate', function () { 5 | // ====================================================== 6 | it('tween pixelate', function (done) { 7 | var stage = addStage(); 8 | 9 | loadImage('darth-vader.jpg', (imageObj) => { 10 | var layer = new Konva.Layer(); 11 | const lion = new Konva.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([Konva.Filters.Pixelate]); 23 | lion.pixelSize(16); 24 | layer.draw(); 25 | 26 | var tween = new Konva.Tween({ 27 | node: lion, 28 | duration: 3.0, 29 | pixelSize: 1, 30 | easing: Konva.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 | 45 | it('make sure we have no extra transparent pixels', function (done) { 46 | var stage = addStage(); 47 | var layer = new Konva.Layer(); 48 | stage.add(layer); 49 | 50 | Konva.Image.fromURL( 51 | 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGUAAABmCAYAAADS6F9hAAAAAXNSR0IArs4c6QAAAXJJREFUeF7t1cEJADAMw8B2/6Fd6BT3UCYQEiZ3205HGbhFoXp8mKJ4TYoCNilKUUQDIFM/pSigARCppRQFNAAitZSigAZApJZSFNAAiNRSigIaAJFaSlFAAyBSSykKaABEailFAQ2ASC2lKKABEKmlFAU0ACK1lKKABkCkllIU0ACI1FKKAhoAkVpKUUADIFJLKQpoAERqKUUBDYBILaUooAEQqaUUBTQAIrWUooAGQKSWUhTQAIjUUooCGgCRWkpRQAMgUkspCmgARGopRQENgEgtpSigARCppRQFNAAitZSigAZApJZSFNAAiNRSigIaAJFaSlFAAyBSSykKaABEailFAQ2ASC2lKKABEKmlFAU0ACK1lKKABkCkllIU0ACI1FKKAhoAkVpKUUADIFJLKQpoAERqKUUBDYBILaUooAEQqaUUBTQAIrWUooAGQKSWUhTQAIjUUooCGgCRWkpRQAMgUkspCmgARGopRQENgEgPgGOW3jCsp3sAAAAASUVORK5CYII=', 52 | function (image) { 53 | layer.add(image); 54 | 55 | image.cache(); 56 | image.filters([Konva.Filters.Pixelate]); 57 | image.pixelSize(4); 58 | layer.draw(); 59 | cloneAndCompareLayer(layer); 60 | 61 | done(); 62 | } 63 | ); 64 | }); 65 | }); 66 | -------------------------------------------------------------------------------- /test/manual/Posterize-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | 3 | import { addStage, Konva, loadImage } from '../unit/test-utils'; 4 | 5 | describe('Posterize', function () { 6 | // ====================================================== 7 | it('on image tween', function (done) { 8 | var stage = addStage(); 9 | 10 | loadImage('darth-vader.jpg', (imageObj) => { 11 | var layer = new Konva.Layer(); 12 | const darth = new Konva.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([Konva.Filters.Posterize]); 24 | darth.levels(0.2); 25 | layer.draw(); 26 | 27 | var tween = new Konva.Tween({ 28 | node: darth, 29 | duration: 1.0, 30 | levels: 0, 31 | easing: Konva.Easings.Linear, 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 | }); 46 | -------------------------------------------------------------------------------- /test/manual/RGB-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | 3 | import { addStage, Konva, loadImage } from '../unit/test-utils'; 4 | 5 | describe('RGB', function () { 6 | // ====================================================== 7 | it('colorize basic', function (done) { 8 | var stage = addStage(); 9 | 10 | loadImage('darth-vader.jpg', (imageObj) => { 11 | var layer = new Konva.Layer(); 12 | const darth = new Konva.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([Konva.Filters.RGB]); 24 | darth.red(255).green(0).blue(128); 25 | layer.draw(); 26 | 27 | // Assert fails even though '[255,0,128] = [255,0,128]' 28 | // assert.deepEqual(darth.getFilterColorizeColor(), [255,0,128]); 29 | 30 | done(); 31 | }); 32 | }); 33 | 34 | // ====================================================== 35 | it('colorize crop', function (done) { 36 | var stage = addStage(); 37 | 38 | loadImage('darth-vader.jpg', (imageObj) => { 39 | var layer = new Konva.Layer(); 40 | const darth = new Konva.Image({ 41 | x: 10, 42 | y: 10, 43 | image: imageObj, 44 | crop: { x: 128, y: 48, width: 256, height: 128 }, 45 | draggable: true, 46 | }); 47 | 48 | layer.add(darth); 49 | stage.add(layer); 50 | 51 | darth.cache(); 52 | darth.filters([Konva.Filters.RGB]); 53 | darth.red(0).green(255).blue(0); 54 | layer.draw(); 55 | 56 | // assert.deepEqual(darth.getFilterColorizeColor(), [0,255,0]); 57 | 58 | done(); 59 | }); 60 | }); 61 | 62 | // ====================================================== 63 | it('colorize transparancy', function (done) { 64 | loadImage('lion.png', (imageObj) => { 65 | var stage = addStage(); 66 | var layer = new Konva.Layer(); 67 | 68 | var colors = [ 69 | [255, 0, 0], 70 | [255, 128, 0], 71 | [255, 255, 0], 72 | [0, 255, 0], 73 | [0, 255, 128], 74 | [0, 255, 255], 75 | [0, 0, 255], 76 | [128, 0, 255], 77 | [255, 0, 255], 78 | [0, 0, 0], 79 | [128, 128, 128], 80 | [255, 255, 255], 81 | ]; 82 | var i, 83 | l = colors.length; 84 | var nAdded = 0; 85 | for (i = 0; i < l; i += 1) { 86 | const color = colors[i]; 87 | const x = -64 + (i / l) * stage.width(); 88 | var darth = new Konva.Image({ 89 | x: x, 90 | y: 32, 91 | image: imageObj, 92 | draggable: true, 93 | }); 94 | layer.add(darth); 95 | 96 | darth.cache(); 97 | darth.filters([Konva.Filters.RGB]); 98 | darth.red(color[0]).green(color[1]).blue(color[2]); 99 | 100 | nAdded += 1; 101 | if (nAdded >= l) { 102 | stage.add(layer); 103 | layer.draw(); 104 | done(); 105 | } 106 | } 107 | }); 108 | }); 109 | }); 110 | -------------------------------------------------------------------------------- /test/manual/RGBA-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | 3 | import { addStage, Konva, loadImage } from '../unit/test-utils'; 4 | 5 | describe('RGBA', function () { 6 | // ====================================================== 7 | it.skip('colorize basic', function (done) { 8 | var data = [ 9 | { 10 | color: '#2a6511', 11 | filter: [242, 193, 168, 0.33], 12 | result: [108, 131, 67, 255], 13 | }, 14 | { 15 | color: '#e4d526', 16 | filter: [175, 98, 37, 0.79], 17 | result: [186, 122, 37, 255], 18 | }, 19 | ]; 20 | 21 | var stage = new Konva.Stage({ 22 | container: 'konva-container', 23 | width: data.length, 24 | height: 1, 25 | }); 26 | 27 | var layer = new Konva.Layer({ 28 | id: 'layer', 29 | }); 30 | 31 | for (var i = 0; i < data.length; i += 1) { 32 | var d = data[i]; 33 | 34 | var rect = new Konva.Rect({ 35 | x: i, 36 | y: 0, 37 | width: 1, 38 | height: 1, 39 | fill: d.color, 40 | }); 41 | 42 | rect.cache(); 43 | 44 | rect.red(d.filter[0]); 45 | rect.green(d.filter[1]); 46 | rect.blue(d.filter[2]); 47 | rect.alpha(d.filter[3]); 48 | 49 | rect.filters([Konva.Filters.RGBA]); 50 | layer.add(rect); 51 | } 52 | 53 | stage.add(layer); 54 | layer.batchDraw(); 55 | 56 | var context = layer.getCanvas().getContext(); 57 | 58 | var imageDataToArray = function (x) { 59 | var imageData = context.getImageData(x, 0, 1, 1).data; 60 | 61 | return [imageData['0'], imageData['1'], imageData['2'], imageData['3']]; 62 | }; 63 | 64 | var a0 = imageDataToArray(0); 65 | var a1 = imageDataToArray(1); 66 | 67 | assert.deepEqual(a0, data[0].result); 68 | assert.deepEqual(a1, data[1].result); 69 | 70 | done(); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /test/manual/Sepia-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | 3 | import { addStage, Konva, loadImage } from '../unit/test-utils'; 4 | 5 | describe('Filter Sepia', function () { 6 | // ====================================================== 7 | it('basic', function (done) { 8 | var stage = addStage(); 9 | 10 | loadImage('darth-vader.jpg', (imageObj) => { 11 | var layer = new Konva.Layer(); 12 | const darth = new Konva.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([Konva.Filters.Sepia]); 24 | layer.draw(); 25 | 26 | done(); 27 | }); 28 | }); 29 | 30 | // ====================================================== 31 | it('crop', function (done) { 32 | var stage = addStage(); 33 | 34 | loadImage('darth-vader.jpg', (imageObj) => { 35 | var layer = new Konva.Layer(); 36 | const darth = new Konva.Image({ 37 | x: 10, 38 | y: 10, 39 | image: imageObj, 40 | crop: { x: 128, y: 48, width: 256, height: 128 }, 41 | draggable: true, 42 | }); 43 | 44 | layer.add(darth); 45 | stage.add(layer); 46 | 47 | darth.cache(); 48 | darth.filters([Konva.Filters.Sepia]); 49 | layer.draw(); 50 | 51 | done(); 52 | }); 53 | }); 54 | 55 | // ====================================================== 56 | it('with transparency', function (done) { 57 | var stage = addStage(); 58 | 59 | loadImage('darth-vader.jpg', (imageObj) => { 60 | var layer = new Konva.Layer(); 61 | const darth = new Konva.Image({ 62 | x: 10, 63 | y: 10, 64 | image: imageObj, 65 | draggable: true, 66 | }); 67 | 68 | layer.add(darth); 69 | stage.add(layer); 70 | 71 | darth.cache(); 72 | darth.filters([Konva.Filters.Sepia]); 73 | layer.draw(); 74 | 75 | done(); 76 | }); 77 | }); 78 | }); 79 | -------------------------------------------------------------------------------- /test/manual/Solarize-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | 3 | import { addStage, Konva, loadImage } from '../unit/test-utils'; 4 | 5 | describe('Solarize', function () { 6 | // ====================================================== 7 | it('solarize', function (done) { 8 | var stage = addStage(); 9 | 10 | loadImage('darth-vader.jpg', (imageObj) => { 11 | var layer = new Konva.Layer(); 12 | const darth = new Konva.Image({ 13 | x: 10, 14 | y: 10, 15 | image: imageObj, 16 | draggable: true, 17 | }); 18 | 19 | layer.add(darth); 20 | stage.add(layer); 21 | darth.cache(); 22 | darth.filters([Konva.Filters.Solarize]); 23 | 24 | layer.draw(); 25 | 26 | done(); 27 | }); 28 | //imageObj.src = 'assets/lion.png'; 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/manual/Threshold-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | 3 | import { addStage, Konva, loadImage } from '../unit/test-utils'; 4 | 5 | describe('Threshold', function () { 6 | // ====================================================== 7 | it('image tween', function (done) { 8 | var stage = addStage(); 9 | 10 | loadImage('darth-vader.jpg', (imageObj) => { 11 | var layer = new Konva.Layer(); 12 | const darth = new Konva.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([Konva.Filters.Threshold]); 24 | darth.threshold(1); 25 | layer.draw(); 26 | 27 | var tween = new Konva.Tween({ 28 | node: darth, 29 | duration: 5.0, 30 | threshold: 0, 31 | easing: Konva.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 | }); 46 | -------------------------------------------------------------------------------- /test/node-global-setup.mjs: -------------------------------------------------------------------------------- 1 | export function mochaGlobalSetup() { 2 | globalThis.Path2D ??= class Path2D { 3 | constructor(path) { 4 | this.path = path 5 | } 6 | 7 | get [Symbol.toStringTag]() { 8 | return `Path2D`; 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "commonjs" 3 | } 4 | -------------------------------------------------------------------------------- /test/performance/bunnies_native.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | 13 | 14 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /test/performance/creating_elements.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /test/performance/jump-shape.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /test/sandbox.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | KonvaJS Sandbox 6 | 10 | 21 | 22 | 23 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 | 35 | 36 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2015", 4 | "noEmitOnError": false, 5 | "moduleResolution": "node", 6 | "lib": ["ES2015", "dom"], 7 | "module": "CommonJS", 8 | "skipLibCheck": true, 9 | "noImplicitAny": false, 10 | "allowJs": true, 11 | "noEmit": false, 12 | "checkJs": false, 13 | "allowUnreachableCode": true, 14 | "allowUnusedLabels": true, 15 | "noFallthroughCasesInSwitch": false, 16 | "noImplicitReturns": false, 17 | "noImplicitThis": false, 18 | "noPropertyAccessFromIndexSignature": false, 19 | "noUnusedLocals": false, 20 | "noUnusedParameters": false, 21 | "strict": false 22 | }, 23 | "include": ["../src/**/*.ts", "./**/*.ts"], 24 | "exclude": ["../node_modules/**/*"] 25 | } 26 | -------------------------------------------------------------------------------- /test/unit-tests.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 11 | 57 | 58 | 61 |
62 | 68 | 74 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /test/unit/Animation-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | 3 | import { addStage, Konva } from './test-utils'; 4 | 5 | describe('Animation', function () { 6 | // ====================================================== 7 | it('test start and stop', function () { 8 | var stage = addStage(); 9 | var layer = new Konva.Layer(); 10 | var rect = new Konva.Rect({ 11 | x: 200, 12 | y: 100, 13 | width: 100, 14 | height: 50, 15 | fill: 'green', 16 | stroke: 'black', 17 | strokeWidth: 4, 18 | }); 19 | 20 | layer.add(rect); 21 | stage.add(layer); 22 | 23 | var amplitude = 150; 24 | var period = 1000; 25 | // in ms 26 | var centerX = stage.width() / 2 - 100 / 2; 27 | 28 | var anim = new Konva.Animation(function (frame) { 29 | rect.x( 30 | amplitude * Math.sin((frame.time * 2 * Math.PI) / period) + centerX 31 | ); 32 | }, layer); 33 | var a = Konva.Animation.animations; 34 | var startLen = a.length; 35 | 36 | assert.equal(a.length, startLen, '1should be no animations running'); 37 | 38 | anim.start(); 39 | assert.equal(a.length, startLen + 1, '2should be 1 animation running'); 40 | 41 | anim.stop(); 42 | assert.equal(a.length, startLen, '3should be no animations running'); 43 | 44 | anim.start(); 45 | assert.equal(a.length, startLen + 1, '4should be 1 animation running'); 46 | 47 | anim.start(); 48 | assert.equal(a.length, startLen + 1, '5should be 1 animation runningg'); 49 | 50 | anim.stop(); 51 | assert.equal(a.length, startLen, '6should be no animations running'); 52 | 53 | anim.stop(); 54 | assert.equal(a.length, startLen, '7should be no animations running'); 55 | }); 56 | 57 | // ====================================================== 58 | it('layer batch draw', function () { 59 | var stage = addStage(); 60 | var layer = new Konva.Layer(); 61 | var rect = new Konva.Rect({ 62 | x: 200, 63 | y: 100, 64 | width: 100, 65 | height: 50, 66 | fill: 'green', 67 | stroke: 'black', 68 | strokeWidth: 4, 69 | }); 70 | 71 | layer.add(rect); 72 | stage.add(layer); 73 | 74 | var draws = 0; 75 | 76 | layer.on('draw', function () { 77 | //console.log('draw') 78 | draws++; 79 | }); 80 | 81 | layer.draw(); 82 | layer.draw(); 83 | layer.draw(); 84 | 85 | assert.equal(draws, 3, 'draw count should be 3'); 86 | 87 | layer.batchDraw(); 88 | layer.batchDraw(); 89 | layer.batchDraw(); 90 | 91 | assert.notEqual(draws, 6, 'should not be 6 draws'); 92 | }); 93 | 94 | // ====================================================== 95 | it('stage batch draw', function () { 96 | var stage = addStage(); 97 | var layer = new Konva.Layer(); 98 | var rect = new Konva.Rect({ 99 | x: 200, 100 | y: 100, 101 | width: 100, 102 | height: 50, 103 | fill: 'green', 104 | stroke: 'black', 105 | strokeWidth: 4, 106 | }); 107 | 108 | layer.add(rect); 109 | stage.add(layer); 110 | 111 | var draws = 0; 112 | 113 | layer.on('draw', function () { 114 | //console.log('draw') 115 | draws++; 116 | }); 117 | 118 | stage.draw(); 119 | stage.draw(); 120 | stage.draw(); 121 | 122 | assert.equal(draws, 3, 'draw count should be 3'); 123 | 124 | stage.batchDraw(); 125 | stage.batchDraw(); 126 | stage.batchDraw(); 127 | 128 | assert.notEqual(draws, 6, 'should not be 6 draws'); 129 | }); 130 | }); 131 | -------------------------------------------------------------------------------- /test/unit/Arc-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | 3 | import { 4 | addStage, 5 | Konva, 6 | createCanvas, 7 | compareLayerAndCanvas, 8 | assertAlmostDeepEqual, 9 | } from './test-utils'; 10 | 11 | describe('Arc', function () { 12 | // ====================================================== 13 | it('add arc', function () { 14 | var stage = addStage(); 15 | var layer = new Konva.Layer(); 16 | var arc = new Konva.Arc({ 17 | x: 100, 18 | y: 100, 19 | innerRadius: 50, 20 | outerRadius: 80, 21 | angle: 90, 22 | fill: 'green', 23 | stroke: 'black', 24 | strokeWidth: 4, 25 | name: 'myArc', 26 | draggable: true, 27 | }); 28 | 29 | layer.add(arc); 30 | stage.add(layer); 31 | 32 | assert.equal(arc.getClassName(), 'Arc'); 33 | 34 | var trace = layer.getContext().getTrace(); 35 | //console.log(trace); 36 | assert.equal( 37 | trace, 38 | '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();' 39 | ); 40 | }); 41 | 42 | // ====================================================== 43 | it('attrs sync', function () { 44 | var stage = addStage(); 45 | var layer = new Konva.Layer(); 46 | var arc = new Konva.Arc({ 47 | x: 100, 48 | y: 100, 49 | innerRadius: 50, 50 | outerRadius: 80, 51 | angle: 90, 52 | fill: 'green', 53 | stroke: 'black', 54 | strokeWidth: 4, 55 | name: 'myArc', 56 | draggable: true, 57 | }); 58 | 59 | layer.add(arc); 60 | stage.add(layer); 61 | assert.equal(arc.getWidth(), 160); 62 | assert.equal(arc.getHeight(), 160); 63 | 64 | arc.setWidth(100); 65 | assert.equal(arc.outerRadius(), 50); 66 | assert.equal(arc.getHeight(), 100); 67 | 68 | arc.setHeight(120); 69 | assert.equal(arc.outerRadius(), 60); 70 | assert.equal(arc.getHeight(), 120); 71 | }); 72 | 73 | it('getSelfRect', function () { 74 | var stage = addStage(); 75 | var layer = new Konva.Layer(); 76 | var arc = new Konva.Arc({ 77 | x: 100, 78 | y: 100, 79 | innerRadius: 50, 80 | outerRadius: 80, 81 | angle: 90, 82 | fill: 'green', 83 | stroke: 'black', 84 | strokeWidth: 4, 85 | name: 'myArc', 86 | draggable: true, 87 | }); 88 | 89 | layer.add(arc); 90 | stage.add(layer); 91 | 92 | assertAlmostDeepEqual(arc.getSelfRect(), { 93 | x: 0, 94 | y: 0, 95 | width: 80, 96 | height: 80, 97 | }); 98 | }); 99 | 100 | it('getSelfRect on clockwise', function () { 101 | var stage = addStage(); 102 | var layer = new Konva.Layer(); 103 | var arc = new Konva.Arc({ 104 | x: 100, 105 | y: 100, 106 | innerRadius: 50, 107 | outerRadius: 80, 108 | angle: 90, 109 | fill: 'green', 110 | stroke: 'black', 111 | strokeWidth: 4, 112 | name: 'myArc', 113 | draggable: true, 114 | clockwise: true, 115 | }); 116 | 117 | layer.add(arc); 118 | stage.add(layer); 119 | 120 | assertAlmostDeepEqual(arc.getSelfRect(), { 121 | x: -80, 122 | y: -80, 123 | width: 160, 124 | height: 160, 125 | }); 126 | }); 127 | 128 | it('getSelfRect on quarter clockwise arc bounds to the visible part', function () { 129 | var stage = addStage(); 130 | var layer = new Konva.Layer(); 131 | var arc = new Konva.Arc({ 132 | x: 100, 133 | y: 100, 134 | innerRadius: 50, 135 | outerRadius: 80, 136 | angle: 270, 137 | strokeWidth: 4, 138 | clockwise: true, 139 | }); 140 | 141 | layer.add(arc); 142 | stage.add(layer); 143 | 144 | assertAlmostDeepEqual(arc.getSelfRect(), { 145 | x: 0, 146 | y: -80, 147 | width: 80, 148 | height: 80, 149 | }); 150 | }); 151 | 152 | it('getSelfRect on small angle arc should bounds to inner radius', function () { 153 | var stage = addStage(); 154 | var layer = new Konva.Layer(); 155 | var arc = new Konva.Arc({ 156 | x: 100, 157 | y: 100, 158 | innerRadius: 50, 159 | outerRadius: 80, 160 | angle: 60, 161 | strokeWidth: 4, 162 | }); 163 | 164 | layer.add(arc); 165 | stage.add(layer); 166 | 167 | assertAlmostDeepEqual(arc.getSelfRect(), { 168 | x: 25, 169 | y: 0, 170 | width: 55, 171 | height: 69.282032302755, 172 | }); 173 | }); 174 | 175 | it('cache', function () { 176 | var stage = addStage(); 177 | var layer = new Konva.Layer(); 178 | var arc = new Konva.Arc({ 179 | x: 100, 180 | y: 100, 181 | innerRadius: 50, 182 | outerRadius: 80, 183 | angle: 90, 184 | fill: 'green', 185 | stroke: 'black', 186 | strokeWidth: 4, 187 | }); 188 | 189 | layer.add(arc); 190 | stage.add(layer); 191 | 192 | var canvas = createCanvas(); 193 | var context = canvas.getContext('2d'); 194 | context.beginPath(); 195 | context.arc(100, 100, 80, 0, Math.PI / 2, false); 196 | context.arc(100, 100, 50, Math.PI / 2, 0, true); 197 | context.closePath(); 198 | context.fillStyle = 'green'; 199 | context.fill(); 200 | context.lineWidth = 4; 201 | context.stroke(); 202 | compareLayerAndCanvas(layer, canvas, 10); 203 | }); 204 | }); 205 | -------------------------------------------------------------------------------- /test/unit/AutoDraw-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | import { addStage, isNode, Konva } from './test-utils'; 3 | 4 | describe('AutoDraw', function () { 5 | // ====================================================== 6 | it('schedule draw on shape add/change/remove', function () { 7 | var stage = addStage(); 8 | var layer = new Konva.Layer(); 9 | stage.add(layer); 10 | 11 | let callCount = 0; 12 | layer.batchDraw = function () { 13 | callCount += 1; 14 | Konva.Layer.prototype.batchDraw.call(this); 15 | return layer; 16 | }; 17 | var circle = new Konva.Circle({ 18 | x: stage.width() / 2, 19 | y: stage.height() / 2, 20 | radius: 70, 21 | fill: 'green', 22 | stroke: 'black', 23 | strokeWidth: 4, 24 | name: 'myCircle', 25 | }); 26 | layer.add(circle); 27 | assert.equal(callCount, 1); 28 | circle.radius(50); 29 | assert.equal(callCount, 2); 30 | circle.destroy(); 31 | assert.equal(callCount, 3); 32 | }); 33 | 34 | // ====================================================== 35 | it('schedule draw on order change', function () { 36 | var stage = addStage(); 37 | var layer = new Konva.Layer(); 38 | stage.add(layer); 39 | 40 | var circle = new Konva.Circle({ 41 | x: stage.width() / 2, 42 | y: stage.height() / 2, 43 | radius: 70, 44 | fill: 'green', 45 | stroke: 'black', 46 | strokeWidth: 4, 47 | name: 'myCircle', 48 | }); 49 | layer.add(circle); 50 | 51 | var circle2 = new Konva.Circle({ 52 | x: stage.width() / 2, 53 | y: stage.height() / 2, 54 | radius: 70, 55 | fill: 'green', 56 | stroke: 'black', 57 | strokeWidth: 4, 58 | name: 'myCircle', 59 | }); 60 | layer.add(circle2); 61 | 62 | let callCount = 0; 63 | layer.batchDraw = function () { 64 | callCount += 1; 65 | Konva.Layer.prototype.batchDraw.call(this); 66 | return layer; 67 | }; 68 | 69 | circle.moveToTop(); 70 | assert.equal(callCount, 1); 71 | }); 72 | 73 | // ====================================================== 74 | it('schedules draw when calling removeChildren/destroyChildren', () => { 75 | var stage = addStage(); 76 | var layer = new Konva.Layer(); 77 | var group1 = new Konva.Group(); 78 | var group2 = new Konva.Group(); 79 | 80 | stage.add(layer); 81 | layer.add(group1); 82 | group1.add(new Konva.Circle()); 83 | layer.add(group2); 84 | group2.add(new Konva.Circle()); 85 | 86 | let callCount = 0; 87 | layer.batchDraw = function () { 88 | callCount += 1; 89 | Konva.Layer.prototype.batchDraw.call(this); 90 | return layer; 91 | }; 92 | 93 | group1.destroyChildren(); 94 | assert.equal(callCount, 1); 95 | group2.removeChildren(); 96 | assert.equal(callCount, 2); 97 | }); 98 | 99 | // ====================================================== 100 | it('schedule draw on cache', function () { 101 | var stage = addStage(); 102 | var layer = new Konva.Layer(); 103 | stage.add(layer); 104 | 105 | var circle = new Konva.Circle({ 106 | x: stage.width() / 2, 107 | y: stage.height() / 2, 108 | radius: 70, 109 | fill: 'green', 110 | stroke: 'black', 111 | strokeWidth: 4, 112 | name: 'myCircle', 113 | }); 114 | layer.add(circle); 115 | 116 | let callCount = 0; 117 | layer.batchDraw = function () { 118 | callCount += 1; 119 | Konva.Layer.prototype.batchDraw.call(this); 120 | return layer; 121 | }; 122 | 123 | circle.cache(); 124 | assert.equal(callCount, 1); 125 | 126 | circle.clearCache(); 127 | assert.equal(callCount, 2); 128 | }); 129 | 130 | // ====================================================== 131 | it('redraw for images', function (done) { 132 | // don't test on node, because of specific url access 133 | if (isNode) { 134 | return done(); 135 | } 136 | var stage = addStage(); 137 | var layer = new Konva.Layer(); 138 | stage.add(layer); 139 | 140 | const { src } = document.getElementById( 141 | 'darth-vader.jpg' 142 | ) as HTMLImageElement; 143 | 144 | const img = new Image(); 145 | img.src = src + '?'; // change url to reset cache 146 | const image = new Konva.Image({ 147 | image: img, 148 | }); 149 | layer.add(image); 150 | 151 | let callCount = 0; 152 | layer.batchDraw = function () { 153 | callCount += 1; 154 | Konva.Layer.prototype.batchDraw.call(this); 155 | return layer; 156 | }; 157 | 158 | img.onload = () => { 159 | assert.equal(callCount, 1); 160 | done(); 161 | }; 162 | }); 163 | }); 164 | -------------------------------------------------------------------------------- /test/unit/Blob-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | import { Line } from '../../src/shapes/Line'; 3 | 4 | import { addStage, Konva, cloneAndCompareLayer } from './test-utils'; 5 | 6 | describe('Blob', function () { 7 | // ====================================================== 8 | it('add blob', function () { 9 | var stage = addStage(); 10 | var layer = new Konva.Layer(); 11 | 12 | var blob = new Konva.Line({ 13 | points: [73, 140, 340, 23, 500, 109, 300, 170], 14 | stroke: 'blue', 15 | strokeWidth: 10, 16 | draggable: true, 17 | fill: '#aaf', 18 | tension: 0.8, 19 | closed: true, 20 | }); 21 | 22 | layer.add(blob); 23 | stage.add(layer); 24 | 25 | assert.equal(blob.tension(), 0.8); 26 | 27 | assert.equal(blob.getClassName(), 'Line'); 28 | 29 | //console.log(blob1.getPoints()) 30 | 31 | // test setter 32 | blob.tension(1.5); 33 | assert.equal(blob.tension(), 1.5); 34 | 35 | var trace = layer.getContext().getTrace(); 36 | assert.equal( 37 | trace, 38 | '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();' 39 | ); 40 | }); 41 | 42 | // ====================================================== 43 | it('define tension first', function () { 44 | var stage = addStage(); 45 | var layer = new Konva.Layer(); 46 | 47 | var blob = new Konva.Line({ 48 | tension: 0.8, 49 | points: [73, 140, 340, 23, 500, 109, 300, 170], 50 | stroke: 'blue', 51 | strokeWidth: 10, 52 | draggable: true, 53 | fill: '#aaf', 54 | closed: true, 55 | }); 56 | 57 | layer.add(blob); 58 | stage.add(layer); 59 | 60 | assert.equal(stage.findOne('Line').points().length, 8); 61 | }); 62 | 63 | // ====================================================== 64 | it('check for konva event handlers', function () { 65 | var stage = addStage(); 66 | var layer = new Konva.Layer(); 67 | 68 | var blob = new Konva.Line({ 69 | points: [73, 140, 340, 23, 500, 109, 300, 170], 70 | stroke: 'blue', 71 | strokeWidth: 10, 72 | draggable: true, 73 | fill: '#aaf', 74 | tension: 0.8, 75 | closed: true, 76 | }); 77 | 78 | layer.add(blob); 79 | 80 | stage.add(layer); 81 | 82 | assert.equal(blob.eventListeners.pointsChange[0].name, 'konva'); 83 | assert.equal(blob.eventListeners.tensionChange[0].name, 'konva'); 84 | 85 | // removing handlers should not remove konva specific handlers 86 | blob.off('pointsChange'); 87 | blob.off('tensionChange'); 88 | 89 | assert.equal(blob.eventListeners.pointsChange[0].name, 'konva'); 90 | assert.equal(blob.eventListeners.tensionChange[0].name, 'konva'); 91 | 92 | // you can force remove an event by adding the name 93 | blob.off('pointsChange.konva'); 94 | blob.off('tensionChange.konva'); 95 | 96 | assert.equal(blob.eventListeners.pointsChange, undefined); 97 | assert.equal(blob.eventListeners.tensionChange, undefined); 98 | }); 99 | 100 | it('cache', function () { 101 | var stage = addStage(); 102 | var layer = new Konva.Layer(); 103 | var blob = new Konva.Line({ 104 | x: 50, 105 | y: 50, 106 | points: [-25, 50, 250, -30, 150, 50, 250, 110], 107 | stroke: 'black', 108 | strokeWidth: 10, 109 | draggable: true, 110 | fill: '#aaf', 111 | tension: 0.3, 112 | closed: true, 113 | }); 114 | 115 | blob.cache(); 116 | layer.add(blob); 117 | stage.add(layer); 118 | 119 | cloneAndCompareLayer(layer, 150); 120 | }); 121 | }); 122 | -------------------------------------------------------------------------------- /test/unit/Canvas-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | import { addStage, Konva } from './test-utils'; 3 | 4 | describe('Canvas', function () { 5 | // ====================================================== 6 | it('pixel ratio', function () { 7 | var stage = addStage(); 8 | 9 | var layer = new Konva.Layer(); 10 | 11 | var circle = new Konva.Circle({ 12 | x: 100, 13 | y: 70, 14 | radius: 70, 15 | fill: 'green', 16 | stroke: 'blue', 17 | strokeWidth: 4, 18 | draggable: true, 19 | }); 20 | 21 | layer.add(circle); 22 | stage.add(layer); 23 | 24 | stage.width(578 / 2); 25 | stage.height(100); 26 | 27 | stage.draw(); 28 | assert.equal(layer.getCanvas().getPixelRatio(), Konva.pixelRatio); 29 | 30 | layer.getCanvas().setPixelRatio(1); 31 | assert.equal(layer.getCanvas().getPixelRatio(), 1); 32 | assert.equal(layer.getCanvas().width, 289); 33 | assert.equal(layer.getCanvas().height, 100); 34 | 35 | layer.getCanvas().setPixelRatio(2); 36 | assert.equal(layer.getCanvas().getPixelRatio(), 2); 37 | assert.equal(layer.getCanvas().width, 578); 38 | assert.equal(layer.getCanvas().height, 200); 39 | 40 | layer.draw(); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /test/unit/Context-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | import { addStage, Konva } from './test-utils'; 3 | 4 | describe('Context', function () { 5 | // ====================================================== 6 | var contextMethods = [ 7 | 'clearRect', 8 | 'fillRect', 9 | 'strokeRect', 10 | 'fillText', 11 | 'strokeText', 12 | 'measureText', 13 | 'createLinearGradient', 14 | 'createRadialGradient', 15 | 'createPattern', 16 | 'beginPath', 17 | 'closePath', 18 | 'moveTo', 19 | 'lineTo', 20 | 'bezierCurveTo', 21 | 'quadraticCurveTo', 22 | 'arc', 23 | 'arcTo', 24 | 'rect', 25 | 'roundRect', 26 | 'ellipse', 27 | 'fill', 28 | 'stroke', 29 | 'clip', 30 | 'isPointInPath', 31 | 'scale', 32 | 'rotate', 33 | 'translate', 34 | 'transform', 35 | 'setTransform', 36 | 'drawImage', 37 | 'createImageData', 38 | 'getImageData', 39 | 'putImageData', 40 | 'save', 41 | 'restore', 42 | ]; 43 | 44 | var contextProperties = [ 45 | 'fillStyle', 46 | 'strokeStyle', 47 | 'shadowColor', 48 | 'shadowBlur', 49 | 'shadowOffsetX', 50 | 'shadowOffsetY', 51 | 'lineCap', 52 | 'lineDashOffset', 53 | 'lineJoin', 54 | 'lineWidth', 55 | 'miterLimit', 56 | 'font', 57 | 'textAlign', 58 | 'textBaseline', 59 | 'globalAlpha', 60 | 'globalCompositeOperation', 61 | ] as const; 62 | 63 | it('context wrapper should work like native context', function () { 64 | var stage = addStage(); 65 | 66 | var layer = new Konva.Layer(); 67 | 68 | var circle = new Konva.Circle({ 69 | x: 100, 70 | y: 70, 71 | radius: 70, 72 | fill: 'green', 73 | stroke: 'blue', 74 | strokeWidth: 4, 75 | draggable: true, 76 | }); 77 | 78 | layer.add(circle); 79 | stage.add(layer); 80 | 81 | var context = layer.getContext(); 82 | var nativeContext = context._context; 83 | 84 | contextMethods.forEach(function (method) { 85 | assert.equal( 86 | typeof nativeContext[method], 87 | 'function', 88 | 'native context has no method ' + method 89 | ); 90 | assert.equal( 91 | typeof context[method], 92 | 'function', 93 | 'context wrapper has no method ' + method 94 | ); 95 | }); 96 | 97 | contextProperties.forEach(function (prop) { 98 | assert.equal( 99 | nativeContext[prop] !== undefined, 100 | true, 101 | 'native context has no property ' + prop 102 | ); 103 | assert.equal( 104 | context[prop] !== undefined, 105 | true, 106 | 'context wrapper has no property ' + prop 107 | ); 108 | }); 109 | 110 | // test get 111 | nativeContext.fillStyle = '#ff0000'; 112 | assert.equal(context.fillStyle, '#ff0000'); 113 | 114 | // test set 115 | context.globalAlpha = 0.5; 116 | assert.equal(context.globalAlpha, 0.5); 117 | }); 118 | }); 119 | -------------------------------------------------------------------------------- /test/unit/Ellipse-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | 3 | import { 4 | addStage, 5 | Konva, 6 | createCanvas, 7 | compareLayerAndCanvas, 8 | } from './test-utils'; 9 | 10 | describe('Ellipse', function () { 11 | // ====================================================== 12 | it('add ellipse', function () { 13 | var stage = addStage(); 14 | var layer = new Konva.Layer(); 15 | var ellipse = new Konva.Ellipse({ 16 | x: stage.width() / 2, 17 | y: stage.height() / 2, 18 | radiusX: 70, 19 | radiusY: 35, 20 | fill: 'green', 21 | stroke: 'black', 22 | strokeWidth: 8, 23 | }); 24 | layer.add(ellipse); 25 | stage.add(layer); 26 | assert.equal(ellipse.getClassName(), 'Ellipse'); 27 | 28 | var trace = layer.getContext().getTrace(); 29 | assert.equal( 30 | trace, 31 | '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();' 32 | ); 33 | }); 34 | 35 | // ====================================================== 36 | it('attrs sync', function () { 37 | var stage = addStage(); 38 | var layer = new Konva.Layer(); 39 | var ellipse = new Konva.Ellipse({ 40 | x: stage.width() / 2, 41 | y: stage.height() / 2, 42 | radiusX: 70, 43 | radiusY: 35, 44 | fill: 'green', 45 | stroke: 'black', 46 | strokeWidth: 8, 47 | }); 48 | layer.add(ellipse); 49 | stage.add(layer); 50 | 51 | assert.equal(ellipse.getWidth(), 140); 52 | assert.equal(ellipse.getHeight(), 70); 53 | 54 | ellipse.setWidth(100); 55 | assert.equal(ellipse.radiusX(), 50); 56 | assert.equal(ellipse.radiusY(), 35); 57 | 58 | ellipse.setHeight(120); 59 | assert.equal(ellipse.radiusX(), 50); 60 | assert.equal(ellipse.radiusY(), 60); 61 | }); 62 | 63 | it('getSelfRect', function () { 64 | var stage = addStage(); 65 | var layer = new Konva.Layer(); 66 | var ellipse = new Konva.Ellipse({ 67 | x: stage.width() / 2, 68 | y: stage.height() / 2, 69 | radiusX: 70, 70 | radiusY: 35, 71 | fill: 'green', 72 | stroke: 'black', 73 | strokeWidth: 8, 74 | }); 75 | layer.add(ellipse); 76 | stage.add(layer); 77 | 78 | assert.deepEqual(ellipse.getSelfRect(), { 79 | x: -70, 80 | y: -35, 81 | width: 140, 82 | height: 70, 83 | }); 84 | }); 85 | 86 | it('cache', function () { 87 | var stage = addStage(); 88 | var layer = new Konva.Layer(); 89 | var ellipse = new Konva.Ellipse({ 90 | x: stage.width() / 2, 91 | y: stage.height() / 2, 92 | radiusX: 70, 93 | radiusY: 35, 94 | fill: 'green', 95 | stroke: 'black', 96 | strokeWidth: 8, 97 | }); 98 | ellipse.cache(); 99 | layer.add(ellipse); 100 | stage.add(layer); 101 | 102 | var canvas = createCanvas(); 103 | var context = canvas.getContext('2d'); 104 | context.save(); 105 | context.beginPath(); 106 | context.scale(1, 0.5); 107 | context.arc(stage.width() / 2, stage.height(), 70, 0, Math.PI * 2, false); 108 | context.closePath(); 109 | context.restore(); 110 | context.fillStyle = 'green'; 111 | context.fill(); 112 | context.lineWidth = 8; 113 | context.stroke(); 114 | compareLayerAndCanvas(layer, canvas, 150); 115 | }); 116 | }); 117 | -------------------------------------------------------------------------------- /test/unit/Global-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | import { Konva } from './test-utils'; 3 | 4 | describe('Global', function () { 5 | // ====================================================== 6 | it('test Konva version number', function () { 7 | assert.equal(!!Konva.version, true); 8 | }); 9 | 10 | // ====================================================== 11 | it('getAngle()', function () { 12 | // test that default angleDeg is true 13 | assert.equal(Konva.angleDeg, true); 14 | assert.equal(Konva.getAngle(180), Math.PI); 15 | 16 | Konva.angleDeg = false; 17 | assert.equal(Konva.getAngle(1), 1); 18 | 19 | // set angleDeg back to true for future tests 20 | Konva.angleDeg = true; 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/unit/Group-test.ts: -------------------------------------------------------------------------------- 1 | import { addStage, cloneAndCompareLayer, Konva } from './test-utils'; 2 | import { assert } from 'chai'; 3 | 4 | describe('Group', function () { 5 | // ====================================================== 6 | it('cache group with text', function () { 7 | var stage = addStage(); 8 | 9 | var layer = new Konva.Layer(); 10 | var group = new Konva.Group({ 11 | draggable: true, 12 | x: 50, 13 | y: 40, 14 | }); 15 | var text = new Konva.Text({ 16 | text: 'some text', 17 | fontSize: 20, 18 | fill: 'black', 19 | y: 50, 20 | }); 21 | 22 | var rect = new Konva.Rect({ 23 | height: 100, 24 | width: 100, 25 | stroke: 'black', 26 | strokeWidth: 10, 27 | // cornerRadius: 1, 28 | }); 29 | group.add(text); 30 | group.add(rect); 31 | layer.add(group); 32 | 33 | stage.add(layer); 34 | 35 | group 36 | .cache({ 37 | x: -15, 38 | y: -15, 39 | width: 150, 40 | height: 150, 41 | }) 42 | .offsetX(5) 43 | .offsetY(5); 44 | 45 | layer.draw(); 46 | 47 | cloneAndCompareLayer(layer, 200); 48 | }); 49 | 50 | it('clip group with a Path2D', function () { 51 | var stage = addStage(); 52 | 53 | var layer = new Konva.Layer(); 54 | 55 | var path = new Konva.Group({ 56 | width: 100, 57 | height: 100, 58 | clipFunc: () => [new Path2D('M0 0v50h50Z')], 59 | }); 60 | 61 | layer.add(path); 62 | stage.add(layer); 63 | 64 | const trace = layer.getContext().getTrace(); 65 | 66 | assert.equal( 67 | trace, 68 | 'clearRect(0,0,578,200);save();transform(1,0,0,1,0,0);beginPath();clip([object Path2D]);transform(1,0,0,1,0,0);restore();' 69 | ); 70 | }); 71 | 72 | it('clip group with by zero size', function () { 73 | var stage = addStage(); 74 | 75 | var layer = new Konva.Layer(); 76 | 77 | var group = new Konva.Group({ 78 | width: 100, 79 | height: 100, 80 | clipWidth: 0, 81 | clipHeight: 0, 82 | }); 83 | 84 | layer.add(group); 85 | stage.add(layer); 86 | 87 | const trace = layer.getContext().getTrace(); 88 | 89 | console.log(trace); 90 | 91 | assert.equal( 92 | trace, 93 | 'clearRect(0,0,578,200);save();transform(1,0,0,1,0,0);beginPath();rect(0,0,0,0);clip();transform(1,0,0,1,0,0);restore();' 94 | ); 95 | }); 96 | 97 | it('clip group with a Path2D and clipRule', function () { 98 | var stage = addStage(); 99 | 100 | var layer = new Konva.Layer(); 101 | 102 | var path = new Konva.Group({ 103 | width: 100, 104 | height: 100, 105 | clipFunc: () => [new Path2D('M0 0v50h50Z'), 'evenodd'], 106 | }); 107 | 108 | layer.add(path); 109 | stage.add(layer); 110 | 111 | const trace = layer.getContext().getTrace(); 112 | 113 | assert.equal( 114 | trace, 115 | 'clearRect(0,0,578,200);save();transform(1,0,0,1,0,0);beginPath();clip([object Path2D],evenodd);transform(1,0,0,1,0,0);restore();' 116 | ); 117 | }); 118 | }); 119 | -------------------------------------------------------------------------------- /test/unit/Polygon-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | 3 | import { addStage, Konva } from './test-utils'; 4 | 5 | describe('Polygon', function () { 6 | it('add polygon', function () { 7 | var stage = addStage(); 8 | 9 | var layer = new Konva.Layer(); 10 | var points = [73, 192, 73, 160, 340, 23, 500, 109, 499, 139, 342, 93]; 11 | 12 | var poly = new Konva.Line({ 13 | points: points, 14 | fill: 'green', 15 | stroke: 'blue', 16 | strokeWidth: 5, 17 | closed: true, 18 | }); 19 | 20 | layer.add(poly); 21 | stage.add(layer); 22 | 23 | assert.equal(poly.getClassName(), 'Line'); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/unit/Ring-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | 3 | import { addStage, Konva, compareLayers } from './test-utils'; 4 | 5 | describe('Ring', function () { 6 | // ====================================================== 7 | it('add ring', function () { 8 | var stage = addStage(); 9 | var layer = new Konva.Layer(); 10 | var ring = new Konva.Ring({ 11 | x: stage.width() / 2, 12 | y: stage.height() / 2, 13 | innerRadius: 50, 14 | outerRadius: 90, 15 | fill: 'green', 16 | stroke: 'black', 17 | strokeWidth: 4, 18 | draggable: true, 19 | }); 20 | layer.add(ring); 21 | stage.add(layer); 22 | assert.equal(ring.getClassName(), 'Ring'); 23 | 24 | var trace = layer.getContext().getTrace(); 25 | assert.equal( 26 | trace, 27 | '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();' 28 | ); 29 | }); 30 | 31 | // ====================================================== 32 | it('ring attrs sync', function () { 33 | var stage = addStage(); 34 | var layer = new Konva.Layer(); 35 | var ring = new Konva.Ring({ 36 | name: 'ring', 37 | x: 30, 38 | y: 50, 39 | innerRadius: 15, 40 | outerRadius: 30, 41 | fill: 'green', 42 | stroke: 'black', 43 | strokeWidth: 4, 44 | draggable: true, 45 | }); 46 | layer.add(ring); 47 | stage.add(layer); 48 | 49 | assert(ring.width(), 60); 50 | assert(ring.height(), 60); 51 | 52 | ring.height(100); 53 | assert(ring.width(), 100); 54 | assert(ring.outerRadius(), 50); 55 | 56 | ring.width(120); 57 | assert(ring.height(), 120); 58 | assert(ring.outerRadius(), 60); 59 | }); 60 | 61 | it('ring cache', function () { 62 | var stage = addStage(); 63 | var layer = new Konva.Layer(); 64 | var ring = new Konva.Ring({ 65 | name: 'ring', 66 | x: 30, 67 | y: 50, 68 | innerRadius: 15, 69 | outerRadius: 30, 70 | fill: 'green', 71 | stroke: 'black', 72 | strokeWidth: 4, 73 | draggable: true, 74 | }); 75 | 76 | layer.add(ring); 77 | stage.add(layer); 78 | 79 | assert.deepEqual(ring.getSelfRect(), { 80 | x: -30, 81 | y: -30, 82 | width: 60, 83 | height: 60, 84 | }); 85 | 86 | var layer2 = layer.clone(); 87 | stage.add(layer2); 88 | layer2.hide(); 89 | 90 | compareLayers(layer, layer2); 91 | }); 92 | }); 93 | -------------------------------------------------------------------------------- /test/unit/Spline-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | 3 | import { addStage, Konva } from './test-utils'; 4 | 5 | describe('Spline', function () { 6 | // ====================================================== 7 | it('add splines', function () { 8 | var stage = addStage(); 9 | var layer = new Konva.Layer(); 10 | 11 | var line1 = new Konva.Line({ 12 | points: [73, 160, 340, 23, 500, 109, 300, 109], 13 | stroke: 'blue', 14 | strokeWidth: 10, 15 | lineCap: 'round', 16 | lineJoin: 'round', 17 | draggable: true, 18 | tension: 1, 19 | }); 20 | 21 | var line2 = new Konva.Line({ 22 | points: [73, 160, 340, 23, 500, 109], 23 | stroke: 'red', 24 | strokeWidth: 10, 25 | lineCap: 'round', 26 | lineJoin: 'round', 27 | draggable: true, 28 | tension: 1, 29 | }); 30 | 31 | var line3 = new Konva.Line({ 32 | points: [73, 160, 340, 23], 33 | stroke: 'green', 34 | strokeWidth: 10, 35 | lineCap: 'round', 36 | lineJoin: 'round', 37 | draggable: true, 38 | tension: 1, 39 | }); 40 | 41 | layer.add(line1); 42 | layer.add(line2); 43 | layer.add(line3); 44 | stage.add(layer); 45 | 46 | assert.equal(line1.getClassName(), 'Line'); 47 | 48 | var trace = layer.getContext().getTrace(); 49 | 50 | //console.log(trace); 51 | assert.equal( 52 | trace, 53 | '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();' 54 | ); 55 | }); 56 | 57 | // ====================================================== 58 | it('update spline points', function () { 59 | var stage = addStage(); 60 | var layer = new Konva.Layer(); 61 | 62 | var spline = new Konva.Line({ 63 | points: [73, 160, 340, 23, 500, 109, 300, 109], 64 | stroke: 'blue', 65 | strokeWidth: 10, 66 | lineCap: 'round', 67 | lineJoin: 'round', 68 | draggable: true, 69 | tension: 1, 70 | }); 71 | 72 | layer.add(spline); 73 | stage.add(layer); 74 | 75 | assert.equal(spline.getTensionPoints().length, 12); 76 | 77 | spline.points([73, 160, 340, 23, 500, 109]); 78 | 79 | assert.equal(spline.getTensionPoints().length, 6); 80 | 81 | layer.draw(); 82 | }); 83 | 84 | // ====================================================== 85 | it('add point to spline points', function () { 86 | var stage = addStage(); 87 | var layer = new Konva.Layer(); 88 | 89 | var spline = new Konva.Line({ 90 | points: [73, 160, 340, 23, 500, 109, 300, 109], 91 | stroke: 'blue', 92 | strokeWidth: 10, 93 | lineCap: 'round', 94 | lineJoin: 'round', 95 | draggable: true, 96 | tension: 1, 97 | }); 98 | 99 | layer.add(spline); 100 | stage.add(layer); 101 | 102 | assert.equal(spline.points().length, 8); 103 | 104 | var points = spline.points(); 105 | points.push(300); 106 | points.push(200); 107 | spline.clearCache(); 108 | 109 | assert.equal(spline.points().length, 10); 110 | 111 | layer.draw(); 112 | }); 113 | }); 114 | -------------------------------------------------------------------------------- /test/unit/Star-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | 3 | import { addStage, Konva, cloneAndCompareLayer } from './test-utils'; 4 | 5 | describe('Star', function () { 6 | // ====================================================== 7 | it('add five point star', function () { 8 | var stage = addStage(); 9 | 10 | var layer = new Konva.Layer(); 11 | 12 | var star = new Konva.Star({ 13 | x: 200, 14 | y: 100, 15 | numPoints: 5, 16 | innerRadius: 40, 17 | outerRadius: 70, 18 | fill: 'green', 19 | stroke: 'blue', 20 | strokeWidth: 5, 21 | name: 'foobar', 22 | center: { 23 | x: 0, 24 | y: -70, 25 | }, 26 | scale: { 27 | x: 0.5, 28 | y: 0.5, 29 | }, 30 | }); 31 | 32 | layer.add(star); 33 | stage.add(layer); 34 | 35 | assert.equal(star.getClassName(), 'Star'); 36 | }); 37 | 38 | // ====================================================== 39 | it('add star with line join and shadow', function () { 40 | var stage = addStage(); 41 | var layer = new Konva.Layer(); 42 | 43 | var rect = new Konva.Rect({ 44 | x: 250, 45 | y: 75, 46 | width: 100, 47 | height: 100, 48 | fill: 'red', 49 | }); 50 | 51 | var star = new Konva.Star({ 52 | x: 200, 53 | y: 100, 54 | numPoints: 5, 55 | innerRadius: 40, 56 | outerRadius: 70, 57 | fill: 'green', 58 | stroke: 'blue', 59 | strokeWidth: 5, 60 | lineJoin: 'round', 61 | shadowColor: 'black', 62 | shadowBlur: 10, 63 | shadowOffset: { x: 20, y: 20 }, 64 | shadowOpacity: 0.5, 65 | draggable: true, 66 | }); 67 | 68 | layer.add(rect); 69 | layer.add(star); 70 | 71 | stage.add(layer); 72 | 73 | assert.equal(star.lineJoin(), 'round'); 74 | star.lineJoin('bevel'); 75 | assert.equal(star.lineJoin(), 'bevel'); 76 | 77 | star.lineJoin('round'); 78 | }); 79 | 80 | // ====================================================== 81 | it('attr sync', function () { 82 | var stage = addStage(); 83 | var layer = new Konva.Layer(); 84 | 85 | var star = new Konva.Star({ 86 | x: 200, 87 | y: 100, 88 | numPoints: 5, 89 | innerRadius: 30, 90 | outerRadius: 50, 91 | fill: 'green', 92 | stroke: 'blue', 93 | strokeWidth: 5, 94 | lineJoin: 'round', 95 | shadowColor: 'black', 96 | shadowBlur: 10, 97 | shadowOffset: { x: 20, y: 20 }, 98 | shadowOpacity: 0.5, 99 | draggable: true, 100 | }); 101 | 102 | layer.add(star); 103 | 104 | stage.add(layer); 105 | 106 | assert.equal(star.getWidth(), 100); 107 | assert.equal(star.getHeight(), 100); 108 | 109 | star.setWidth(120); 110 | assert.equal(star.outerRadius(), 60); 111 | assert.equal(star.getHeight(), 120); 112 | 113 | star.setHeight(140); 114 | assert.equal(star.outerRadius(), 70); 115 | assert.equal(star.getHeight(), 140); 116 | }); 117 | 118 | // ====================================================== 119 | it('star cache', function () { 120 | var stage = addStage(); 121 | var layer = new Konva.Layer(); 122 | 123 | var star = new Konva.Star({ 124 | x: 200, 125 | y: 100, 126 | numPoints: 5, 127 | innerRadius: 30, 128 | outerRadius: 50, 129 | fill: 'green', 130 | stroke: 'black', 131 | strokeWidth: 5, 132 | lineJoin: 'round', 133 | shadowColor: 'black', 134 | shadowBlur: 10, 135 | shadowOffset: { x: 20, y: 20 }, 136 | shadowOpacity: 0.5, 137 | draggable: true, 138 | }); 139 | 140 | layer.add(star); 141 | 142 | stage.add(layer); 143 | star.cache(); 144 | 145 | assert.deepEqual(star.getSelfRect(), { 146 | x: -50, 147 | y: -50, 148 | height: 100, 149 | width: 100, 150 | }); 151 | cloneAndCompareLayer(layer, 100); 152 | }); 153 | }); 154 | -------------------------------------------------------------------------------- /test/unit/Util-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | import { Konva } from './test-utils'; 3 | 4 | describe('Util', function () { 5 | it('test _prepareToStringify', function () { 6 | var o: any = { 7 | a: 1, 8 | b: 'string1', 9 | }; 10 | o.c = { 11 | d: 'string2', 12 | e: o, 13 | f: global.document ? global.document.createElement('p') : { nodeType: 1 }, 14 | }; 15 | o.g = o; 16 | 17 | assert.deepEqual(Konva.Util._prepareToStringify(o), { 18 | a: 1, 19 | b: 'string1', 20 | c: { 21 | d: 'string2', 22 | }, 23 | }); 24 | }); 25 | 26 | it('colorToRGBA() - from HSL to RGBA conversion', function () { 27 | assert.deepEqual(Konva.Util.colorToRGBA('hsl(0, 0%, 0%)'), { 28 | r: 0, 29 | g: 0, 30 | b: 0, 31 | a: 1, 32 | }); 33 | 34 | assert.deepEqual(Konva.Util.colorToRGBA('hsl(96, 48%, 59%)'), { 35 | r: 140, 36 | g: 201, 37 | b: 100, 38 | a: 1, 39 | }); 40 | 41 | assert.deepEqual(Konva.Util.colorToRGBA('hsl(200, 100%, 70%)'), { 42 | r: 102, 43 | g: 204, 44 | b: 255, 45 | a: 1, 46 | }); 47 | }); 48 | 49 | it('colorToRGBA() - from color string with percentage to RGBA conversion!', function () { 50 | assert.deepEqual(Konva.Util.colorToRGBA('rgba(50, 100, 150, 0.5)'), { 51 | r: 50, 52 | g: 100, 53 | b: 150, 54 | a: 0.5, 55 | }); 56 | 57 | assert.deepEqual(Konva.Util.colorToRGBA('rgba(50, 100, 150, 50%)'), { 58 | r: 50, 59 | g: 100, 60 | b: 150, 61 | a: 0.5, 62 | }); 63 | 64 | assert.deepEqual(Konva.Util.colorToRGBA('rgba(25%, 50%, 100%, 0.5)'), { 65 | r: 63.75, 66 | g: 127.5, 67 | b: 255, 68 | a: 0.5, 69 | }); 70 | 71 | assert.deepEqual(Konva.Util.colorToRGBA('rgba(0%, 50%, 100%, 100%)'), { 72 | r: 0, 73 | g: 127.5, 74 | b: 255, 75 | a: 1, 76 | }); 77 | }); 78 | 79 | it('colorToRGBA() - from hex color string with percentage to RGBA conversion!', function () { 80 | assert.deepEqual(Konva.Util.colorToRGBA('#F00'), { 81 | r: 255, 82 | g: 0, 83 | b: 0, 84 | a: 1, 85 | }); 86 | 87 | assert.deepEqual(Konva.Util.colorToRGBA('#F00F'), { 88 | r: 255, 89 | g: 0, 90 | b: 0, 91 | a: 1, 92 | }); 93 | 94 | assert.deepEqual(Konva.Util.colorToRGBA('#F00C'), { 95 | r: 255, 96 | g: 0, 97 | b: 0, 98 | a: 0.8, 99 | }); 100 | 101 | assert.deepEqual(Konva.Util.colorToRGBA('#FF0000FF'), { 102 | r: 255, 103 | g: 0, 104 | b: 0, 105 | a: 1, 106 | }); 107 | 108 | assert.deepEqual(Konva.Util.colorToRGBA('#FF0000CC'), { 109 | r: 255, 110 | g: 0, 111 | b: 0, 112 | a: 0.8, 113 | }); 114 | }); 115 | 116 | it('make sure Transform is exported', () => { 117 | assert.equal(!!Konva.Transform, true); 118 | }); 119 | }); 120 | -------------------------------------------------------------------------------- /test/unit/Wedge-test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | 3 | import { addStage, Konva } from './test-utils'; 4 | 5 | describe('Wedge', function () { 6 | // ====================================================== 7 | it('add wedge', function () { 8 | var stage = addStage(); 9 | var layer = new Konva.Layer(); 10 | var wedge = new Konva.Wedge({ 11 | x: 100, 12 | y: 100, 13 | radius: 70, 14 | angle: 180 * 0.4, 15 | fill: 'green', 16 | stroke: 'black', 17 | strokeWidth: 4, 18 | name: 'myCircle', 19 | draggable: true, 20 | }); 21 | 22 | layer.add(wedge); 23 | stage.add(layer); 24 | 25 | assert.equal(wedge.getClassName(), 'Wedge'); 26 | 27 | var trace = layer.getContext().getTrace(); 28 | //console.log(trace); 29 | assert.equal( 30 | trace, 31 | '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();' 32 | ); 33 | }); 34 | 35 | it('attrs sync', function () { 36 | var stage = addStage(); 37 | var layer = new Konva.Layer(); 38 | var wedge = new Konva.Wedge({ 39 | x: stage.width() / 2, 40 | y: stage.height() / 2, 41 | angle: 180 * 0.4, 42 | radius: 70, 43 | fill: 'green', 44 | stroke: 'black', 45 | strokeWidth: 4, 46 | }); 47 | 48 | layer.add(wedge); 49 | stage.add(layer); 50 | 51 | assert.equal(wedge.getWidth(), 140); 52 | assert.equal(wedge.getHeight(), 140); 53 | 54 | wedge.setWidth(100); 55 | assert.equal(wedge.radius(), 50); 56 | assert.equal(wedge.getHeight(), 100); 57 | 58 | wedge.setHeight(120); 59 | assert.equal(wedge.radius(), 60); 60 | assert.equal(wedge.getHeight(), 120); 61 | }); 62 | 63 | it('getSelfRect', function () { 64 | var stage = addStage(); 65 | var layer = new Konva.Layer(); 66 | var wedge = new Konva.Wedge({ 67 | x: stage.width() / 2, 68 | y: stage.height() / 2, 69 | angle: 180 * 0.4, 70 | radius: 70, 71 | fill: 'green', 72 | stroke: 'black', 73 | strokeWidth: 4, 74 | }); 75 | 76 | layer.add(wedge); 77 | stage.add(layer); 78 | 79 | assert.deepEqual(wedge.getSelfRect(), { 80 | x: -70, 81 | y: -70, 82 | width: 140, 83 | height: 140, 84 | }); 85 | }); 86 | }); 87 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "lib", 4 | "module": "CommonJS", 5 | "target": "ES2018", 6 | // "sourceMap": true, 7 | "noEmitOnError": true, 8 | "lib": ["ES2019", "dom"], 9 | "moduleResolution": "node", 10 | "declaration": true, 11 | "removeComments": false, 12 | 13 | "strict": true, 14 | "noImplicitAny": false, 15 | "noImplicitThis": false, 16 | "useUnknownInCatchVariables": false, 17 | "skipLibCheck": true, 18 | // probably we would never enable this one 19 | // because it's too strict, konva generates many functions on the runtime 20 | "strictPropertyInitialization": false, 21 | 22 | }, 23 | "include": ["./src/**/*.ts"], 24 | } 25 | -------------------------------------------------------------------------------- /tsconfig.testing.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "lib", 4 | "target": "ES2015", 5 | "noEmitOnError": true, 6 | "moduleResolution": "node", 7 | "lib": ["ES2015", "dom"] 8 | }, 9 | "include": ["./src/**/*.ts"] 10 | } 11 | --------------------------------------------------------------------------------