mirror of
https://github.com/konvajs/konva.git
synced 2025-06-28 15:23:44 +08:00
Compare commits
88 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
27fc1b1ba5 | ||
![]() |
cdd61d7179 | ||
![]() |
f00fd70756 | ||
![]() |
faa2203da7 | ||
![]() |
3442071bdd | ||
![]() |
9c59566a9b | ||
![]() |
ca9be838a8 | ||
![]() |
09f2838d43 | ||
![]() |
a4980fccdc | ||
![]() |
35873586db | ||
![]() |
88e3d2a088 | ||
![]() |
791a786c81 | ||
![]() |
a29157a528 | ||
![]() |
dc8b9df1f5 | ||
![]() |
1ff1885de9 | ||
![]() |
3d5a216f23 | ||
![]() |
2e08f7319f | ||
![]() |
e12365f14f | ||
![]() |
fe80a44407 | ||
![]() |
8211db3233 | ||
![]() |
f32a416e03 | ||
![]() |
fd77f305d1 | ||
![]() |
53a4b494ee | ||
![]() |
9f5a7f0e2d | ||
![]() |
4eddaf813d | ||
![]() |
e9c4387159 | ||
![]() |
244fb2448a | ||
![]() |
bdd43b239c | ||
![]() |
1fb391d551 | ||
![]() |
224e60e32d | ||
![]() |
bc0e4edc07 | ||
![]() |
acb19bd552 | ||
![]() |
2f5f0557cc | ||
![]() |
538c422f9d | ||
![]() |
48ce5e9712 | ||
![]() |
3e8f0dff8f | ||
![]() |
bd5482766a | ||
![]() |
13fc7da406 | ||
![]() |
dd19e32ba8 | ||
![]() |
ff8632e099 | ||
![]() |
f8d2d0a624 | ||
![]() |
3d561edf37 | ||
![]() |
cb98acd0e3 | ||
![]() |
56a8ca7cdc | ||
![]() |
88578bae63 | ||
![]() |
8a9933484c | ||
![]() |
c2942a8927 | ||
![]() |
9b989b41aa | ||
![]() |
9e790a36ee | ||
![]() |
6e28a2a93b | ||
![]() |
10f382d465 | ||
![]() |
52b48fb662 | ||
![]() |
765258d69f | ||
![]() |
adba0073e3 | ||
![]() |
f2ed14fa08 | ||
![]() |
c7d11caa29 | ||
![]() |
fe32305d6d | ||
![]() |
c23e8f9b6b | ||
![]() |
a94b63f3ec | ||
![]() |
535edd8145 | ||
![]() |
682dbdb47f | ||
![]() |
96bbbc4b72 | ||
![]() |
7e174b075c | ||
![]() |
a8395cad5c | ||
![]() |
855e7fa40d | ||
![]() |
de0503814b | ||
![]() |
505348099d | ||
![]() |
3e092392cc | ||
![]() |
9e39485e00 | ||
![]() |
a24b30b110 | ||
![]() |
09445e0880 | ||
![]() |
13f88d6b72 | ||
![]() |
cbbc9921b6 | ||
![]() |
5ca5fd06f6 | ||
![]() |
09485ef1db | ||
![]() |
be24e9bb8b | ||
![]() |
4009e6bd11 | ||
![]() |
00ee0846d5 | ||
![]() |
14118054f6 | ||
![]() |
9322bfaa80 | ||
![]() |
db71b4ba28 | ||
![]() |
e1edd1241f | ||
![]() |
3ba489fc09 | ||
![]() |
338696c691 | ||
![]() |
ae4f53b422 | ||
![]() |
e47d54889c | ||
![]() |
28659eeaf2 | ||
![]() |
9c41a6eed1 |
2
.github/ISSUE_TEMPLATE.md
vendored
2
.github/ISSUE_TEMPLATE.md
vendored
@ -4,4 +4,4 @@ Please make sure to check current open and closed issues to see if your question
|
|||||||
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).
|
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).
|
||||||
|
|
||||||
If you have a bug, please, try to create a reproducible example with jsfiddle (or any similar service).
|
If you have a bug, please, try to create a reproducible example with jsfiddle (or any similar service).
|
||||||
You can use [this JSBIN](https://jsbin.com/tekehizuta/1/edit?html,js,output) as a template.
|
You can use [this JSBIN](https://jsbin.com/necojavuma/edit?js,output) as a template.
|
||||||
|
2
.github/workflows/test-browser.yml
vendored
2
.github/workflows/test-browser.yml
vendored
@ -12,7 +12,7 @@ jobs:
|
|||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [16.x]
|
node-version: [20.x]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
2
.github/workflows/test-node.yml
vendored
2
.github/workflows/test-node.yml
vendored
@ -12,7 +12,7 @@ jobs:
|
|||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [16.x]
|
node-version: [23.x]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -19,6 +19,7 @@ src_old
|
|||||||
types
|
types
|
||||||
out.png
|
out.png
|
||||||
cmj
|
cmj
|
||||||
|
.test-temp
|
||||||
|
|
||||||
# Numerous always-ignore extensions
|
# Numerous always-ignore extensions
|
||||||
*.diff
|
*.diff
|
||||||
|
27
CHANGELOG.md
27
CHANGELOG.md
@ -3,6 +3,33 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
|
## 9.3.21 (not released)
|
||||||
|
|
||||||
|
- Fixed memory leaks on Tween destroy
|
||||||
|
- Fixed incorrect export of stage/layer when internal nodes used buffer canvas for rendering
|
||||||
|
- Fixed incorrect render of cached node when buffer canvas is used
|
||||||
|
- Fixed incorrect path lenth calculations
|
||||||
|
|
||||||
|
## 9.3.20 (2025-03-20)
|
||||||
|
|
||||||
|
- Fix text rendering when ellipses are used
|
||||||
|
|
||||||
|
## 9.3.19 (2025-03-12)
|
||||||
|
|
||||||
|
- Typescript fixes
|
||||||
|
- Memory leak fixes
|
||||||
|
|
||||||
|
## 9.3.18 (2024-12-23)
|
||||||
|
|
||||||
|
- Fixed emoji split in multiple lines
|
||||||
|
|
||||||
|
## 9.3.17 (2024-12-23)
|
||||||
|
|
||||||
|
- Fixed `Arrow.getClientRect()`
|
||||||
|
- Fixed emoji rendering with letterSpacing
|
||||||
|
- Fixed line-through for justify text
|
||||||
|
- Changes in letter spacing width calculations to match DOM rendering
|
||||||
|
|
||||||
## 9.3.16 (2024-10-21)
|
## 9.3.16 (2024-10-21)
|
||||||
|
|
||||||
- Fix freeze on ios on touch cancel event
|
- Fix freeze on ios on touch cancel event
|
||||||
|
@ -16,7 +16,7 @@ You can draw things onto the stage, add event listeners to them, move them, scal
|
|||||||
This repository began as a GitHub fork of [ericdrowell/KineticJS](https://github.com/ericdrowell/KineticJS).
|
This repository began as a GitHub fork of [ericdrowell/KineticJS](https://github.com/ericdrowell/KineticJS).
|
||||||
|
|
||||||
- **Visit:** The [Home Page](http://konvajs.org/) and follow on [Twitter](https://twitter.com/lavrton)
|
- **Visit:** The [Home Page](http://konvajs.org/) and follow on [Twitter](https://twitter.com/lavrton)
|
||||||
- **Discover:** [Tutorials](http://konvajs.org/docs), [API Documentation](http://konvajs.org/api)
|
- **Discover:** [Tutorials](http://konvajs.org/docs/index.html), [API Documentation](http://konvajs.org/api/Konva.html)
|
||||||
- **Help:** [StackOverflow](http://stackoverflow.com/questions/tagged/konvajs), [Discord Chat](https://discord.gg/8FqZwVT)
|
- **Help:** [StackOverflow](http://stackoverflow.com/questions/tagged/konvajs), [Discord Chat](https://discord.gg/8FqZwVT)
|
||||||
|
|
||||||
# Quick Look
|
# Quick Look
|
||||||
|
3
eslint.config.mjs
Normal file
3
eslint.config.mjs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import tseslint from 'typescript-eslint';
|
||||||
|
|
||||||
|
export default tseslint.config(...tseslint.configs.recommended);
|
32
package.json
32
package.json
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "konva",
|
"name": "konva",
|
||||||
"version": "9.3.16",
|
"version": "9.3.20",
|
||||||
|
"description": "HTML5 2d canvas library.",
|
||||||
"author": "Anton Lavrenov",
|
"author": "Anton Lavrenov",
|
||||||
"files": [
|
"files": [
|
||||||
"README.md",
|
"README.md",
|
||||||
@ -16,12 +17,13 @@
|
|||||||
"start": "npm run test:watch",
|
"start": "npm run test:watch",
|
||||||
"compile": "npm run clean && npm run tsc && cp ./src/index-types.d.ts ./lib/index-types.d.ts && npm run rollup",
|
"compile": "npm run clean && npm run tsc && cp ./src/index-types.d.ts ./lib/index-types.d.ts && npm run rollup",
|
||||||
"build": "npm run compile && cp ./src/index-types.d.ts ./lib && gulp build && node ./rename-imports.mjs",
|
"build": "npm run compile && cp ./src/index-types.d.ts ./lib && gulp build && node ./rename-imports.mjs",
|
||||||
"test:import": "npm run build && node ./test/import-test.cjs &&node ./test/import-test.mjs",
|
"test:import": "npm run build && node ./test/import-test.cjs && node ./test/import-test.mjs",
|
||||||
"test": "npm run test:browser && npm run test:node",
|
"test": "npm run test:browser && npm run test:node",
|
||||||
"test:build": "parcel build ./test/unit-tests.html --dist-dir ./test-build --target none --public-url ./ --no-source-maps",
|
"test:build": "PARCEL_WORKER_BACKEND=process parcel build ./test/unit-tests.html --dist-dir ./test-build --target none --public-url ./ --no-source-maps",
|
||||||
"test:browser": "npm run test:build && mocha-headless-chrome -f ./test-build/unit-tests.html -a disable-web-security",
|
"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",
|
||||||
"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",
|
"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",
|
||||||
"test:node": "ts-mocha -r ./test/node-global-setup.mjs -p ./test/tsconfig.json test/unit/**/*.ts --exit && npm run test:import",
|
"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",
|
||||||
|
"test:node": "npm run test:node:compiled",
|
||||||
"tsc": "tsc --removeComments",
|
"tsc": "tsc --removeComments",
|
||||||
"rollup": "rollup -c --bundleConfigAsCjs",
|
"rollup": "rollup -c --bundleConfigAsCjs",
|
||||||
"clean": "rm -rf ./lib && rm -rf ./types && rm -rf ./cmj && rm -rf ./test-build",
|
"clean": "rm -rf ./lib && rm -rf ./types && rm -rf ./cmj && rm -rf ./test-build",
|
||||||
@ -59,13 +61,13 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@parcel/transformer-image": "2.10.1",
|
"@parcel/transformer-image": "2.13.2",
|
||||||
"@size-limit/preset-big-lib": "^11.0.1",
|
"@size-limit/preset-big-lib": "^11.1.6",
|
||||||
"@types/mocha": "^10.0.6",
|
"@types/mocha": "^10.0.10",
|
||||||
"canvas": "^2.11.2",
|
"canvas": "^3.1.0",
|
||||||
"chai": "4.3.10",
|
"chai": "5.1.2",
|
||||||
"filehound": "^1.17.6",
|
"filehound": "^1.17.6",
|
||||||
"gulp": "^4.0.2",
|
"gulp": "^5.0.0",
|
||||||
"gulp-concat": "^2.6.1",
|
"gulp-concat": "^2.6.1",
|
||||||
"gulp-connect": "^5.7.0",
|
"gulp-connect": "^5.7.0",
|
||||||
"gulp-exec": "^5.0.0",
|
"gulp-exec": "^5.0.0",
|
||||||
@ -78,14 +80,14 @@
|
|||||||
"gulp-util": "^3.0.8",
|
"gulp-util": "^3.0.8",
|
||||||
"mocha": "10.2.0",
|
"mocha": "10.2.0",
|
||||||
"mocha-headless-chrome": "^4.0.0",
|
"mocha-headless-chrome": "^4.0.0",
|
||||||
"parcel": "2.10.1",
|
"parcel": "2.13.3",
|
||||||
"process": "^0.11.10",
|
"process": "^0.11.10",
|
||||||
"rollup": "^4.9.1",
|
"rollup": "^4.31.0",
|
||||||
"rollup-plugin-typescript2": "^0.36.0",
|
"rollup-plugin-typescript2": "^0.36.0",
|
||||||
"size-limit": "^11.0.1",
|
"size-limit": "^11.1.6",
|
||||||
"ts-mocha": "^10.0.0",
|
"ts-mocha": "^10.0.0",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.7.3"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"canvas",
|
"canvas",
|
||||||
|
18
release.sh
18
release.sh
@ -49,7 +49,7 @@ echo "build for $1"
|
|||||||
npm run build >/dev/null
|
npm run build >/dev/null
|
||||||
git commit -am "build for $1" --allow-empty >/dev/null
|
git commit -am "build for $1" --allow-empty >/dev/null
|
||||||
|
|
||||||
echo "update CDN link in REAME"
|
echo "update CDN link in README"
|
||||||
perl -i -pe "s|${old_cdn_min}|${new_cdn_min}|g" ./README.md >/dev/null
|
perl -i -pe "s|${old_cdn_min}|${new_cdn_min}|g" ./README.md >/dev/null
|
||||||
git commit -am "update cdn link" --allow-empty >/dev/null
|
git commit -am "update cdn link" --allow-empty >/dev/null
|
||||||
|
|
||||||
@ -61,17 +61,17 @@ git push >/dev/null
|
|||||||
git push --tags >/dev/null
|
git push --tags >/dev/null
|
||||||
npm publish
|
npm publish
|
||||||
|
|
||||||
echo "copy konva.js into konva-site"
|
# echo "copy konva.js into konva-site"
|
||||||
cp ./konva.js ../konva-site/
|
# cp ./konva.js ../konva-site/
|
||||||
cd ../konva-site
|
# cd ../konva-site
|
||||||
|
|
||||||
echo "replace CDN links"
|
# echo "replace CDN links"
|
||||||
|
|
||||||
|
|
||||||
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
|
# 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
|
||||||
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
|
# 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
|
||||||
|
|
||||||
echo "regenerate site"
|
# echo "regenerate site"
|
||||||
./deploy.sh >/dev/null
|
# ./deploy.sh >/dev/null
|
||||||
|
|
||||||
echo "DONE!"
|
echo "DONE!"
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// import resolve from 'rollup-plugin-node-resolve';
|
// import resolve from 'rollup-plugin-node-resolve';
|
||||||
import typescript from 'rollup-plugin-typescript2';
|
import typescript from 'rollup-plugin-typescript2';
|
||||||
|
|
||||||
const pkg = require('./package.json');
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
input: `src/index.ts`,
|
input: `src/index.ts`,
|
||||||
output: [
|
output: [
|
@ -1,8 +1,6 @@
|
|||||||
import { Util } from './Util';
|
import { Util } from './Util';
|
||||||
import { SceneContext, HitContext, Context } from './Context';
|
import { SceneContext, HitContext, Context } from './Context';
|
||||||
import { Konva } from './Global';
|
import { Konva } from './Global';
|
||||||
import { Factory } from './Factory';
|
|
||||||
import { getNumberValidator } from './Validators';
|
|
||||||
|
|
||||||
// calculate pixel ratio
|
// calculate pixel ratio
|
||||||
let _pixelRatio;
|
let _pixelRatio;
|
||||||
@ -82,9 +80,32 @@ export class Canvas {
|
|||||||
getContext() {
|
getContext() {
|
||||||
return this.context;
|
return this.context;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* get pixel ratio
|
||||||
|
* @method
|
||||||
|
* @name Konva.Canvas#getPixelRatio
|
||||||
|
* @returns {Number} pixel ratio
|
||||||
|
* @example
|
||||||
|
* var pixelRatio = layer.getCanvas.getPixelRatio();
|
||||||
|
*/
|
||||||
getPixelRatio() {
|
getPixelRatio() {
|
||||||
return this.pixelRatio;
|
return this.pixelRatio;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* set pixel ratio
|
||||||
|
* KonvaJS automatically handles pixel ratio adustments in order to render crisp drawings
|
||||||
|
* on all devices. Most desktops, low end tablets, and low end phones, have device pixel ratios
|
||||||
|
* of 1. Some high end tablets and phones, like iPhones and iPads have a device pixel ratio
|
||||||
|
* of 2. Some Macbook Pros, and iMacs also have a device pixel ratio of 2. Some high end Android devices have pixel
|
||||||
|
* ratios of 2 or 3. Some browsers like Firefox allow you to configure the pixel ratio of the viewport. Unless otherwise
|
||||||
|
* specificed, the pixel ratio will be defaulted to the actual device pixel ratio. You can override the device pixel
|
||||||
|
* ratio for special situations, or, if you don't want the pixel ratio to be taken into account, you can set it to 1.
|
||||||
|
* @method
|
||||||
|
* @name Konva.Canvas#setPixelRatio
|
||||||
|
* @param {Number} pixelRatio
|
||||||
|
* @example
|
||||||
|
* layer.getCanvas().setPixelRatio(3);
|
||||||
|
*/
|
||||||
setPixelRatio(pixelRatio) {
|
setPixelRatio(pixelRatio) {
|
||||||
const previousRatio = this.pixelRatio;
|
const previousRatio = this.pixelRatio;
|
||||||
this.pixelRatio = pixelRatio;
|
this.pixelRatio = pixelRatio;
|
||||||
@ -148,28 +169,6 @@ export class Canvas {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* get/set pixel ratio.
|
|
||||||
* KonvaJS automatically handles pixel ratio adustments in order to render crisp drawings
|
|
||||||
* on all devices. Most desktops, low end tablets, and low end phones, have device pixel ratios
|
|
||||||
* of 1. Some high end tablets and phones, like iPhones and iPads have a device pixel ratio
|
|
||||||
* of 2. Some Macbook Pros, and iMacs also have a device pixel ratio of 2. Some high end Android devices have pixel
|
|
||||||
* ratios of 2 or 3. Some browsers like Firefox allow you to configure the pixel ratio of the viewport. Unless otherwise
|
|
||||||
* specificed, the pixel ratio will be defaulted to the actual device pixel ratio. You can override the device pixel
|
|
||||||
* ratio for special situations, or, if you don't want the pixel ratio to be taken into account, you can set it to 1.
|
|
||||||
* @name Konva.Canvas#pixelRatio
|
|
||||||
* @method
|
|
||||||
* @param {Number} pixelRatio
|
|
||||||
* @returns {Number}
|
|
||||||
* @example
|
|
||||||
* // get
|
|
||||||
* var pixelRatio = layer.getCanvas.pixelRatio();
|
|
||||||
*
|
|
||||||
* // set
|
|
||||||
* layer.getCanvas().pixelRatio(3);
|
|
||||||
*/
|
|
||||||
Factory.addGetterSetter(Canvas, 'pixelRatio', undefined, getNumberValidator());
|
|
||||||
|
|
||||||
export class SceneCanvas extends Canvas {
|
export class SceneCanvas extends Canvas {
|
||||||
constructor(
|
constructor(
|
||||||
config: ICanvasConfig = { width: 0, height: 0, willReadFrequently: false }
|
config: ICanvasConfig = { width: 0, height: 0, willReadFrequently: false }
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import { Factory } from './Factory';
|
|
||||||
import { Node, NodeConfig } from './Node';
|
|
||||||
import { getNumberValidator } from './Validators';
|
|
||||||
|
|
||||||
import { GetSet, IRect } from './types';
|
|
||||||
import { Shape } from './Shape';
|
|
||||||
import { HitCanvas, SceneCanvas } from './Canvas';
|
import { HitCanvas, SceneCanvas } from './Canvas';
|
||||||
import { SceneContext } from './Context';
|
import { SceneContext } from './Context';
|
||||||
|
import { Factory } from './Factory';
|
||||||
|
import { Node, NodeConfig } from './Node';
|
||||||
|
import { Shape } from './Shape';
|
||||||
|
import { GetSet, IRect } from './types';
|
||||||
|
import { getNumberValidator } from './Validators';
|
||||||
|
|
||||||
export type ClipFuncOutput =
|
export type ClipFuncOutput =
|
||||||
| void
|
| void
|
||||||
@ -51,18 +50,11 @@ export abstract class Container<
|
|||||||
* });
|
* });
|
||||||
*/
|
*/
|
||||||
getChildren(filterFunc?: (item: Node) => boolean) {
|
getChildren(filterFunc?: (item: Node) => boolean) {
|
||||||
if (!filterFunc) {
|
|
||||||
return this.children || [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const children = this.children || [];
|
const children = this.children || [];
|
||||||
const results: Array<ChildType> = [];
|
if (filterFunc) {
|
||||||
children.forEach(function (child) {
|
return children.filter(filterFunc);
|
||||||
if (filterFunc(child)) {
|
}
|
||||||
results.push(child);
|
return children;
|
||||||
}
|
|
||||||
});
|
|
||||||
return results;
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* determine if node has children
|
* determine if node has children
|
||||||
@ -120,8 +112,6 @@ export abstract class Container<
|
|||||||
* layer.add(shape1, shape2, shape3);
|
* layer.add(shape1, shape2, shape3);
|
||||||
* // empty arrays are accepted, though each individual child must be defined
|
* // empty arrays are accepted, though each individual child must be defined
|
||||||
* layer.add(...shapes);
|
* layer.add(...shapes);
|
||||||
* // remember to redraw layer if you changed something
|
|
||||||
* layer.draw();
|
|
||||||
*/
|
*/
|
||||||
add(...children: ChildType[]) {
|
add(...children: ChildType[]) {
|
||||||
if (children.length === 0) {
|
if (children.length === 0) {
|
||||||
@ -235,7 +225,7 @@ export abstract class Container<
|
|||||||
this._descendants((node) => {
|
this._descendants((node) => {
|
||||||
const valid = node._isMatch(selector);
|
const valid = node._isMatch(selector);
|
||||||
if (valid) {
|
if (valid) {
|
||||||
retArr.push(node as unknown as ChildNode);
|
retArr.push(node as ChildNode);
|
||||||
}
|
}
|
||||||
if (valid && findOne) {
|
if (valid && findOne) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -6,14 +6,12 @@ import { IRect } from './types';
|
|||||||
import type { Node } from './Node';
|
import type { Node } from './Node';
|
||||||
|
|
||||||
function simplifyArray(arr: Array<any>) {
|
function simplifyArray(arr: Array<any>) {
|
||||||
let retArr: Array<any> = [],
|
const retArr: Array<any> = [],
|
||||||
len = arr.length,
|
len = arr.length,
|
||||||
util = Util,
|
util = Util;
|
||||||
n,
|
|
||||||
val;
|
|
||||||
|
|
||||||
for (n = 0; n < len; n++) {
|
for (let n = 0; n < len; n++) {
|
||||||
val = arr[n];
|
let val = arr[n];
|
||||||
if (util._isNumber(val)) {
|
if (util._isNumber(val)) {
|
||||||
val = Math.round(val * 1000) / 1000;
|
val = Math.round(val * 1000) / 1000;
|
||||||
} else if (!util._isString(val)) {
|
} else if (!util._isString(val)) {
|
||||||
|
132
src/Factory.ts
132
src/Factory.ts
@ -1,17 +1,75 @@
|
|||||||
import { Node } from './Node';
|
import { Node } from './Node';
|
||||||
|
import { GetSet } from './types';
|
||||||
import { Util } from './Util';
|
import { Util } from './Util';
|
||||||
import { getComponentValidator } from './Validators';
|
import { getComponentValidator } from './Validators';
|
||||||
|
|
||||||
const GET = 'get',
|
const GET = 'get';
|
||||||
SET = 'set';
|
const SET = 'set';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enforces that a type is a string.
|
||||||
|
*/
|
||||||
|
type EnforceString<T> = T extends string ? T : never;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a class.
|
||||||
|
*/
|
||||||
|
type Constructor = abstract new (...args: any) => any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An attribute of an instance of the provided class. Attributes names be strings.
|
||||||
|
*/
|
||||||
|
type Attr<T extends Constructor> = EnforceString<keyof InstanceType<T>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function that is called after a setter is called.
|
||||||
|
*/
|
||||||
|
type AfterFunc<T extends Constructor> = (this: InstanceType<T>) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the type of a GetSet.
|
||||||
|
*/
|
||||||
|
type ExtractGetSet<T> = T extends GetSet<infer U, any> ? U : never;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the type of a GetSet class attribute.
|
||||||
|
*/
|
||||||
|
type Value<T extends Constructor, U extends Attr<T>> = ExtractGetSet<
|
||||||
|
InstanceType<T>[U]
|
||||||
|
>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function that validates a value.
|
||||||
|
*/
|
||||||
|
type ValidatorFunc<T> = (val: ExtractGetSet<T>, attr: string) => T;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the "components" (keys) of a GetSet value. The value must be an object.
|
||||||
|
*/
|
||||||
|
type ExtractComponents<T extends Constructor, U extends Attr<T>> = Value<
|
||||||
|
T,
|
||||||
|
U
|
||||||
|
> extends Record<string, any>
|
||||||
|
? EnforceString<keyof Value<T, U>>[]
|
||||||
|
: never;
|
||||||
|
|
||||||
export const Factory = {
|
export const Factory = {
|
||||||
addGetterSetter(constructor, attr, def?, validator?, after?) {
|
addGetterSetter<T extends Constructor, U extends Attr<T>>(
|
||||||
|
constructor: T,
|
||||||
|
attr: U,
|
||||||
|
def?: Value<T, U>,
|
||||||
|
validator?: ValidatorFunc<Value<T, U>>,
|
||||||
|
after?: AfterFunc<T>
|
||||||
|
): void {
|
||||||
Factory.addGetter(constructor, attr, def);
|
Factory.addGetter(constructor, attr, def);
|
||||||
Factory.addSetter(constructor, attr, validator, after);
|
Factory.addSetter(constructor, attr, validator, after);
|
||||||
Factory.addOverloadedGetterSetter(constructor, attr);
|
Factory.addOverloadedGetterSetter(constructor, attr);
|
||||||
},
|
},
|
||||||
addGetter(constructor, attr, def?) {
|
addGetter<T extends Constructor, U extends Attr<T>>(
|
||||||
|
constructor: T,
|
||||||
|
attr: U,
|
||||||
|
def?: Value<T, U>
|
||||||
|
) {
|
||||||
const method = GET + Util._capitalize(attr);
|
const method = GET + Util._capitalize(attr);
|
||||||
|
|
||||||
constructor.prototype[method] =
|
constructor.prototype[method] =
|
||||||
@ -22,14 +80,25 @@ export const Factory = {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
addSetter(constructor, attr, validator?, after?) {
|
addSetter<T extends Constructor, U extends Attr<T>>(
|
||||||
|
constructor: T,
|
||||||
|
attr: U,
|
||||||
|
validator?: ValidatorFunc<Value<T, U>>,
|
||||||
|
after?: AfterFunc<T>
|
||||||
|
) {
|
||||||
const method = SET + Util._capitalize(attr);
|
const method = SET + Util._capitalize(attr);
|
||||||
|
|
||||||
if (!constructor.prototype[method]) {
|
if (!constructor.prototype[method]) {
|
||||||
Factory.overWriteSetter(constructor, attr, validator, after);
|
Factory.overWriteSetter(constructor, attr, validator, after);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
overWriteSetter(constructor, attr, validator?, after?) {
|
|
||||||
|
overWriteSetter<T extends Constructor, U extends Attr<T>>(
|
||||||
|
constructor: T,
|
||||||
|
attr: U,
|
||||||
|
validator?: ValidatorFunc<Value<T, U>>,
|
||||||
|
after?: AfterFunc<T>
|
||||||
|
) {
|
||||||
const method = SET + Util._capitalize(attr);
|
const method = SET + Util._capitalize(attr);
|
||||||
constructor.prototype[method] = function (val) {
|
constructor.prototype[method] = function (val) {
|
||||||
if (validator && val !== undefined && val !== null) {
|
if (validator && val !== undefined && val !== null) {
|
||||||
@ -45,26 +114,25 @@ export const Factory = {
|
|||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
addComponentsGetterSetter(
|
|
||||||
constructor,
|
addComponentsGetterSetter<T extends Constructor, U extends Attr<T>>(
|
||||||
attr: string,
|
constructor: T,
|
||||||
components: Array<string>,
|
attr: U,
|
||||||
validator?: Function,
|
components: ExtractComponents<T, U>,
|
||||||
after?: Function
|
validator?: ValidatorFunc<Value<T, U>>,
|
||||||
|
after?: AfterFunc<T>
|
||||||
) {
|
) {
|
||||||
let len = components.length,
|
const len = components.length,
|
||||||
capitalize = Util._capitalize,
|
capitalize = Util._capitalize,
|
||||||
getter = GET + capitalize(attr),
|
getter = GET + capitalize(attr),
|
||||||
setter = SET + capitalize(attr),
|
setter = SET + capitalize(attr);
|
||||||
n,
|
|
||||||
component;
|
|
||||||
|
|
||||||
// getter
|
// getter
|
||||||
constructor.prototype[getter] = function () {
|
constructor.prototype[getter] = function () {
|
||||||
const ret = {};
|
const ret: Record<string, any> = {};
|
||||||
|
|
||||||
for (n = 0; n < len; n++) {
|
for (let n = 0; n < len; n++) {
|
||||||
component = components[n];
|
const component = components[n];
|
||||||
ret[component] = this.getAttr(attr + capitalize(component));
|
ret[component] = this.getAttr(attr + capitalize(component));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,18 +143,17 @@ export const Factory = {
|
|||||||
|
|
||||||
// setter
|
// setter
|
||||||
constructor.prototype[setter] = function (val) {
|
constructor.prototype[setter] = function (val) {
|
||||||
let oldVal = this.attrs[attr],
|
const oldVal = this.attrs[attr];
|
||||||
key;
|
|
||||||
|
|
||||||
if (validator) {
|
if (validator) {
|
||||||
val = validator.call(this, val);
|
val = validator.call(this, val, attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (basicValidator) {
|
if (basicValidator) {
|
||||||
basicValidator.call(this, val, attr);
|
basicValidator.call(this, val, attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (key in val) {
|
for (const key in val) {
|
||||||
if (!val.hasOwnProperty(key)) {
|
if (!val.hasOwnProperty(key)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -109,7 +176,10 @@ export const Factory = {
|
|||||||
|
|
||||||
Factory.addOverloadedGetterSetter(constructor, attr);
|
Factory.addOverloadedGetterSetter(constructor, attr);
|
||||||
},
|
},
|
||||||
addOverloadedGetterSetter(constructor, attr) {
|
addOverloadedGetterSetter<T extends Constructor, U extends Attr<T>>(
|
||||||
|
constructor: T,
|
||||||
|
attr: U
|
||||||
|
) {
|
||||||
const capitalizedAttr = Util._capitalize(attr),
|
const capitalizedAttr = Util._capitalize(attr),
|
||||||
setter = SET + capitalizedAttr,
|
setter = SET + capitalizedAttr,
|
||||||
getter = GET + capitalizedAttr;
|
getter = GET + capitalizedAttr;
|
||||||
@ -124,7 +194,12 @@ export const Factory = {
|
|||||||
return this[getter]();
|
return this[getter]();
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
addDeprecatedGetterSetter(constructor, attr, def, validator) {
|
addDeprecatedGetterSetter<T extends Constructor, U extends Attr<T>>(
|
||||||
|
constructor: T,
|
||||||
|
attr: U,
|
||||||
|
def: Value<T, U>,
|
||||||
|
validator: ValidatorFunc<Value<T, U>>
|
||||||
|
) {
|
||||||
Util.error('Adding deprecated ' + attr);
|
Util.error('Adding deprecated ' + attr);
|
||||||
|
|
||||||
const method = GET + Util._capitalize(attr);
|
const method = GET + Util._capitalize(attr);
|
||||||
@ -142,7 +217,10 @@ export const Factory = {
|
|||||||
});
|
});
|
||||||
Factory.addOverloadedGetterSetter(constructor, attr);
|
Factory.addOverloadedGetterSetter(constructor, attr);
|
||||||
},
|
},
|
||||||
backCompat(constructor, methods) {
|
backCompat<T extends Constructor>(
|
||||||
|
constructor: T,
|
||||||
|
methods: Record<string, string>
|
||||||
|
) {
|
||||||
Util.each(methods, function (oldMethodName, newMethodName) {
|
Util.each(methods, function (oldMethodName, newMethodName) {
|
||||||
const method = constructor.prototype[newMethodName];
|
const method = constructor.prototype[newMethodName];
|
||||||
const oldGetter = GET + Util._capitalize(oldMethodName);
|
const oldGetter = GET + Util._capitalize(oldMethodName);
|
||||||
@ -164,7 +242,7 @@ export const Factory = {
|
|||||||
constructor.prototype[oldSetter] = deprecated;
|
constructor.prototype[oldSetter] = deprecated;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
afterSetFilter(this: Node) {
|
afterSetFilter(this: Node): void {
|
||||||
this._filterUpToDate = false;
|
this._filterUpToDate = false;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -11,7 +11,7 @@ import { _registerNode } from './Global';
|
|||||||
* @constructor
|
* @constructor
|
||||||
* @memberof Konva
|
* @memberof Konva
|
||||||
* @augments Konva.Layer
|
* @augments Konva.Layer
|
||||||
* @@containerParams
|
@@containerParams
|
||||||
* @example
|
* @example
|
||||||
* var layer = new Konva.FastLayer();
|
* var layer = new Konva.FastLayer();
|
||||||
*/
|
*/
|
||||||
|
@ -114,7 +114,7 @@ export class Layer extends Container<Group | Shape> {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
// extend Node.prototype.setZIndex
|
// extend Node.prototype.setZIndex
|
||||||
setZIndex(index) {
|
setZIndex(index: number) {
|
||||||
super.setZIndex(index);
|
super.setZIndex(index);
|
||||||
const stage = this.getStage();
|
const stage = this.getStage();
|
||||||
if (stage && stage.content) {
|
if (stage && stage.content) {
|
||||||
@ -385,7 +385,7 @@ export class Layer extends Container<Group | Shape> {
|
|||||||
// empty pixel
|
// empty pixel
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
drawScene(can?: SceneCanvas, top?: Node) {
|
drawScene(can?: SceneCanvas, top?: Node, bufferCanvas?: SceneCanvas) {
|
||||||
const layer = this.getLayer(),
|
const layer = this.getLayer(),
|
||||||
canvas = can || (layer && layer.getCanvas());
|
canvas = can || (layer && layer.getCanvas());
|
||||||
|
|
||||||
@ -397,7 +397,7 @@ export class Layer extends Container<Group | Shape> {
|
|||||||
canvas.getContext().clear();
|
canvas.getContext().clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
Container.prototype.drawScene.call(this, canvas, top);
|
Container.prototype.drawScene.call(this, canvas, top, bufferCanvas);
|
||||||
|
|
||||||
this._fire(DRAW, {
|
this._fire(DRAW, {
|
||||||
node: this,
|
node: this,
|
||||||
|
113
src/Node.ts
113
src/Node.ts
@ -1,19 +1,19 @@
|
|||||||
import { Util, Transform } from './Util';
|
import { Canvas, HitCanvas, SceneCanvas } from './Canvas';
|
||||||
import { Factory } from './Factory';
|
|
||||||
import { SceneCanvas, HitCanvas, Canvas } from './Canvas';
|
|
||||||
import { Konva } from './Global';
|
|
||||||
import { Container } from './Container';
|
import { Container } from './Container';
|
||||||
import { GetSet, Vector2d, IRect } from './types';
|
import { Context } from './Context';
|
||||||
import { DD } from './DragAndDrop';
|
import { DD } from './DragAndDrop';
|
||||||
|
import { Factory } from './Factory';
|
||||||
|
import { Konva } from './Global';
|
||||||
|
import { Layer } from './Layer';
|
||||||
|
import { Shape } from './Shape';
|
||||||
|
import { Stage } from './Stage';
|
||||||
|
import { GetSet, IRect, Vector2d } from './types';
|
||||||
|
import { Transform, Util } from './Util';
|
||||||
import {
|
import {
|
||||||
|
getBooleanValidator,
|
||||||
getNumberValidator,
|
getNumberValidator,
|
||||||
getStringValidator,
|
getStringValidator,
|
||||||
getBooleanValidator,
|
|
||||||
} from './Validators';
|
} from './Validators';
|
||||||
import { Stage } from './Stage';
|
|
||||||
import { Context } from './Context';
|
|
||||||
import { Shape } from './Shape';
|
|
||||||
import { Layer } from './Layer';
|
|
||||||
|
|
||||||
export type Filter = (this: Node, imageData: ImageData) => void;
|
export type Filter = (this: Node, imageData: ImageData) => void;
|
||||||
|
|
||||||
@ -88,6 +88,10 @@ const ABSOLUTE_OPACITY = 'absoluteOpacity',
|
|||||||
LISTENING = 'listening',
|
LISTENING = 'listening',
|
||||||
MOUSEENTER = 'mouseenter',
|
MOUSEENTER = 'mouseenter',
|
||||||
MOUSELEAVE = 'mouseleave',
|
MOUSELEAVE = 'mouseleave',
|
||||||
|
POINTERENTER = 'pointerenter',
|
||||||
|
POINTERLEAVE = 'pointerleave',
|
||||||
|
TOUCHENTER = 'touchenter',
|
||||||
|
TOUCHLEAVE = 'touchleave',
|
||||||
NAME = 'name',
|
NAME = 'name',
|
||||||
SET = 'set',
|
SET = 'set',
|
||||||
SHAPE = 'Shape',
|
SHAPE = 'Shape',
|
||||||
@ -198,7 +202,8 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
// for transform the cache can be NOT empty
|
// for transform the cache can be NOT empty
|
||||||
// but we still need to recalculate it if it is dirty
|
// but we still need to recalculate it if it is dirty
|
||||||
const isTransform = attr === TRANSFORM || attr === ABSOLUTE_TRANSFORM;
|
const isTransform = attr === TRANSFORM || attr === ABSOLUTE_TRANSFORM;
|
||||||
const invalid = cache === undefined || (isTransform && cache.dirty === true);
|
const invalid =
|
||||||
|
cache === undefined || (isTransform && cache.dirty === true);
|
||||||
|
|
||||||
// if not cached, we need to set it using the private getter method.
|
// if not cached, we need to set it using the private getter method.
|
||||||
if (invalid) {
|
if (invalid) {
|
||||||
@ -247,8 +252,8 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
*/
|
*/
|
||||||
clearCache() {
|
clearCache() {
|
||||||
if (this._cache.has(CANVAS)) {
|
if (this._cache.has(CANVAS)) {
|
||||||
const { scene, filter, hit } = this._cache.get(CANVAS);
|
const { scene, filter, hit, buffer } = this._cache.get(CANVAS);
|
||||||
Util.releaseCanvas(scene, filter, hit);
|
Util.releaseCanvas(scene, filter, hit, buffer);
|
||||||
this._cache.delete(CANVAS);
|
this._cache.delete(CANVAS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,6 +389,18 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
sceneContext = cachedSceneCanvas.getContext(),
|
sceneContext = cachedSceneCanvas.getContext(),
|
||||||
hitContext = cachedHitCanvas.getContext();
|
hitContext = cachedHitCanvas.getContext();
|
||||||
|
|
||||||
|
const bufferCanvas = new SceneCanvas({
|
||||||
|
// width and height already multiplied by pixelRatio
|
||||||
|
// so we need to revert that
|
||||||
|
// also increase size by x nd y offset to make sure content fits canvas
|
||||||
|
width:
|
||||||
|
cachedSceneCanvas.width / cachedSceneCanvas.pixelRatio + Math.abs(x),
|
||||||
|
height:
|
||||||
|
cachedSceneCanvas.height / cachedSceneCanvas.pixelRatio + Math.abs(y),
|
||||||
|
pixelRatio: cachedSceneCanvas.pixelRatio,
|
||||||
|
}),
|
||||||
|
bufferContext = bufferCanvas.getContext();
|
||||||
|
|
||||||
cachedHitCanvas.isCache = true;
|
cachedHitCanvas.isCache = true;
|
||||||
cachedSceneCanvas.isCache = true;
|
cachedSceneCanvas.isCache = true;
|
||||||
|
|
||||||
@ -397,16 +414,23 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
|
|
||||||
sceneContext.save();
|
sceneContext.save();
|
||||||
hitContext.save();
|
hitContext.save();
|
||||||
|
bufferContext.save();
|
||||||
|
|
||||||
sceneContext.translate(-x, -y);
|
sceneContext.translate(-x, -y);
|
||||||
hitContext.translate(-x, -y);
|
hitContext.translate(-x, -y);
|
||||||
|
bufferContext.translate(-x, -y);
|
||||||
|
// hard-code offset to make sure content fits canvas
|
||||||
|
// @ts-ignore
|
||||||
|
bufferCanvas.x = x;
|
||||||
|
// @ts-ignore
|
||||||
|
bufferCanvas.y = y;
|
||||||
|
|
||||||
// extra flag to skip on getAbsolute opacity calc
|
// extra flag to skip on getAbsolute opacity calc
|
||||||
this._isUnderCache = true;
|
this._isUnderCache = true;
|
||||||
this._clearSelfAndDescendantCache(ABSOLUTE_OPACITY);
|
this._clearSelfAndDescendantCache(ABSOLUTE_OPACITY);
|
||||||
this._clearSelfAndDescendantCache(ABSOLUTE_SCALE);
|
this._clearSelfAndDescendantCache(ABSOLUTE_SCALE);
|
||||||
|
|
||||||
this.drawScene(cachedSceneCanvas, this);
|
this.drawScene(cachedSceneCanvas, this, bufferCanvas);
|
||||||
this.drawHit(cachedHitCanvas, this);
|
this.drawHit(cachedHitCanvas, this);
|
||||||
this._isUnderCache = false;
|
this._isUnderCache = false;
|
||||||
|
|
||||||
@ -430,6 +454,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
scene: cachedSceneCanvas,
|
scene: cachedSceneCanvas,
|
||||||
filter: cachedFilterCanvas,
|
filter: cachedFilterCanvas,
|
||||||
hit: cachedHitCanvas,
|
hit: cachedHitCanvas,
|
||||||
|
buffer: bufferCanvas,
|
||||||
x: x,
|
x: x,
|
||||||
y: y,
|
y: y,
|
||||||
});
|
});
|
||||||
@ -692,39 +717,32 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
evtStr: K,
|
evtStr: K,
|
||||||
handler: KonvaEventListener<this, NodeEventMap[K]>
|
handler: KonvaEventListener<this, NodeEventMap[K]>
|
||||||
) {
|
) {
|
||||||
this._cache && this._cache.delete(ALL_LISTENERS);
|
if (this._cache) {
|
||||||
|
this._cache.delete(ALL_LISTENERS);
|
||||||
|
}
|
||||||
|
|
||||||
if (arguments.length === 3) {
|
if (arguments.length === 3) {
|
||||||
return this._delegate.apply(this, arguments as any);
|
return this._delegate.apply(this, arguments as any);
|
||||||
}
|
}
|
||||||
let events = (evtStr as string).split(SPACE),
|
const events = (evtStr as string).split(SPACE);
|
||||||
len = events.length,
|
|
||||||
n,
|
|
||||||
event,
|
|
||||||
parts,
|
|
||||||
baseEvent,
|
|
||||||
name;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* loop through types and attach event listeners to
|
* loop through types and attach event listeners to
|
||||||
* each one. eg. 'click mouseover.namespace mouseout'
|
* each one. eg. 'click mouseover.namespace mouseout'
|
||||||
* will create three event bindings
|
* will create three event bindings
|
||||||
*/
|
*/
|
||||||
for (n = 0; n < len; n++) {
|
for (let n = 0; n < events.length; n++) {
|
||||||
event = events[n];
|
const event = events[n];
|
||||||
parts = event.split('.');
|
const parts = event.split('.');
|
||||||
baseEvent = parts[0];
|
const baseEvent = parts[0];
|
||||||
name = parts[1] || '';
|
const name = parts[1] || '';
|
||||||
|
|
||||||
// create events array if it doesn't exist
|
// create events array if it doesn't exist
|
||||||
if (!this.eventListeners[baseEvent]) {
|
if (!this.eventListeners[baseEvent]) {
|
||||||
this.eventListeners[baseEvent] = [];
|
this.eventListeners[baseEvent] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.eventListeners[baseEvent].push({
|
this.eventListeners[baseEvent].push({ name, handler });
|
||||||
name: name,
|
|
||||||
handler: handler,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
@ -880,13 +898,13 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
* @example
|
* @example
|
||||||
* var x = node.getAttr('x');
|
* var x = node.getAttr('x');
|
||||||
*/
|
*/
|
||||||
getAttr(attr: string) {
|
getAttr<T>(attr: string) {
|
||||||
const method = 'get' + Util._capitalize(attr);
|
const method = 'get' + Util._capitalize(attr);
|
||||||
if (Util._isFunction((this as any)[method])) {
|
if (Util._isFunction((this as any)[method])) {
|
||||||
return (this as any)[method]();
|
return (this as any)[method]();
|
||||||
}
|
}
|
||||||
// otherwise get directly
|
// otherwise get directly
|
||||||
return this.attrs[attr];
|
return this.attrs[attr] as T | undefined;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* get ancestors
|
* get ancestors
|
||||||
@ -2207,7 +2225,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
* node.addName('selected');
|
* node.addName('selected');
|
||||||
* node.name(); // return 'red selected'
|
* node.name(); // return 'red selected'
|
||||||
*/
|
*/
|
||||||
addName(name) {
|
addName(name: string) {
|
||||||
if (!this.hasName(name)) {
|
if (!this.hasName(name)) {
|
||||||
const oldName = this.name();
|
const oldName = this.name();
|
||||||
const newName = oldName ? oldName + ' ' + name : name;
|
const newName = oldName ? oldName + ' ' + name : name;
|
||||||
@ -2270,7 +2288,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
* @example
|
* @example
|
||||||
* node.setAttr('x', 5);
|
* node.setAttr('x', 5);
|
||||||
*/
|
*/
|
||||||
setAttr(attr, val) {
|
setAttr(attr: string, val) {
|
||||||
const func = this[SET + Util._capitalize(attr)];
|
const func = this[SET + Util._capitalize(attr)];
|
||||||
|
|
||||||
if (Util._isFunction(func)) {
|
if (Util._isFunction(func)) {
|
||||||
@ -2287,7 +2305,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
drawNode?.batchDraw();
|
drawNode?.batchDraw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_setAttr(key, val) {
|
_setAttr(key: string, val) {
|
||||||
const oldVal = this.attrs[key];
|
const oldVal = this.attrs[key];
|
||||||
if (oldVal === val && !Util.isObject(val)) {
|
if (oldVal === val && !Util.isObject(val)) {
|
||||||
return;
|
return;
|
||||||
@ -2321,8 +2339,17 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
evt.target = this;
|
evt.target = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const nonBubbling = [
|
||||||
|
MOUSEENTER,
|
||||||
|
MOUSELEAVE,
|
||||||
|
POINTERENTER,
|
||||||
|
POINTERLEAVE,
|
||||||
|
TOUCHENTER,
|
||||||
|
TOUCHLEAVE,
|
||||||
|
];
|
||||||
|
|
||||||
const shouldStop =
|
const shouldStop =
|
||||||
(eventType === MOUSEENTER || eventType === MOUSELEAVE) &&
|
nonBubbling.indexOf(eventType) !== -1 &&
|
||||||
((compareShape &&
|
((compareShape &&
|
||||||
(this === compareShape ||
|
(this === compareShape ||
|
||||||
(this.isAncestorOf && this.isAncestorOf(compareShape)))) ||
|
(this.isAncestorOf && this.isAncestorOf(compareShape)))) ||
|
||||||
@ -2333,7 +2360,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
|
|
||||||
// simulate event bubbling
|
// simulate event bubbling
|
||||||
const stopBubble =
|
const stopBubble =
|
||||||
(eventType === MOUSEENTER || eventType === MOUSELEAVE) &&
|
nonBubbling.indexOf(eventType) !== -1 &&
|
||||||
compareShape &&
|
compareShape &&
|
||||||
compareShape.isAncestorOf &&
|
compareShape.isAncestorOf &&
|
||||||
compareShape.isAncestorOf(this) &&
|
compareShape.isAncestorOf(this) &&
|
||||||
@ -2379,7 +2406,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
|
|
||||||
const topListeners = this._getProtoListeners(eventType);
|
const topListeners = this._getProtoListeners(eventType);
|
||||||
if (topListeners) {
|
if (topListeners) {
|
||||||
for (var i = 0; i < topListeners.length; i++) {
|
for (let i = 0; i < topListeners.length; i++) {
|
||||||
topListeners[i].handler.call(this, evt);
|
topListeners[i].handler.call(this, evt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2388,7 +2415,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
// because events can be added/removed while firing
|
// because events can be added/removed while firing
|
||||||
const selfListeners = this.eventListeners[eventType];
|
const selfListeners = this.eventListeners[eventType];
|
||||||
if (selfListeners) {
|
if (selfListeners) {
|
||||||
for (var i = 0; i < selfListeners.length; i++) {
|
for (let i = 0; i < selfListeners.length; i++) {
|
||||||
selfListeners[i].handler.call(this, evt);
|
selfListeners[i].handler.call(this, evt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2660,7 +2687,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|||||||
rotation: GetSet<number, this>;
|
rotation: GetSet<number, this>;
|
||||||
zIndex: GetSet<number, this>;
|
zIndex: GetSet<number, this>;
|
||||||
|
|
||||||
scale: GetSet<Vector2d | undefined, this>;
|
scale: GetSet<Vector2d, this>;
|
||||||
scaleX: GetSet<number, this>;
|
scaleX: GetSet<number, this>;
|
||||||
scaleY: GetSet<number, this>;
|
scaleY: GetSet<number, this>;
|
||||||
skew: GetSet<Vector2d, this>;
|
skew: GetSet<Vector2d, this>;
|
||||||
@ -3102,7 +3129,7 @@ addGetterSetter(Node, 'offsetY', 0, getNumberValidator());
|
|||||||
* node.offsetY(3);
|
* node.offsetY(3);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
addGetterSetter(Node, 'dragDistance', null, getNumberValidator());
|
addGetterSetter(Node, 'dragDistance', undefined, getNumberValidator());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get/set drag distance
|
* get/set drag distance
|
||||||
@ -3193,7 +3220,7 @@ addGetterSetter(Node, 'listening', true, getBooleanValidator());
|
|||||||
|
|
||||||
addGetterSetter(Node, 'preventDefault', true, getBooleanValidator());
|
addGetterSetter(Node, 'preventDefault', true, getBooleanValidator());
|
||||||
|
|
||||||
addGetterSetter(Node, 'filters', null, function (this: Node, val) {
|
addGetterSetter(Node, 'filters', undefined, function (this: Node, val) {
|
||||||
this._filterUpToDate = false;
|
this._filterUpToDate = false;
|
||||||
return val;
|
return val;
|
||||||
});
|
});
|
||||||
|
58
src/Shape.ts
58
src/Shape.ts
@ -213,10 +213,16 @@ export class Shape<
|
|||||||
shapes[key] = this;
|
shapes[key] = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
getContext() {
|
getContext() {
|
||||||
Util.warn('shape.getContext() method is deprecated. Please do not use it.');
|
Util.warn('shape.getContext() method is deprecated. Please do not use it.');
|
||||||
return this.getLayer()!.getContext();
|
return this.getLayer()!.getContext();
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
getCanvas() {
|
getCanvas() {
|
||||||
Util.warn('shape.getCanvas() method is deprecated. Please do not use it.');
|
Util.warn('shape.getCanvas() method is deprecated. Please do not use it.');
|
||||||
return this.getLayer()!.getCanvas();
|
return this.getLayer()!.getCanvas();
|
||||||
@ -442,7 +448,7 @@ export class Shape<
|
|||||||
* @param {Number} point.y
|
* @param {Number} point.y
|
||||||
* @returns {Boolean}
|
* @returns {Boolean}
|
||||||
*/
|
*/
|
||||||
intersects(point) {
|
intersects(point: Vector2d) {
|
||||||
const stage = this.getStage();
|
const stage = this.getStage();
|
||||||
if (!stage) {
|
if (!stage) {
|
||||||
return false;
|
return false;
|
||||||
@ -594,15 +600,14 @@ export class Shape<
|
|||||||
// 3 - when node is cached and we need to draw it into layer
|
// 3 - when node is cached and we need to draw it into layer
|
||||||
|
|
||||||
const layer = this.getLayer();
|
const layer = this.getLayer();
|
||||||
let canvas = can || layer!.getCanvas(),
|
const canvas = can || layer!.getCanvas(),
|
||||||
context = canvas.getContext() as SceneContext,
|
context = canvas.getContext() as SceneContext,
|
||||||
cachedCanvas = this._getCanvasCache(),
|
cachedCanvas = this._getCanvasCache(),
|
||||||
drawFunc = this.getSceneFunc(),
|
drawFunc = this.getSceneFunc(),
|
||||||
hasShadow = this.hasShadow(),
|
hasShadow = this.hasShadow();
|
||||||
stage,
|
let stage;
|
||||||
bufferContext;
|
|
||||||
|
|
||||||
const skipBuffer = canvas.isCache;
|
const skipBuffer = false;
|
||||||
const cachingSelf = top === this;
|
const cachingSelf = top === this;
|
||||||
|
|
||||||
if (!this.isVisible() && !cachingSelf) {
|
if (!this.isVisible() && !cachingSelf) {
|
||||||
@ -628,12 +633,12 @@ export class Shape<
|
|||||||
if (this._useBufferCanvas() && !skipBuffer) {
|
if (this._useBufferCanvas() && !skipBuffer) {
|
||||||
stage = this.getStage();
|
stage = this.getStage();
|
||||||
const bc = bufferCanvas || stage.bufferCanvas;
|
const bc = bufferCanvas || stage.bufferCanvas;
|
||||||
bufferContext = bc.getContext();
|
const bufferContext = bc.getContext();
|
||||||
bufferContext.clear();
|
bufferContext.clear();
|
||||||
bufferContext.save();
|
bufferContext.save();
|
||||||
bufferContext._applyLineJoin(this);
|
bufferContext._applyLineJoin(this);
|
||||||
// layer might be undefined if we are using cache before adding to layer
|
// layer might be undefined if we are using cache before adding to layer
|
||||||
var o = this.getAbsoluteTransform(top).getMatrix();
|
const o = this.getAbsoluteTransform(top).getMatrix();
|
||||||
bufferContext.transform(o[0], o[1], o[2], o[3], o[4], o[5]);
|
bufferContext.transform(o[0], o[1], o[2], o[3], o[4], o[5]);
|
||||||
|
|
||||||
drawFunc.call(this, bufferContext, this);
|
drawFunc.call(this, bufferContext, this);
|
||||||
@ -646,12 +651,18 @@ export class Shape<
|
|||||||
}
|
}
|
||||||
context._applyOpacity(this);
|
context._applyOpacity(this);
|
||||||
context._applyGlobalCompositeOperation(this);
|
context._applyGlobalCompositeOperation(this);
|
||||||
context.drawImage(bc._canvas, 0, 0, bc.width / ratio, bc.height / ratio);
|
context.drawImage(
|
||||||
|
bc._canvas,
|
||||||
|
bc.x || 0,
|
||||||
|
bc.y || 0,
|
||||||
|
bc.width / ratio,
|
||||||
|
bc.height / ratio
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
context._applyLineJoin(this);
|
context._applyLineJoin(this);
|
||||||
|
|
||||||
if (!cachingSelf) {
|
if (!cachingSelf) {
|
||||||
var o = this.getAbsoluteTransform(top).getMatrix();
|
const o = this.getAbsoluteTransform(top).getMatrix();
|
||||||
context.transform(o[0], o[1], o[2], o[3], o[4], o[5]);
|
context.transform(o[0], o[1], o[2], o[3], o[4], o[5]);
|
||||||
context._applyOpacity(this);
|
context._applyOpacity(this);
|
||||||
context._applyGlobalCompositeOperation(this);
|
context._applyGlobalCompositeOperation(this);
|
||||||
@ -722,31 +733,25 @@ export class Shape<
|
|||||||
* shape.drawHitFromCache();
|
* shape.drawHitFromCache();
|
||||||
*/
|
*/
|
||||||
drawHitFromCache(alphaThreshold = 0) {
|
drawHitFromCache(alphaThreshold = 0) {
|
||||||
let cachedCanvas = this._getCanvasCache(),
|
const cachedCanvas = this._getCanvasCache(),
|
||||||
sceneCanvas = this._getCachedSceneCanvas(),
|
sceneCanvas = this._getCachedSceneCanvas(),
|
||||||
hitCanvas = cachedCanvas.hit,
|
hitCanvas = cachedCanvas.hit,
|
||||||
hitContext = hitCanvas.getContext(),
|
hitContext = hitCanvas.getContext(),
|
||||||
hitWidth = hitCanvas.getWidth(),
|
hitWidth = hitCanvas.getWidth(),
|
||||||
hitHeight = hitCanvas.getHeight(),
|
hitHeight = hitCanvas.getHeight();
|
||||||
hitImageData,
|
|
||||||
hitData,
|
|
||||||
len,
|
|
||||||
rgbColorKey,
|
|
||||||
i,
|
|
||||||
alpha;
|
|
||||||
|
|
||||||
hitContext.clear();
|
hitContext.clear();
|
||||||
hitContext.drawImage(sceneCanvas._canvas, 0, 0, hitWidth, hitHeight);
|
hitContext.drawImage(sceneCanvas._canvas, 0, 0, hitWidth, hitHeight);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
hitImageData = hitContext.getImageData(0, 0, hitWidth, hitHeight);
|
const hitImageData = hitContext.getImageData(0, 0, hitWidth, hitHeight);
|
||||||
hitData = hitImageData.data;
|
const hitData = hitImageData.data;
|
||||||
len = hitData.length;
|
const len = hitData.length;
|
||||||
rgbColorKey = Util._hexToRgb(this.colorKey);
|
const rgbColorKey = Util._hexToRgb(this.colorKey);
|
||||||
|
|
||||||
// replace non transparent pixels with color key
|
// replace non transparent pixels with color key
|
||||||
for (i = 0; i < len; i += 4) {
|
for (let i = 0; i < len; i += 4) {
|
||||||
alpha = hitData[i + 3];
|
const alpha = hitData[i + 3];
|
||||||
if (alpha > alphaThreshold) {
|
if (alpha > alphaThreshold) {
|
||||||
hitData[i] = rgbColorKey.r;
|
hitData[i] = rgbColorKey.r;
|
||||||
hitData[i + 1] = rgbColorKey.g;
|
hitData[i + 1] = rgbColorKey.g;
|
||||||
@ -843,6 +848,11 @@ export class Shape<
|
|||||||
strokeLinearGradientStartPoint: GetSet<Vector2d, this>;
|
strokeLinearGradientStartPoint: GetSet<Vector2d, this>;
|
||||||
strokeLinearGradientEndPoint: GetSet<Vector2d, this>;
|
strokeLinearGradientEndPoint: GetSet<Vector2d, this>;
|
||||||
strokeLinearGradientColorStops: GetSet<Array<number | string>, this>;
|
strokeLinearGradientColorStops: GetSet<Array<number | string>, this>;
|
||||||
|
strokeLinearGradientStartPointX: GetSet<number, this>;
|
||||||
|
strokeLinearGradientStartPointY: GetSet<number, this>;
|
||||||
|
strokeLinearGradientEndPointX: GetSet<number, this>;
|
||||||
|
strokeLinearGradientEndPointY: GetSet<number, this>;
|
||||||
|
fillRule: GetSet<CanvasFillRule, this>;
|
||||||
}
|
}
|
||||||
|
|
||||||
Shape.prototype._fillFunc = _fillFunc;
|
Shape.prototype._fillFunc = _fillFunc;
|
||||||
|
22
src/Stage.ts
22
src/Stage.ts
@ -214,11 +214,11 @@ export class Stage extends Container<Layer> {
|
|||||||
*/
|
*/
|
||||||
setContainer(container) {
|
setContainer(container) {
|
||||||
if (typeof container === STRING) {
|
if (typeof container === STRING) {
|
||||||
|
let id;
|
||||||
if (container.charAt(0) === '.') {
|
if (container.charAt(0) === '.') {
|
||||||
const className = container.slice(1);
|
const className = container.slice(1);
|
||||||
container = document.getElementsByClassName(className)[0];
|
container = document.getElementsByClassName(className)[0];
|
||||||
} else {
|
} else {
|
||||||
var id;
|
|
||||||
if (container.charAt(0) !== '#') {
|
if (container.charAt(0) !== '#') {
|
||||||
id = container;
|
id = container;
|
||||||
} else {
|
} else {
|
||||||
@ -249,11 +249,10 @@ export class Stage extends Container<Layer> {
|
|||||||
* @name Konva.Stage#clear
|
* @name Konva.Stage#clear
|
||||||
*/
|
*/
|
||||||
clear() {
|
clear() {
|
||||||
let layers = this.children,
|
const layers = this.children,
|
||||||
len = layers.length,
|
len = layers.length;
|
||||||
n;
|
|
||||||
|
|
||||||
for (n = 0; n < len; n++) {
|
for (let n = 0; n < len; n++) {
|
||||||
layers[n].clear();
|
layers[n].clear();
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
@ -367,12 +366,11 @@ export class Stage extends Container<Layer> {
|
|||||||
if (!pos) {
|
if (!pos) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let layers = this.children,
|
const layers = this.children,
|
||||||
len = layers.length,
|
len = layers.length,
|
||||||
end = len - 1,
|
end = len - 1;
|
||||||
n;
|
|
||||||
|
|
||||||
for (n = end; n >= 0; n--) {
|
for (let n = end; n >= 0; n--) {
|
||||||
const shape = layers[n].getIntersection(pos);
|
const shape = layers[n].getIntersection(pos);
|
||||||
if (shape) {
|
if (shape) {
|
||||||
return shape;
|
return shape;
|
||||||
@ -754,7 +752,7 @@ export class Stage extends Container<Layer> {
|
|||||||
// TODO: are we sure we need to prevent default at all?
|
// TODO: are we sure we need to prevent default at all?
|
||||||
// do not call this function on mobile because it prevent "click" event on all parent containers
|
// do not call this function on mobile because it prevent "click" event on all parent containers
|
||||||
// but apps may listen to it.
|
// but apps may listen to it.
|
||||||
if (evt.cancelable && eventType !== 'touch') {
|
if (evt.cancelable && eventType !== 'touch' && eventType !== 'pointer') {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -820,8 +818,8 @@ export class Stage extends Container<Layer> {
|
|||||||
* });
|
* });
|
||||||
*/
|
*/
|
||||||
setPointersPositions(evt) {
|
setPointersPositions(evt) {
|
||||||
let contentPosition = this._getContentPosition(),
|
const contentPosition = this._getContentPosition();
|
||||||
x: number | null = null,
|
let x: number | null = null,
|
||||||
y: number | null = null;
|
y: number | null = null;
|
||||||
evt = evt ? evt : window.event;
|
evt = evt ? evt : window.event;
|
||||||
|
|
||||||
|
67
src/Tween.ts
67
src/Tween.ts
@ -4,7 +4,7 @@ import { Node, NodeConfig } from './Node';
|
|||||||
import { Konva } from './Global';
|
import { Konva } from './Global';
|
||||||
import { Line } from './shapes/Line';
|
import { Line } from './shapes/Line';
|
||||||
|
|
||||||
let blacklist = {
|
const blacklist = {
|
||||||
node: 1,
|
node: 1,
|
||||||
duration: 1,
|
duration: 1,
|
||||||
easing: 1,
|
easing: 1,
|
||||||
@ -14,8 +14,8 @@ let blacklist = {
|
|||||||
PAUSED = 1,
|
PAUSED = 1,
|
||||||
PLAYING = 2,
|
PLAYING = 2,
|
||||||
REVERSING = 3,
|
REVERSING = 3,
|
||||||
idCounter = 0,
|
|
||||||
colorAttrs = ['fill', 'stroke', 'shadowColor'];
|
colorAttrs = ['fill', 'stroke', 'shadowColor'];
|
||||||
|
let idCounter = 0;
|
||||||
|
|
||||||
class TweenEngine {
|
class TweenEngine {
|
||||||
prop: string;
|
prop: string;
|
||||||
@ -195,13 +195,12 @@ export class Tween {
|
|||||||
onUpdate: Function | undefined;
|
onUpdate: Function | undefined;
|
||||||
|
|
||||||
constructor(config: TweenConfig) {
|
constructor(config: TweenConfig) {
|
||||||
let that = this,
|
const that = this,
|
||||||
node = config.node as any,
|
node = config.node as any,
|
||||||
nodeId = node._id,
|
nodeId = node._id,
|
||||||
duration,
|
|
||||||
easing = config.easing || Easings.Linear,
|
easing = config.easing || Easings.Linear,
|
||||||
yoyo = !!config.yoyo,
|
yoyo = !!config.yoyo;
|
||||||
key;
|
let duration, key;
|
||||||
|
|
||||||
if (typeof config.duration === 'undefined') {
|
if (typeof config.duration === 'undefined') {
|
||||||
duration = 0.3;
|
duration = 0.3;
|
||||||
@ -266,26 +265,19 @@ export class Tween {
|
|||||||
this.onUpdate = config.onUpdate;
|
this.onUpdate = config.onUpdate;
|
||||||
}
|
}
|
||||||
_addAttr(key, end) {
|
_addAttr(key, end) {
|
||||||
let node = this.node,
|
const node = this.node,
|
||||||
nodeId = node._id,
|
nodeId = node._id;
|
||||||
start,
|
let diff, len, trueEnd, trueStart, endRGBA;
|
||||||
diff,
|
|
||||||
tweenId,
|
|
||||||
n,
|
|
||||||
len,
|
|
||||||
trueEnd,
|
|
||||||
trueStart,
|
|
||||||
endRGBA;
|
|
||||||
|
|
||||||
// remove conflict from tween map if it exists
|
// remove conflict from tween map if it exists
|
||||||
tweenId = Tween.tweens[nodeId][key];
|
const tweenId = Tween.tweens[nodeId][key];
|
||||||
|
|
||||||
if (tweenId) {
|
if (tweenId) {
|
||||||
delete Tween.attrs[nodeId][tweenId][key];
|
delete Tween.attrs[nodeId][tweenId][key];
|
||||||
}
|
}
|
||||||
|
|
||||||
// add to tween map
|
// add to tween map
|
||||||
start = node.getAttr(key);
|
let start = node.getAttr(key);
|
||||||
|
|
||||||
if (Util._isArray(end)) {
|
if (Util._isArray(end)) {
|
||||||
diff = [];
|
diff = [];
|
||||||
@ -310,7 +302,7 @@ export class Tween {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (key.indexOf('fill') === 0) {
|
if (key.indexOf('fill') === 0) {
|
||||||
for (n = 0; n < len; n++) {
|
for (let n = 0; n < len; n++) {
|
||||||
if (n % 2 === 0) {
|
if (n % 2 === 0) {
|
||||||
diff.push(end[n] - start[n]);
|
diff.push(end[n] - start[n]);
|
||||||
} else {
|
} else {
|
||||||
@ -326,7 +318,7 @@ export class Tween {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (n = 0; n < len; n++) {
|
for (let n = 0; n < len; n++) {
|
||||||
diff.push(end[n] - start[n]);
|
diff.push(end[n] - start[n]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -353,16 +345,9 @@ export class Tween {
|
|||||||
Tween.tweens[nodeId][key] = this._id;
|
Tween.tweens[nodeId][key] = this._id;
|
||||||
}
|
}
|
||||||
_tweenFunc(i) {
|
_tweenFunc(i) {
|
||||||
let node = this.node,
|
const node = this.node,
|
||||||
attrs = Tween.attrs[node._id][this._id],
|
attrs = Tween.attrs[node._id][this._id];
|
||||||
key,
|
let key, attr, start, diff, newVal, n, len, end;
|
||||||
attr,
|
|
||||||
start,
|
|
||||||
diff,
|
|
||||||
newVal,
|
|
||||||
n,
|
|
||||||
len,
|
|
||||||
end;
|
|
||||||
|
|
||||||
for (key in attrs) {
|
for (key in attrs) {
|
||||||
attr = attrs[key];
|
attr = attrs[key];
|
||||||
@ -525,18 +510,32 @@ export class Tween {
|
|||||||
* @name Konva.Tween#destroy
|
* @name Konva.Tween#destroy
|
||||||
*/
|
*/
|
||||||
destroy() {
|
destroy() {
|
||||||
let nodeId = this.node._id,
|
const nodeId = this.node._id,
|
||||||
thisId = this._id,
|
thisId = this._id,
|
||||||
attrs = Tween.tweens[nodeId],
|
attrs = Tween.tweens[nodeId];
|
||||||
key;
|
|
||||||
|
|
||||||
this.pause();
|
this.pause();
|
||||||
|
|
||||||
for (key in attrs) {
|
// Clean up animation
|
||||||
|
if (this.anim) {
|
||||||
|
this.anim.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up tween entries
|
||||||
|
for (const key in attrs) {
|
||||||
delete Tween.tweens[nodeId][key];
|
delete Tween.tweens[nodeId][key];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clean up attrs entry
|
||||||
delete Tween.attrs[nodeId][thisId];
|
delete Tween.attrs[nodeId][thisId];
|
||||||
|
|
||||||
|
// Clean up parent objects if empty
|
||||||
|
if (Object.keys(Tween.tweens[nodeId]).length === 0) {
|
||||||
|
delete Tween.tweens[nodeId];
|
||||||
|
}
|
||||||
|
if (Object.keys(Tween.attrs[nodeId]).length === 0) {
|
||||||
|
delete Tween.attrs[nodeId];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
13
src/Util.ts
13
src/Util.ts
@ -260,7 +260,7 @@ export class Transform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CONSTANTS
|
// CONSTANTS
|
||||||
let OBJECT_ARRAY = '[object Array]',
|
const OBJECT_ARRAY = '[object Array]',
|
||||||
OBJECT_NUMBER = '[object Number]',
|
OBJECT_NUMBER = '[object Number]',
|
||||||
OBJECT_STRING = '[object String]',
|
OBJECT_STRING = '[object String]',
|
||||||
OBJECT_BOOLEAN = '[object Boolean]',
|
OBJECT_BOOLEAN = '[object Boolean]',
|
||||||
@ -423,8 +423,8 @@ let OBJECT_ARRAY = '[object Array]',
|
|||||||
yellow: [255, 255, 0],
|
yellow: [255, 255, 0],
|
||||||
yellowgreen: [154, 205, 5],
|
yellowgreen: [154, 205, 5],
|
||||||
},
|
},
|
||||||
RGB_REGEX = /rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)/,
|
RGB_REGEX = /rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)/;
|
||||||
animQueue: Array<Function> = [];
|
let animQueue: Array<Function> = [];
|
||||||
|
|
||||||
const req =
|
const req =
|
||||||
(typeof requestAnimationFrame !== 'undefined' && requestAnimationFrame) ||
|
(typeof requestAnimationFrame !== 'undefined' && requestAnimationFrame) ||
|
||||||
@ -904,21 +904,20 @@ export const Util = {
|
|||||||
return pc;
|
return pc;
|
||||||
},
|
},
|
||||||
_prepareArrayForTween(startArray, endArray, isClosed) {
|
_prepareArrayForTween(startArray, endArray, isClosed) {
|
||||||
let n,
|
const start: Vector2d[] = [],
|
||||||
start: Vector2d[] = [],
|
|
||||||
end: Vector2d[] = [];
|
end: Vector2d[] = [];
|
||||||
if (startArray.length > endArray.length) {
|
if (startArray.length > endArray.length) {
|
||||||
const temp = endArray;
|
const temp = endArray;
|
||||||
endArray = startArray;
|
endArray = startArray;
|
||||||
startArray = temp;
|
startArray = temp;
|
||||||
}
|
}
|
||||||
for (n = 0; n < startArray.length; n += 2) {
|
for (let n = 0; n < startArray.length; n += 2) {
|
||||||
start.push({
|
start.push({
|
||||||
x: startArray[n],
|
x: startArray[n],
|
||||||
y: startArray[n + 1],
|
y: startArray[n + 1],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
for (n = 0; n < endArray.length; n += 2) {
|
for (let n = 0; n < endArray.length; n += 2) {
|
||||||
end.push({
|
end.push({
|
||||||
x: endArray[n],
|
x: endArray[n],
|
||||||
y: endArray[n + 1],
|
y: endArray[n + 1],
|
||||||
|
@ -33,9 +33,9 @@ export function alphaComponent(val: number) {
|
|||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getNumberValidator() {
|
export function getNumberValidator<T>() {
|
||||||
if (Konva.isUnminified) {
|
if (Konva.isUnminified) {
|
||||||
return function <T>(val: T, attr: string): T | void {
|
return function (val: T, attr: string): T {
|
||||||
if (!Util._isNumber(val)) {
|
if (!Util._isNumber(val)) {
|
||||||
Util.warn(
|
Util.warn(
|
||||||
_formatValue(val) +
|
_formatValue(val) +
|
||||||
@ -49,11 +49,11 @@ export function getNumberValidator() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getNumberOrArrayOfNumbersValidator(noOfElements: number) {
|
export function getNumberOrArrayOfNumbersValidator<T>(noOfElements: number) {
|
||||||
if (Konva.isUnminified) {
|
if (Konva.isUnminified) {
|
||||||
return function <T>(val: T, attr: string): T | void {
|
return function (val: T, attr: string): T {
|
||||||
const isNumber = Util._isNumber(val);
|
let isNumber = Util._isNumber(val);
|
||||||
const isValidArray = Util._isArray(val) && val.length == noOfElements;
|
let isValidArray = Util._isArray(val) && val.length == noOfElements;
|
||||||
if (!isNumber && !isValidArray) {
|
if (!isNumber && !isValidArray) {
|
||||||
Util.warn(
|
Util.warn(
|
||||||
_formatValue(val) +
|
_formatValue(val) +
|
||||||
@ -69,9 +69,9 @@ export function getNumberOrArrayOfNumbersValidator(noOfElements: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getNumberOrAutoValidator() {
|
export function getNumberOrAutoValidator<T>() {
|
||||||
if (Konva.isUnminified) {
|
if (Konva.isUnminified) {
|
||||||
return function <T extends string>(val: T, attr: string): T | void {
|
return function (val: T, attr: string): T {
|
||||||
const isNumber = Util._isNumber(val);
|
const isNumber = Util._isNumber(val);
|
||||||
const isAuto = val === 'auto';
|
const isAuto = val === 'auto';
|
||||||
|
|
||||||
@ -88,9 +88,9 @@ export function getNumberOrAutoValidator() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getStringValidator() {
|
export function getStringValidator<T>() {
|
||||||
if (Konva.isUnminified) {
|
if (Konva.isUnminified) {
|
||||||
return function (val: any, attr: string) {
|
return function (val: T, attr: string): T {
|
||||||
if (!Util._isString(val)) {
|
if (!Util._isString(val)) {
|
||||||
Util.warn(
|
Util.warn(
|
||||||
_formatValue(val) +
|
_formatValue(val) +
|
||||||
@ -104,13 +104,13 @@ export function getStringValidator() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getStringOrGradientValidator() {
|
export function getStringOrGradientValidator<T>() {
|
||||||
if (Konva.isUnminified) {
|
if (Konva.isUnminified) {
|
||||||
return function (val: any, attr: string) {
|
return function (val: T, attr: string): T {
|
||||||
const isString = Util._isString(val);
|
const isString = Util._isString(val);
|
||||||
const isGradient =
|
const isGradient =
|
||||||
Object.prototype.toString.call(val) === '[object CanvasGradient]' ||
|
Object.prototype.toString.call(val) === '[object CanvasGradient]' ||
|
||||||
(val && val.addColorStop);
|
(val && val['addColorStop']);
|
||||||
if (!(isString || isGradient)) {
|
if (!(isString || isGradient)) {
|
||||||
Util.warn(
|
Util.warn(
|
||||||
_formatValue(val) +
|
_formatValue(val) +
|
||||||
@ -124,9 +124,9 @@ export function getStringOrGradientValidator() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getFunctionValidator() {
|
export function getFunctionValidator<T>() {
|
||||||
if (Konva.isUnminified) {
|
if (Konva.isUnminified) {
|
||||||
return function (val: any, attr: string) {
|
return function (val: T, attr: string): T {
|
||||||
if (!Util._isFunction(val)) {
|
if (!Util._isFunction(val)) {
|
||||||
Util.warn(
|
Util.warn(
|
||||||
_formatValue(val) +
|
_formatValue(val) +
|
||||||
@ -139,9 +139,9 @@ export function getFunctionValidator() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export function getNumberArrayValidator() {
|
export function getNumberArrayValidator<T>() {
|
||||||
if (Konva.isUnminified) {
|
if (Konva.isUnminified) {
|
||||||
return function (val: any, attr: string) {
|
return function (val: T, attr: string): T {
|
||||||
// Retrieve TypedArray constructor as found in MDN (if TypedArray is available)
|
// Retrieve TypedArray constructor as found in MDN (if TypedArray is available)
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray#description
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray#description
|
||||||
const TypedArray = Int8Array ? Object.getPrototypeOf(Int8Array) : null;
|
const TypedArray = Int8Array ? Object.getPrototypeOf(Int8Array) : null;
|
||||||
@ -172,9 +172,9 @@ export function getNumberArrayValidator() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export function getBooleanValidator() {
|
export function getBooleanValidator<T>() {
|
||||||
if (Konva.isUnminified) {
|
if (Konva.isUnminified) {
|
||||||
return function (val: any, attr: string) {
|
return function (val: T, attr: string): T {
|
||||||
const isBool = val === true || val === false;
|
const isBool = val === true || val === false;
|
||||||
if (!isBool) {
|
if (!isBool) {
|
||||||
Util.warn(
|
Util.warn(
|
||||||
@ -188,9 +188,9 @@ export function getBooleanValidator() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export function getComponentValidator(components: any) {
|
export function getComponentValidator<T>(components: string[]) {
|
||||||
if (Konva.isUnminified) {
|
if (Konva.isUnminified) {
|
||||||
return function (val: any, attr: string) {
|
return function (val: T, attr: string): T {
|
||||||
// ignore validation on undefined value, because it will reset to defalt
|
// ignore validation on undefined value, because it will reset to defalt
|
||||||
if (val === undefined || val === null) {
|
if (val === undefined || val === null) {
|
||||||
return val;
|
return val;
|
||||||
|
@ -96,11 +96,7 @@ function filterGaussBlurRGBA(imageData, radius) {
|
|||||||
width = imageData.width,
|
width = imageData.width,
|
||||||
height = imageData.height;
|
height = imageData.height;
|
||||||
|
|
||||||
let x,
|
let p,
|
||||||
y,
|
|
||||||
i,
|
|
||||||
p,
|
|
||||||
yp,
|
|
||||||
yi,
|
yi,
|
||||||
yw,
|
yw,
|
||||||
r_sum,
|
r_sum,
|
||||||
@ -121,20 +117,21 @@ function filterGaussBlurRGBA(imageData, radius) {
|
|||||||
pa,
|
pa,
|
||||||
rbs;
|
rbs;
|
||||||
|
|
||||||
let div = radius + radius + 1,
|
const div = radius + radius + 1,
|
||||||
widthMinus1 = width - 1,
|
widthMinus1 = width - 1,
|
||||||
heightMinus1 = height - 1,
|
heightMinus1 = height - 1,
|
||||||
radiusPlus1 = radius + 1,
|
radiusPlus1 = radius + 1,
|
||||||
sumFactor = (radiusPlus1 * (radiusPlus1 + 1)) / 2,
|
sumFactor = (radiusPlus1 * (radiusPlus1 + 1)) / 2,
|
||||||
stackStart = new BlurStack(),
|
stackStart = new BlurStack(),
|
||||||
stackEnd = null,
|
|
||||||
stack = stackStart,
|
|
||||||
stackIn: any = null,
|
|
||||||
stackOut: any = null,
|
|
||||||
mul_sum = mul_table[radius],
|
mul_sum = mul_table[radius],
|
||||||
shg_sum = shg_table[radius];
|
shg_sum = shg_table[radius];
|
||||||
|
|
||||||
for (i = 1; i < div; i++) {
|
let stackEnd = null,
|
||||||
|
stack = stackStart,
|
||||||
|
stackIn: any = null,
|
||||||
|
stackOut: any = null;
|
||||||
|
|
||||||
|
for (let i = 1; i < div; i++) {
|
||||||
stack = stack.next = new BlurStack();
|
stack = stack.next = new BlurStack();
|
||||||
if (i === radiusPlus1) {
|
if (i === radiusPlus1) {
|
||||||
stackEnd = stack;
|
stackEnd = stack;
|
||||||
@ -145,7 +142,7 @@ function filterGaussBlurRGBA(imageData, radius) {
|
|||||||
|
|
||||||
yw = yi = 0;
|
yw = yi = 0;
|
||||||
|
|
||||||
for (y = 0; y < height; y++) {
|
for (let y = 0; y < height; y++) {
|
||||||
r_in_sum =
|
r_in_sum =
|
||||||
g_in_sum =
|
g_in_sum =
|
||||||
b_in_sum =
|
b_in_sum =
|
||||||
@ -168,7 +165,7 @@ function filterGaussBlurRGBA(imageData, radius) {
|
|||||||
|
|
||||||
stack = stackStart;
|
stack = stackStart;
|
||||||
|
|
||||||
for (i = 0; i < radiusPlus1; i++) {
|
for (let i = 0; i < radiusPlus1; i++) {
|
||||||
stack.r = pr;
|
stack.r = pr;
|
||||||
stack.g = pg;
|
stack.g = pg;
|
||||||
stack.b = pb;
|
stack.b = pb;
|
||||||
@ -176,7 +173,7 @@ function filterGaussBlurRGBA(imageData, radius) {
|
|||||||
stack = stack.next;
|
stack = stack.next;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 1; i < radiusPlus1; i++) {
|
for (let i = 1; i < radiusPlus1; i++) {
|
||||||
p = yi + ((widthMinus1 < i ? widthMinus1 : i) << 2);
|
p = yi + ((widthMinus1 < i ? widthMinus1 : i) << 2);
|
||||||
r_sum += (stack.r = pr = pixels[p]) * (rbs = radiusPlus1 - i);
|
r_sum += (stack.r = pr = pixels[p]) * (rbs = radiusPlus1 - i);
|
||||||
g_sum += (stack.g = pg = pixels[p + 1]) * rbs;
|
g_sum += (stack.g = pg = pixels[p + 1]) * rbs;
|
||||||
@ -193,7 +190,7 @@ function filterGaussBlurRGBA(imageData, radius) {
|
|||||||
|
|
||||||
stackIn = stackStart;
|
stackIn = stackStart;
|
||||||
stackOut = stackEnd;
|
stackOut = stackEnd;
|
||||||
for (x = 0; x < width; x++) {
|
for (let x = 0; x < width; x++) {
|
||||||
pixels[yi + 3] = pa = (a_sum * mul_sum) >> shg_sum;
|
pixels[yi + 3] = pa = (a_sum * mul_sum) >> shg_sum;
|
||||||
if (pa !== 0) {
|
if (pa !== 0) {
|
||||||
pa = 255 / pa;
|
pa = 255 / pa;
|
||||||
@ -245,7 +242,7 @@ function filterGaussBlurRGBA(imageData, radius) {
|
|||||||
yw += width;
|
yw += width;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (x = 0; x < width; x++) {
|
for (let x = 0; x < width; x++) {
|
||||||
g_in_sum =
|
g_in_sum =
|
||||||
b_in_sum =
|
b_in_sum =
|
||||||
a_in_sum =
|
a_in_sum =
|
||||||
@ -269,7 +266,7 @@ function filterGaussBlurRGBA(imageData, radius) {
|
|||||||
|
|
||||||
stack = stackStart;
|
stack = stackStart;
|
||||||
|
|
||||||
for (i = 0; i < radiusPlus1; i++) {
|
for (let i = 0; i < radiusPlus1; i++) {
|
||||||
stack.r = pr;
|
stack.r = pr;
|
||||||
stack.g = pg;
|
stack.g = pg;
|
||||||
stack.b = pb;
|
stack.b = pb;
|
||||||
@ -277,9 +274,9 @@ function filterGaussBlurRGBA(imageData, radius) {
|
|||||||
stack = stack.next;
|
stack = stack.next;
|
||||||
}
|
}
|
||||||
|
|
||||||
yp = width;
|
let yp = width;
|
||||||
|
|
||||||
for (i = 1; i <= radius; i++) {
|
for (let i = 1; i <= radius; i++) {
|
||||||
yi = (yp + x) << 2;
|
yi = (yp + x) << 2;
|
||||||
|
|
||||||
r_sum += (stack.r = pr = pixels[yi]) * (rbs = radiusPlus1 - i);
|
r_sum += (stack.r = pr = pixels[yi]) * (rbs = radiusPlus1 - i);
|
||||||
@ -302,7 +299,7 @@ function filterGaussBlurRGBA(imageData, radius) {
|
|||||||
yi = x;
|
yi = x;
|
||||||
stackIn = stackStart;
|
stackIn = stackStart;
|
||||||
stackOut = stackEnd;
|
stackOut = stackEnd;
|
||||||
for (y = 0; y < height; y++) {
|
for (let y = 0; y < height; y++) {
|
||||||
p = yi << 2;
|
p = yi << 2;
|
||||||
pixels[p + 3] = pa = (a_sum * mul_sum) >> shg_sum;
|
pixels[p + 3] = pa = (a_sum * mul_sum) >> shg_sum;
|
||||||
if (pa > 0) {
|
if (pa > 0) {
|
||||||
|
@ -13,12 +13,11 @@ import { getNumberValidator } from '../Validators';
|
|||||||
* node.brightness(0.8);
|
* node.brightness(0.8);
|
||||||
*/
|
*/
|
||||||
export const Brighten: Filter = function (imageData) {
|
export const Brighten: Filter = function (imageData) {
|
||||||
let brightness = this.brightness() * 255,
|
const brightness = this.brightness() * 255,
|
||||||
data = imageData.data,
|
data = imageData.data,
|
||||||
len = data.length,
|
len = data.length;
|
||||||
i;
|
|
||||||
|
|
||||||
for (i = 0; i < len; i += 4) {
|
for (let i = 0; i < len; i += 4) {
|
||||||
// red
|
// red
|
||||||
data[i] += brightness;
|
data[i] += brightness;
|
||||||
// green
|
// green
|
||||||
|
@ -15,14 +15,13 @@ import { getNumberValidator } from '../Validators';
|
|||||||
export const Contrast: Filter = function (imageData) {
|
export const Contrast: Filter = function (imageData) {
|
||||||
const adjust = Math.pow((this.contrast() + 100) / 100, 2);
|
const adjust = Math.pow((this.contrast() + 100) / 100, 2);
|
||||||
|
|
||||||
let data = imageData.data,
|
const data = imageData.data,
|
||||||
nPixels = data.length,
|
nPixels = data.length;
|
||||||
red = 150,
|
let red = 150,
|
||||||
green = 150,
|
green = 150,
|
||||||
blue = 150,
|
blue = 150;
|
||||||
i;
|
|
||||||
|
|
||||||
for (i = 0; i < nPixels; i += 4) {
|
for (let i = 0; i < nPixels; i += 4) {
|
||||||
red = data[i];
|
red = data[i];
|
||||||
green = data[i + 1];
|
green = data[i + 1];
|
||||||
blue = data[i + 2];
|
blue = data[i + 2];
|
||||||
|
@ -23,16 +23,16 @@ export const Emboss: Filter = function (imageData) {
|
|||||||
// pixastic greyLevel is between 0 and 255. I want it between 0 and 1. Also,
|
// pixastic greyLevel is between 0 and 255. I want it between 0 and 1. Also,
|
||||||
// a max value of greyLevel yields a white emboss, and the min value yields a black
|
// a max value of greyLevel yields a white emboss, and the min value yields a black
|
||||||
// emboss. Therefore, I changed greyLevel to whiteLevel
|
// emboss. Therefore, I changed greyLevel to whiteLevel
|
||||||
let strength = this.embossStrength() * 10,
|
const strength = this.embossStrength() * 10,
|
||||||
greyLevel = this.embossWhiteLevel() * 255,
|
greyLevel = this.embossWhiteLevel() * 255,
|
||||||
direction = this.embossDirection(),
|
direction = this.embossDirection(),
|
||||||
blend = this.embossBlend(),
|
blend = this.embossBlend(),
|
||||||
dirY = 0,
|
|
||||||
dirX = 0,
|
|
||||||
data = imageData.data,
|
data = imageData.data,
|
||||||
w = imageData.width,
|
w = imageData.width,
|
||||||
h = imageData.height,
|
h = imageData.height,
|
||||||
w4 = w * 4,
|
w4 = w * 4;
|
||||||
|
let dirY = 0,
|
||||||
|
dirX = 0,
|
||||||
y = h;
|
y = h;
|
||||||
|
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
@ -174,7 +174,7 @@ Factory.addGetterSetter(
|
|||||||
Node,
|
Node,
|
||||||
'embossDirection',
|
'embossDirection',
|
||||||
'top-left',
|
'top-left',
|
||||||
null,
|
undefined,
|
||||||
Factory.afterSetFilter
|
Factory.afterSetFilter
|
||||||
);
|
);
|
||||||
/**
|
/**
|
||||||
@ -190,7 +190,7 @@ Factory.addGetterSetter(
|
|||||||
Node,
|
Node,
|
||||||
'embossBlend',
|
'embossBlend',
|
||||||
false,
|
false,
|
||||||
null,
|
undefined,
|
||||||
Factory.afterSetFilter
|
Factory.afterSetFilter
|
||||||
);
|
);
|
||||||
/**
|
/**
|
||||||
|
@ -4,9 +4,8 @@ import { getNumberValidator } from '../Validators';
|
|||||||
|
|
||||||
function remap(fromValue: number, fromMin: number, fromMax: number, toMin: number, toMax: number) {
|
function remap(fromValue: number, fromMin: number, fromMax: number, toMin: number, toMax: number) {
|
||||||
// Compute the range of the data
|
// Compute the range of the data
|
||||||
let fromRange = fromMax - fromMin,
|
const fromRange = fromMax - fromMin,
|
||||||
toRange = toMax - toMin,
|
toRange = toMax - toMin;
|
||||||
toValue;
|
|
||||||
|
|
||||||
// If either range is 0, then the value can only be mapped to 1 value
|
// If either range is 0, then the value can only be mapped to 1 value
|
||||||
if (fromRange === 0) {
|
if (fromRange === 0) {
|
||||||
@ -17,7 +16,7 @@ function remap(fromValue: number, fromMin: number, fromMax: number, toMin: numbe
|
|||||||
}
|
}
|
||||||
|
|
||||||
// (1) untranslate, (2) unscale, (3) rescale, (4) retranslate
|
// (1) untranslate, (2) unscale, (3) rescale, (4) retranslate
|
||||||
toValue = (fromValue - fromMin) / fromRange;
|
let toValue = (fromValue - fromMin) / fromRange;
|
||||||
toValue = toRange * toValue + toMin;
|
toValue = toRange * toValue + toMin;
|
||||||
|
|
||||||
return toValue;
|
return toValue;
|
||||||
@ -38,9 +37,9 @@ function remap(fromValue: number, fromMin: number, fromMax: number, toMin: numbe
|
|||||||
* node.enhance(0.4);
|
* node.enhance(0.4);
|
||||||
*/
|
*/
|
||||||
export const Enhance: Filter = function (imageData) {
|
export const Enhance: Filter = function (imageData) {
|
||||||
let data = imageData.data,
|
const data = imageData.data,
|
||||||
nSubPixels = data.length,
|
nSubPixels = data.length;
|
||||||
rMin = data[0],
|
let rMin = data[0],
|
||||||
rMax = rMin,
|
rMax = rMin,
|
||||||
r,
|
r,
|
||||||
gMin = data[1],
|
gMin = data[1],
|
||||||
@ -48,8 +47,7 @@ export const Enhance: Filter = function (imageData) {
|
|||||||
g,
|
g,
|
||||||
bMin = data[2],
|
bMin = data[2],
|
||||||
bMax = bMin,
|
bMax = bMin,
|
||||||
b,
|
b;
|
||||||
i;
|
|
||||||
|
|
||||||
// If we are not enhancing anything - don't do any computation
|
// If we are not enhancing anything - don't do any computation
|
||||||
const enhanceAmount = this.enhance();
|
const enhanceAmount = this.enhance();
|
||||||
@ -58,7 +56,7 @@ export const Enhance: Filter = function (imageData) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 1st Pass - find the min and max for each channel:
|
// 1st Pass - find the min and max for each channel:
|
||||||
for (i = 0; i < nSubPixels; i += 4) {
|
for (let i = 0; i < nSubPixels; i += 4) {
|
||||||
r = data[i + 0];
|
r = data[i + 0];
|
||||||
if (r < rMin) {
|
if (r < rMin) {
|
||||||
rMin = r;
|
rMin = r;
|
||||||
@ -96,15 +94,12 @@ export const Enhance: Filter = function (imageData) {
|
|||||||
bMin = 0;
|
bMin = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let rMid,
|
let rGoalMax: number,
|
||||||
rGoalMax,
|
rGoalMin: number,
|
||||||
rGoalMin,
|
gGoalMax: number,
|
||||||
gMid,
|
gGoalMin: number,
|
||||||
gGoalMax,
|
bGoalMax: number,
|
||||||
gGoalMin,
|
bGoalMin: number;
|
||||||
bMid,
|
|
||||||
bGoalMax,
|
|
||||||
bGoalMin;
|
|
||||||
|
|
||||||
// If the enhancement is positive - stretch the histogram
|
// If the enhancement is positive - stretch the histogram
|
||||||
if (enhanceAmount > 0) {
|
if (enhanceAmount > 0) {
|
||||||
@ -116,19 +111,19 @@ export const Enhance: Filter = function (imageData) {
|
|||||||
bGoalMin = bMin - enhanceAmount * (bMin - 0);
|
bGoalMin = bMin - enhanceAmount * (bMin - 0);
|
||||||
// If the enhancement is negative - compress the histogram
|
// If the enhancement is negative - compress the histogram
|
||||||
} else {
|
} else {
|
||||||
rMid = (rMax + rMin) * 0.5;
|
const rMid = (rMax + rMin) * 0.5;
|
||||||
rGoalMax = rMax + enhanceAmount * (rMax - rMid);
|
rGoalMax = rMax + enhanceAmount * (rMax - rMid);
|
||||||
rGoalMin = rMin + enhanceAmount * (rMin - rMid);
|
rGoalMin = rMin + enhanceAmount * (rMin - rMid);
|
||||||
gMid = (gMax + gMin) * 0.5;
|
const gMid = (gMax + gMin) * 0.5;
|
||||||
gGoalMax = gMax + enhanceAmount * (gMax - gMid);
|
gGoalMax = gMax + enhanceAmount * (gMax - gMid);
|
||||||
gGoalMin = gMin + enhanceAmount * (gMin - gMid);
|
gGoalMin = gMin + enhanceAmount * (gMin - gMid);
|
||||||
bMid = (bMax + bMin) * 0.5;
|
const bMid = (bMax + bMin) * 0.5;
|
||||||
bGoalMax = bMax + enhanceAmount * (bMax - bMid);
|
bGoalMax = bMax + enhanceAmount * (bMax - bMid);
|
||||||
bGoalMin = bMin + enhanceAmount * (bMin - bMid);
|
bGoalMin = bMin + enhanceAmount * (bMin - bMid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass 2 - remap everything, except the alpha
|
// Pass 2 - remap everything, except the alpha
|
||||||
for (i = 0; i < nSubPixels; i += 4) {
|
for (let i = 0; i < nSubPixels; i += 4) {
|
||||||
data[i + 0] = remap(data[i + 0], rMin, rMax, rGoalMin, rGoalMax);
|
data[i + 0] = remap(data[i + 0], rMin, rMax, rGoalMin, rGoalMax);
|
||||||
data[i + 1] = remap(data[i + 1], gMin, gMax, gGoalMin, gGoalMax);
|
data[i + 1] = remap(data[i + 1], gMin, gMax, gGoalMin, gGoalMax);
|
||||||
data[i + 2] = remap(data[i + 2], bMin, bMax, bGoalMin, bGoalMax);
|
data[i + 2] = remap(data[i + 2], bMin, bMax, bGoalMin, bGoalMax);
|
||||||
|
@ -10,13 +10,11 @@ import { Filter } from '../Node';
|
|||||||
* node.filters([Konva.Filters.Grayscale]);
|
* node.filters([Konva.Filters.Grayscale]);
|
||||||
*/
|
*/
|
||||||
export const Grayscale: Filter = function (imageData) {
|
export const Grayscale: Filter = function (imageData) {
|
||||||
let data = imageData.data,
|
const data = imageData.data,
|
||||||
len = data.length,
|
len = data.length;
|
||||||
i,
|
|
||||||
brightness;
|
|
||||||
|
|
||||||
for (i = 0; i < len; i += 4) {
|
for (let i = 0; i < len; i += 4) {
|
||||||
brightness = 0.34 * data[i] + 0.5 * data[i + 1] + 0.16 * data[i + 2];
|
const brightness = 0.34 * data[i] + 0.5 * data[i + 1] + 0.16 * data[i + 2];
|
||||||
// red
|
// red
|
||||||
data[i] = brightness;
|
data[i] = brightness;
|
||||||
// green
|
// green
|
||||||
|
@ -59,13 +59,12 @@ Factory.addGetterSetter(
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export const HSL: Filter = function (imageData) {
|
export const HSL: Filter = function (imageData) {
|
||||||
let data = imageData.data,
|
const data = imageData.data,
|
||||||
nPixels = data.length,
|
nPixels = data.length,
|
||||||
v = 1,
|
v = 1,
|
||||||
s = Math.pow(2, this.saturation()),
|
s = Math.pow(2, this.saturation()),
|
||||||
h = Math.abs(this.hue() + 360) % 360,
|
h = Math.abs(this.hue() + 360) % 360,
|
||||||
l = this.luminance() * 127,
|
l = this.luminance() * 127;
|
||||||
i;
|
|
||||||
|
|
||||||
// Basis for the technique used:
|
// Basis for the technique used:
|
||||||
// http://beesbuzz.biz/code/hsv_color_transforms.php
|
// http://beesbuzz.biz/code/hsv_color_transforms.php
|
||||||
@ -94,7 +93,7 @@ export const HSL: Filter = function (imageData) {
|
|||||||
|
|
||||||
let r: number, g: number, b: number, a: number;
|
let r: number, g: number, b: number, a: number;
|
||||||
|
|
||||||
for (i = 0; i < nPixels; i += 4) {
|
for (let i = 0; i < nPixels; i += 4) {
|
||||||
r = data[i + 0];
|
r = data[i + 0];
|
||||||
g = data[i + 1];
|
g = data[i + 1];
|
||||||
b = data[i + 2];
|
b = data[i + 2];
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Factory } from '../Factory';
|
import { Factory } from '../Factory';
|
||||||
import { Node, Filter } from '../Node';
|
import { Filter, Node } from '../Node';
|
||||||
import { getNumberValidator } from '../Validators';
|
import { getNumberValidator } from '../Validators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -46,13 +46,11 @@ export const HSV: Filter = function (imageData) {
|
|||||||
bg = 0.587 * v - 0.586 * vsu - 1.05 * vsw,
|
bg = 0.587 * v - 0.586 * vsu - 1.05 * vsw,
|
||||||
bb = 0.114 * v + 0.886 * vsu - 0.2 * vsw;
|
bb = 0.114 * v + 0.886 * vsu - 0.2 * vsw;
|
||||||
|
|
||||||
let r, g, b, a;
|
|
||||||
|
|
||||||
for (let i = 0; i < nPixels; i += 4) {
|
for (let i = 0; i < nPixels; i += 4) {
|
||||||
r = data[i + 0];
|
const r = data[i + 0];
|
||||||
g = data[i + 1];
|
const g = data[i + 1];
|
||||||
b = data[i + 2];
|
const b = data[i + 2];
|
||||||
a = data[i + 3];
|
const a = data[i + 3];
|
||||||
|
|
||||||
data[i + 0] = rr * r + rg * g + rb * b;
|
data[i + 0] = rr * r + rg * g + rb * b;
|
||||||
data[i + 1] = gr * r + gg * g + gb * b;
|
data[i + 1] = gr * r + gg * g + gb * b;
|
||||||
|
@ -9,11 +9,10 @@ import { Filter } from '../Node';
|
|||||||
* node.filters([Konva.Filters.Invert]);
|
* node.filters([Konva.Filters.Invert]);
|
||||||
*/
|
*/
|
||||||
export const Invert: Filter = function (imageData) {
|
export const Invert: Filter = function (imageData) {
|
||||||
let data = imageData.data,
|
const data = imageData.data,
|
||||||
len = data.length,
|
len = data.length;
|
||||||
i;
|
|
||||||
|
|
||||||
for (i = 0; i < len; i += 4) {
|
for (let i = 0; i < len; i += 4) {
|
||||||
// red
|
// red
|
||||||
data[i] = 255 - data[i];
|
data[i] = 255 - data[i];
|
||||||
// green
|
// green
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Factory } from '../Factory';
|
import { Factory } from '../Factory';
|
||||||
import { Node, Filter } from '../Node';
|
import { Filter, Node } from '../Node';
|
||||||
import { Util } from '../Util';
|
import { Util } from '../Util';
|
||||||
import { getNumberValidator } from '../Validators';
|
import { getNumberValidator } from '../Validators';
|
||||||
|
|
||||||
@ -20,53 +20,41 @@ import { getNumberValidator } from '../Validators';
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const ToPolar = function (src, dst, opt) {
|
const ToPolar = function (src, dst, opt) {
|
||||||
let srcPixels = src.data,
|
const srcPixels = src.data,
|
||||||
dstPixels = dst.data,
|
dstPixels = dst.data,
|
||||||
xSize = src.width,
|
xSize = src.width,
|
||||||
ySize = src.height,
|
ySize = src.height,
|
||||||
xMid = opt.polarCenterX || xSize / 2,
|
xMid = opt.polarCenterX || xSize / 2,
|
||||||
yMid = opt.polarCenterY || ySize / 2,
|
yMid = opt.polarCenterY || ySize / 2;
|
||||||
i,
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
r = 0,
|
|
||||||
g = 0,
|
|
||||||
b = 0,
|
|
||||||
a = 0;
|
|
||||||
|
|
||||||
// Find the largest radius
|
// Find the largest radius
|
||||||
let rad,
|
let rMax = Math.sqrt(xMid * xMid + yMid * yMid);
|
||||||
rMax = Math.sqrt(xMid * xMid + yMid * yMid);
|
let x = xSize - xMid;
|
||||||
x = xSize - xMid;
|
let y = ySize - yMid;
|
||||||
y = ySize - yMid;
|
const rad = Math.sqrt(x * x + y * y);
|
||||||
rad = Math.sqrt(x * x + y * y);
|
|
||||||
rMax = rad > rMax ? rad : rMax;
|
rMax = rad > rMax ? rad : rMax;
|
||||||
|
|
||||||
// We'll be uisng y as the radius, and x as the angle (theta=t)
|
// We'll be uisng y as the radius, and x as the angle (theta=t)
|
||||||
let rSize = ySize,
|
const rSize = ySize,
|
||||||
tSize = xSize,
|
tSize = xSize;
|
||||||
radius,
|
|
||||||
theta;
|
|
||||||
|
|
||||||
// We want to cover all angles (0-360) and we need to convert to
|
// We want to cover all angles (0-360) and we need to convert to
|
||||||
// radians (*PI/180)
|
// radians (*PI/180)
|
||||||
let conversion = ((360 / tSize) * Math.PI) / 180,
|
const conversion = ((360 / tSize) * Math.PI) / 180;
|
||||||
sin,
|
|
||||||
cos;
|
|
||||||
|
|
||||||
// var x1, x2, x1i, x2i, y1, y2, y1i, y2i, scale;
|
// var x1, x2, x1i, x2i, y1, y2, y1i, y2i, scale;
|
||||||
|
|
||||||
for (theta = 0; theta < tSize; theta += 1) {
|
for (let theta = 0; theta < tSize; theta += 1) {
|
||||||
sin = Math.sin(theta * conversion);
|
const sin = Math.sin(theta * conversion);
|
||||||
cos = Math.cos(theta * conversion);
|
const cos = Math.cos(theta * conversion);
|
||||||
for (radius = 0; radius < rSize; radius += 1) {
|
for (let radius = 0; radius < rSize; radius += 1) {
|
||||||
x = Math.floor(xMid + ((rMax * radius) / rSize) * cos);
|
x = Math.floor(xMid + ((rMax * radius) / rSize) * cos);
|
||||||
y = Math.floor(yMid + ((rMax * radius) / rSize) * sin);
|
y = Math.floor(yMid + ((rMax * radius) / rSize) * sin);
|
||||||
i = (y * xSize + x) * 4;
|
let i = (y * xSize + x) * 4;
|
||||||
r = srcPixels[i + 0];
|
const r = srcPixels[i + 0];
|
||||||
g = srcPixels[i + 1];
|
const g = srcPixels[i + 1];
|
||||||
b = srcPixels[i + 2];
|
const b = srcPixels[i + 2];
|
||||||
a = srcPixels[i + 3];
|
const a = srcPixels[i + 3];
|
||||||
|
|
||||||
// Store it
|
// Store it
|
||||||
//i = (theta * xSize + radius) * 4;
|
//i = (theta * xSize + radius) * 4;
|
||||||
@ -97,35 +85,23 @@ const ToPolar = function (src, dst, opt) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const FromPolar = function (src, dst, opt) {
|
const FromPolar = function (src, dst, opt) {
|
||||||
let srcPixels = src.data,
|
const srcPixels = src.data,
|
||||||
dstPixels = dst.data,
|
dstPixels = dst.data,
|
||||||
xSize = src.width,
|
xSize = src.width,
|
||||||
ySize = src.height,
|
ySize = src.height,
|
||||||
xMid = opt.polarCenterX || xSize / 2,
|
xMid = opt.polarCenterX || xSize / 2,
|
||||||
yMid = opt.polarCenterY || ySize / 2,
|
yMid = opt.polarCenterY || ySize / 2;
|
||||||
i,
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
dx,
|
|
||||||
dy,
|
|
||||||
r = 0,
|
|
||||||
g = 0,
|
|
||||||
b = 0,
|
|
||||||
a = 0;
|
|
||||||
|
|
||||||
// Find the largest radius
|
// Find the largest radius
|
||||||
let rad,
|
let rMax = Math.sqrt(xMid * xMid + yMid * yMid);
|
||||||
rMax = Math.sqrt(xMid * xMid + yMid * yMid);
|
let x = xSize - xMid;
|
||||||
x = xSize - xMid;
|
let y = ySize - yMid;
|
||||||
y = ySize - yMid;
|
const rad = Math.sqrt(x * x + y * y);
|
||||||
rad = Math.sqrt(x * x + y * y);
|
|
||||||
rMax = rad > rMax ? rad : rMax;
|
rMax = rad > rMax ? rad : rMax;
|
||||||
|
|
||||||
// We'll be uisng x as the radius, and y as the angle (theta=t)
|
// We'll be uisng x as the radius, and y as the angle (theta=t)
|
||||||
let rSize = ySize,
|
const rSize = ySize,
|
||||||
tSize = xSize,
|
tSize = xSize,
|
||||||
radius,
|
|
||||||
theta,
|
|
||||||
phaseShift = opt.polarRotation || 0;
|
phaseShift = opt.polarRotation || 0;
|
||||||
|
|
||||||
// We need to convert to degrees and we need to make sure
|
// We need to convert to degrees and we need to make sure
|
||||||
@ -137,18 +113,18 @@ const FromPolar = function (src, dst, opt) {
|
|||||||
|
|
||||||
for (x = 0; x < xSize; x += 1) {
|
for (x = 0; x < xSize; x += 1) {
|
||||||
for (y = 0; y < ySize; y += 1) {
|
for (y = 0; y < ySize; y += 1) {
|
||||||
dx = x - xMid;
|
const dx = x - xMid;
|
||||||
dy = y - yMid;
|
const dy = y - yMid;
|
||||||
radius = (Math.sqrt(dx * dx + dy * dy) * rSize) / rMax;
|
const radius = (Math.sqrt(dx * dx + dy * dy) * rSize) / rMax;
|
||||||
theta = ((Math.atan2(dy, dx) * 180) / Math.PI + 360 + phaseShift) % 360;
|
let theta = ((Math.atan2(dy, dx) * 180) / Math.PI + 360 + phaseShift) % 360;
|
||||||
theta = (theta * tSize) / 360;
|
theta = (theta * tSize) / 360;
|
||||||
x1 = Math.floor(theta);
|
x1 = Math.floor(theta);
|
||||||
y1 = Math.floor(radius);
|
y1 = Math.floor(radius);
|
||||||
i = (y1 * xSize + x1) * 4;
|
let i = (y1 * xSize + x1) * 4;
|
||||||
r = srcPixels[i + 0];
|
const r = srcPixels[i + 0];
|
||||||
g = srcPixels[i + 1];
|
const g = srcPixels[i + 1];
|
||||||
b = srcPixels[i + 2];
|
const b = srcPixels[i + 2];
|
||||||
a = srcPixels[i + 3];
|
const a = srcPixels[i + 3];
|
||||||
|
|
||||||
// Store it
|
// Store it
|
||||||
i = (y * xSize + x) * 4;
|
i = (y * xSize + x) * 4;
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { Factory } from '../Factory';
|
import { Factory } from '../Factory';
|
||||||
import { Node, Filter } from '../Node';
|
import { Filter, Node } from '../Node';
|
||||||
import { getNumberValidator } from '../Validators';
|
import { getNumberValidator } from '../Validators';
|
||||||
|
|
||||||
function pixelAt(idata, x, y) {
|
function pixelAt(idata, x: number, y: number) {
|
||||||
let idx = (y * idata.width + x) * 4;
|
let idx = (y * idata.width + x) * 4;
|
||||||
const d: Array<number> = [];
|
const d: Array<number> = [];
|
||||||
d.push(
|
d.push(
|
||||||
@ -137,7 +137,7 @@ function dilateMask(mask, sw, sh) {
|
|||||||
return maskResult;
|
return maskResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
function smoothEdgeMask(mask, sw, sh) {
|
function smoothEdgeMask(mask, sw: number, sh: number) {
|
||||||
const weights = [1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9];
|
const weights = [1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9];
|
||||||
const side = Math.round(Math.sqrt(weights.length));
|
const side = Math.round(Math.sqrt(weights.length));
|
||||||
const halfSide = Math.floor(side / 2);
|
const halfSide = Math.floor(side / 2);
|
||||||
@ -181,8 +181,8 @@ function smoothEdgeMask(mask, sw, sh) {
|
|||||||
*/
|
*/
|
||||||
export const Mask: Filter = function (imageData) {
|
export const Mask: Filter = function (imageData) {
|
||||||
// Detect pixels close to the background color
|
// Detect pixels close to the background color
|
||||||
let threshold = this.threshold(),
|
const threshold = this.threshold();
|
||||||
mask = backgroundMask(imageData, threshold);
|
let mask = backgroundMask(imageData, threshold);
|
||||||
if (mask) {
|
if (mask) {
|
||||||
// Erode
|
// Erode
|
||||||
mask = erodeMask(mask, imageData.width, imageData.height);
|
mask = erodeMask(mask, imageData.width, imageData.height);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Factory } from '../Factory';
|
import { Factory } from '../Factory';
|
||||||
import { Node, Filter } from '../Node';
|
import { Filter, Node } from '../Node';
|
||||||
import { getNumberValidator } from '../Validators';
|
import { getNumberValidator } from '../Validators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import { Factory } from '../Factory';
|
import { Factory } from '../Factory';
|
||||||
import { Util } from '../Util';
|
import { Util } from '../Util';
|
||||||
import { Node, Filter } from '../Node';
|
import { Node, Filter } from '../Node';
|
||||||
@ -22,23 +21,9 @@ export const Pixelate: Filter = function (imageData) {
|
|||||||
let pixelSize = Math.ceil(this.pixelSize()),
|
let pixelSize = Math.ceil(this.pixelSize()),
|
||||||
width = imageData.width,
|
width = imageData.width,
|
||||||
height = imageData.height,
|
height = imageData.height,
|
||||||
x,
|
|
||||||
y,
|
|
||||||
i,
|
|
||||||
//pixelsPerBin = pixelSize * pixelSize,
|
//pixelsPerBin = pixelSize * pixelSize,
|
||||||
red,
|
|
||||||
green,
|
|
||||||
blue,
|
|
||||||
alpha,
|
|
||||||
nBinsX = Math.ceil(width / pixelSize),
|
nBinsX = Math.ceil(width / pixelSize),
|
||||||
nBinsY = Math.ceil(height / pixelSize),
|
nBinsY = Math.ceil(height / pixelSize),
|
||||||
xBinStart,
|
|
||||||
xBinEnd,
|
|
||||||
yBinStart,
|
|
||||||
yBinEnd,
|
|
||||||
xBin,
|
|
||||||
yBin,
|
|
||||||
pixelsInBin,
|
|
||||||
data = imageData.data;
|
data = imageData.data;
|
||||||
|
|
||||||
if (pixelSize <= 0) {
|
if (pixelSize <= 0) {
|
||||||
@ -46,31 +31,31 @@ export const Pixelate: Filter = function (imageData) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (xBin = 0; xBin < nBinsX; xBin += 1) {
|
for (let xBin = 0; xBin < nBinsX; xBin += 1) {
|
||||||
for (yBin = 0; yBin < nBinsY; yBin += 1) {
|
for (let yBin = 0; yBin < nBinsY; yBin += 1) {
|
||||||
// Initialize the color accumlators to 0
|
// Initialize the color accumlators to 0
|
||||||
red = 0;
|
let red = 0;
|
||||||
green = 0;
|
let green = 0;
|
||||||
blue = 0;
|
let blue = 0;
|
||||||
alpha = 0;
|
let alpha = 0;
|
||||||
|
|
||||||
// Determine which pixels are included in this bin
|
// Determine which pixels are included in this bin
|
||||||
xBinStart = xBin * pixelSize;
|
const xBinStart = xBin * pixelSize;
|
||||||
xBinEnd = xBinStart + pixelSize;
|
const xBinEnd = xBinStart + pixelSize;
|
||||||
yBinStart = yBin * pixelSize;
|
const yBinStart = yBin * pixelSize;
|
||||||
yBinEnd = yBinStart + pixelSize;
|
const yBinEnd = yBinStart + pixelSize;
|
||||||
|
|
||||||
// Add all of the pixels to this bin!
|
// Add all of the pixels to this bin!
|
||||||
pixelsInBin = 0;
|
let pixelsInBin = 0;
|
||||||
for (x = xBinStart; x < xBinEnd; x += 1) {
|
for (let x = xBinStart; x < xBinEnd; x += 1) {
|
||||||
if (x >= width) {
|
if (x >= width) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (y = yBinStart; y < yBinEnd; y += 1) {
|
for (let y = yBinStart; y < yBinEnd; y += 1) {
|
||||||
if (y >= height) {
|
if (y >= height) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
i = (width * y + x) * 4;
|
const i = (width * y + x) * 4;
|
||||||
red += data[i + 0];
|
red += data[i + 0];
|
||||||
green += data[i + 1];
|
green += data[i + 1];
|
||||||
blue += data[i + 2];
|
blue += data[i + 2];
|
||||||
@ -86,15 +71,15 @@ export const Pixelate: Filter = function (imageData) {
|
|||||||
alpha = alpha / pixelsInBin;
|
alpha = alpha / pixelsInBin;
|
||||||
|
|
||||||
// Draw this bin
|
// Draw this bin
|
||||||
for (x = xBinStart; x < xBinEnd; x += 1) {
|
for (let x = xBinStart; x < xBinEnd; x += 1) {
|
||||||
if (x >= width) {
|
if (x >= width) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (y = yBinStart; y < yBinEnd; y += 1) {
|
for (let y = yBinStart; y < yBinEnd; y += 1) {
|
||||||
if (y >= height) {
|
if (y >= height) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
i = (width * y + x) * 4;
|
const i = (width * y + x) * 4;
|
||||||
data[i + 0] = red;
|
data[i + 0] = red;
|
||||||
data[i + 1] = green;
|
data[i + 1] = green;
|
||||||
data[i + 2] = blue;
|
data[i + 2] = blue;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Factory } from '../Factory';
|
import { Factory } from '../Factory';
|
||||||
import { Node, Filter } from '../Node';
|
import { Filter, Node } from '../Node';
|
||||||
import { getNumberValidator } from '../Validators';
|
import { getNumberValidator } from '../Validators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Posterize Filter. Adjusts the channels so that there are no more
|
* Posterize Filter. Adjusts the channels so that there are no more
|
||||||
* than n different values for that channel. This is also applied
|
* than n different values for that channel. This is also applied
|
||||||
@ -15,16 +16,14 @@ import { getNumberValidator } from '../Validators';
|
|||||||
* node.filters([Konva.Filters.Posterize]);
|
* node.filters([Konva.Filters.Posterize]);
|
||||||
* node.levels(0.8); // between 0 and 1
|
* node.levels(0.8); // between 0 and 1
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const Posterize: Filter = function (imageData) {
|
export const Posterize: Filter = function (imageData) {
|
||||||
// level must be between 1 and 255
|
// level must be between 1 and 255
|
||||||
let levels = Math.round(this.levels() * 254) + 1,
|
const levels = Math.round(this.levels() * 254) + 1,
|
||||||
data = imageData.data,
|
data = imageData.data,
|
||||||
len = data.length,
|
len = data.length,
|
||||||
scale = 255 / levels,
|
scale = 255 / levels;
|
||||||
i;
|
|
||||||
|
|
||||||
for (i = 0; i < len; i += 1) {
|
for (let i = 0; i < len; i += 1) {
|
||||||
data[i] = Math.floor(data[i] / scale) * scale;
|
data[i] = Math.floor(data[i] / scale) * scale;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -15,18 +15,15 @@ import { RGBComponent } from '../Validators';
|
|||||||
* node.blue(120);
|
* node.blue(120);
|
||||||
* node.green(200);
|
* node.green(200);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const RGB: Filter = function (imageData) {
|
export const RGB: Filter = function (imageData) {
|
||||||
let data = imageData.data,
|
const data = imageData.data,
|
||||||
nPixels = data.length,
|
nPixels = data.length,
|
||||||
red = this.red(),
|
red = this.red(),
|
||||||
green = this.green(),
|
green = this.green(),
|
||||||
blue = this.blue(),
|
blue = this.blue();
|
||||||
i,
|
|
||||||
brightness;
|
|
||||||
|
|
||||||
for (i = 0; i < nPixels; i += 4) {
|
for (let i = 0; i < nPixels; i += 4) {
|
||||||
brightness =
|
const brightness =
|
||||||
(0.34 * data[i] + 0.5 * data[i + 1] + 0.16 * data[i + 2]) / 255;
|
(0.34 * data[i] + 0.5 * data[i + 1] + 0.16 * data[i + 2]) / 255;
|
||||||
data[i] = brightness * red; // r
|
data[i] = brightness * red; // r
|
||||||
data[i + 1] = brightness * green; // g
|
data[i + 1] = brightness * green; // g
|
||||||
|
@ -12,17 +12,13 @@ import { Filter } from '../Node';
|
|||||||
* node.filters([Konva.Filters.Sepia]);
|
* node.filters([Konva.Filters.Sepia]);
|
||||||
*/
|
*/
|
||||||
export const Sepia: Filter = function (imageData) {
|
export const Sepia: Filter = function (imageData) {
|
||||||
let data = imageData.data,
|
const data = imageData.data,
|
||||||
nPixels = data.length,
|
nPixels = data.length;
|
||||||
i,
|
|
||||||
r,
|
|
||||||
g,
|
|
||||||
b;
|
|
||||||
|
|
||||||
for (i = 0; i < nPixels; i += 4) {
|
for (let i = 0; i < nPixels; i += 4) {
|
||||||
r = data[i + 0];
|
const r = data[i + 0];
|
||||||
g = data[i + 1];
|
const g = data[i + 1];
|
||||||
b = data[i + 2];
|
const b = data[i + 2];
|
||||||
|
|
||||||
data[i + 0] = Math.min(255, r * 0.393 + g * 0.769 + b * 0.189);
|
data[i + 0] = Math.min(255, r * 0.393 + g * 0.769 + b * 0.189);
|
||||||
data[i + 1] = Math.min(255, r * 0.349 + g * 0.686 + b * 0.168);
|
data[i + 1] = Math.min(255, r * 0.349 + g * 0.686 + b * 0.168);
|
||||||
|
@ -98,7 +98,12 @@ export class Arc extends Shape<ArcConfig> {
|
|||||||
|
|
||||||
Arc.prototype._centroid = true;
|
Arc.prototype._centroid = true;
|
||||||
Arc.prototype.className = 'Arc';
|
Arc.prototype.className = 'Arc';
|
||||||
Arc.prototype._attrsAffectingSize = ['innerRadius', 'outerRadius'];
|
Arc.prototype._attrsAffectingSize = [
|
||||||
|
'innerRadius',
|
||||||
|
'outerRadius',
|
||||||
|
'angle',
|
||||||
|
'clockwise',
|
||||||
|
];
|
||||||
_registerNode(Arc);
|
_registerNode(Arc);
|
||||||
|
|
||||||
// add getters setters
|
// add getters setters
|
||||||
|
@ -151,9 +151,9 @@ export class Arrow extends Line<ArrowConfig> {
|
|||||||
const lineRect = super.getSelfRect();
|
const lineRect = super.getSelfRect();
|
||||||
const offset = this.pointerWidth() / 2;
|
const offset = this.pointerWidth() / 2;
|
||||||
return {
|
return {
|
||||||
x: lineRect.x - offset,
|
x: lineRect.x,
|
||||||
y: lineRect.y - offset,
|
y: lineRect.y - offset,
|
||||||
width: lineRect.width + offset * 2,
|
width: lineRect.width,
|
||||||
height: lineRect.height + offset * 2,
|
height: lineRect.height + offset * 2,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -40,9 +40,16 @@ export interface ImageConfig extends ShapeConfig {
|
|||||||
* imageObj.src = '/path/to/image.jpg'
|
* imageObj.src = '/path/to/image.jpg'
|
||||||
*/
|
*/
|
||||||
export class Image extends Shape<ImageConfig> {
|
export class Image extends Shape<ImageConfig> {
|
||||||
|
private _loadListener: () => void;
|
||||||
|
|
||||||
constructor(attrs: ImageConfig) {
|
constructor(attrs: ImageConfig) {
|
||||||
super(attrs);
|
super(attrs);
|
||||||
this.on('imageChange.konva', () => {
|
this._loadListener = () => {
|
||||||
|
this._requestDraw();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.on('imageChange.konva', (props: any) => {
|
||||||
|
this._removeImageLoad(props.oldVal);
|
||||||
this._setImageLoad();
|
this._setImageLoad();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -59,11 +66,19 @@ export class Image extends Shape<ImageConfig> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (image && image['addEventListener']) {
|
if (image && image['addEventListener']) {
|
||||||
image['addEventListener']('load', () => {
|
image['addEventListener']('load', this._loadListener);
|
||||||
this._requestDraw();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_removeImageLoad(image: any) {
|
||||||
|
if (image && image['removeEventListener']) {
|
||||||
|
image['removeEventListener']('load', this._loadListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
destroy() {
|
||||||
|
this._removeImageLoad(this.image());
|
||||||
|
super.destroy();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
_useBufferCanvas() {
|
_useBufferCanvas() {
|
||||||
const hasCornerRadius = !!this.cornerRadius();
|
const hasCornerRadius = !!this.cornerRadius();
|
||||||
const hasShadow = this.hasShadow();
|
const hasShadow = this.hasShadow();
|
||||||
@ -148,7 +163,6 @@ export class Image extends Shape<ImageConfig> {
|
|||||||
* Konva.Image.fromURL(imageURL, function(image){
|
* Konva.Image.fromURL(imageURL, function(image){
|
||||||
* // image is Konva.Image instance
|
* // image is Konva.Image instance
|
||||||
* layer.add(image);
|
* layer.add(image);
|
||||||
* layer.draw();
|
|
||||||
* });
|
* });
|
||||||
*/
|
*/
|
||||||
static fromURL(
|
static fromURL(
|
||||||
|
@ -317,7 +317,10 @@ export class Tag extends Shape<TagConfig> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pointerDirection: GetSet<'left' | 'top' | 'right' | 'bottom' | 'up' | 'down', this>;
|
pointerDirection: GetSet<
|
||||||
|
'left' | 'up' | 'right' | 'down' | typeof NONE,
|
||||||
|
this
|
||||||
|
>;
|
||||||
pointerWidth: GetSet<number, this>;
|
pointerWidth: GetSet<number, this>;
|
||||||
pointerHeight: GetSet<number, this>;
|
pointerHeight: GetSet<number, this>;
|
||||||
cornerRadius: GetSet<number, this>;
|
cornerRadius: GetSet<number, this>;
|
||||||
|
@ -6,7 +6,15 @@ import { getNumberArrayValidator, getNumberValidator } from '../Validators';
|
|||||||
import { Context } from '../Context';
|
import { Context } from '../Context';
|
||||||
import { GetSet } from '../types';
|
import { GetSet } from '../types';
|
||||||
|
|
||||||
function getControlPoints(x0, y0, x1, y1, x2, y2, t) {
|
function getControlPoints(
|
||||||
|
x0: number,
|
||||||
|
y0: number,
|
||||||
|
x1: number,
|
||||||
|
y1: number,
|
||||||
|
x2: number,
|
||||||
|
y2: number,
|
||||||
|
t: number
|
||||||
|
) {
|
||||||
const d01 = Math.sqrt(Math.pow(x1 - x0, 2) + Math.pow(y1 - y0, 2)),
|
const d01 = Math.sqrt(Math.pow(x1 - x0, 2) + Math.pow(y1 - y0, 2)),
|
||||||
d12 = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)),
|
d12 = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)),
|
||||||
fa = (t * d01) / (d01 + d12),
|
fa = (t * d01) / (d01 + d12),
|
||||||
@ -102,26 +110,24 @@ export class Line<
|
|||||||
}
|
}
|
||||||
|
|
||||||
_sceneFunc(context: Context) {
|
_sceneFunc(context: Context) {
|
||||||
let points = this.points(),
|
const points = this.points(),
|
||||||
length = points.length,
|
length = points.length,
|
||||||
tension = this.tension(),
|
tension = this.tension(),
|
||||||
closed = this.closed(),
|
closed = this.closed(),
|
||||||
bezier = this.bezier(),
|
bezier = this.bezier();
|
||||||
tp,
|
|
||||||
len,
|
|
||||||
n;
|
|
||||||
|
|
||||||
if (!length) {
|
if (!length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
let n = 0;
|
||||||
|
|
||||||
context.beginPath();
|
context.beginPath();
|
||||||
context.moveTo(points[0], points[1]);
|
context.moveTo(points[0], points[1]);
|
||||||
|
|
||||||
// tension
|
// tension
|
||||||
if (tension !== 0 && length > 4) {
|
if (tension !== 0 && length > 4) {
|
||||||
tp = this.getTensionPoints();
|
const tp = this.getTensionPoints();
|
||||||
len = tp.length;
|
const len = tp.length;
|
||||||
n = closed ? 0 : 4;
|
n = closed ? 0 : 4;
|
||||||
|
|
||||||
if (!closed) {
|
if (!closed) {
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { Factory } from '../Factory';
|
import { Factory } from '../Factory';
|
||||||
import { Shape, ShapeConfig } from '../Shape';
|
|
||||||
import { _registerNode } from '../Global';
|
import { _registerNode } from '../Global';
|
||||||
|
import { Shape, ShapeConfig } from '../Shape';
|
||||||
|
|
||||||
import { GetSet, PathSegment } from '../types';
|
|
||||||
import {
|
import {
|
||||||
getCubicArcLength,
|
getCubicArcLength,
|
||||||
getQuadraticArcLength,
|
getQuadraticArcLength,
|
||||||
t2length,
|
t2length,
|
||||||
} from '../BezierFunctions';
|
} from '../BezierFunctions';
|
||||||
|
import { GetSet, PathSegment } from '../types';
|
||||||
|
|
||||||
export interface PathConfig extends ShapeConfig {
|
export interface PathConfig extends ShapeConfig {
|
||||||
data?: string;
|
data?: string;
|
||||||
@ -73,7 +73,7 @@ export class Path extends Shape<PathConfig> {
|
|||||||
context.quadraticCurveTo(p[0], p[1], p[2], p[3]);
|
context.quadraticCurveTo(p[0], p[1], p[2], p[3]);
|
||||||
break;
|
break;
|
||||||
case 'A':
|
case 'A':
|
||||||
var cx = p[0],
|
const cx = p[0],
|
||||||
cy = p[1],
|
cy = p[1],
|
||||||
rx = p[2],
|
rx = p[2],
|
||||||
ry = p[3],
|
ry = p[3],
|
||||||
@ -82,9 +82,9 @@ export class Path extends Shape<PathConfig> {
|
|||||||
psi = p[6],
|
psi = p[6],
|
||||||
fs = p[7];
|
fs = p[7];
|
||||||
|
|
||||||
var r = rx > ry ? rx : ry;
|
const r = rx > ry ? rx : ry;
|
||||||
var scaleX = rx > ry ? 1 : rx / ry;
|
const scaleX = rx > ry ? 1 : rx / ry;
|
||||||
var scaleY = rx > ry ? ry / rx : 1;
|
const scaleY = rx > ry ? ry / rx : 1;
|
||||||
|
|
||||||
context.translate(cx, cy);
|
context.translate(cx, cy);
|
||||||
context.rotate(psi);
|
context.rotate(psi);
|
||||||
@ -217,7 +217,7 @@ export class Path extends Shape<PathConfig> {
|
|||||||
* @example
|
* @example
|
||||||
* var point = path.getPointAtLength(10);
|
* var point = path.getPointAtLength(10);
|
||||||
*/
|
*/
|
||||||
getPointAtLength(length) {
|
getPointAtLength(length: number) {
|
||||||
return Path.getPointAtLengthOfDataArray(length, this.dataArray);
|
return Path.getPointAtLengthOfDataArray(length, this.dataArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,11 +258,19 @@ export class Path extends Shape<PathConfig> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (length < 0.01) {
|
if (length < 0.01) {
|
||||||
points = dataArray[i].points.slice(0, 2);
|
const cmd = dataArray[i].command;
|
||||||
return {
|
if (cmd === 'M') {
|
||||||
x: points[0],
|
points = dataArray[i].points.slice(0, 2);
|
||||||
y: points[1],
|
return {
|
||||||
};
|
x: points[0],
|
||||||
|
y: points[1],
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
x: dataArray[i].start.x,
|
||||||
|
y: dataArray[i].start.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const cp = dataArray[i];
|
const cp = dataArray[i];
|
||||||
@ -305,13 +313,13 @@ export class Path extends Shape<PathConfig> {
|
|||||||
p[3]
|
p[3]
|
||||||
);
|
);
|
||||||
case 'A':
|
case 'A':
|
||||||
var cx = p[0],
|
const cx = p[0],
|
||||||
cy = p[1],
|
cy = p[1],
|
||||||
rx = p[2],
|
rx = p[2],
|
||||||
ry = p[3],
|
ry = p[3],
|
||||||
theta = p[4],
|
|
||||||
dTheta = p[5],
|
dTheta = p[5],
|
||||||
psi = p[6];
|
psi = p[6];
|
||||||
|
let theta = p[4];
|
||||||
theta += (dTheta * length) / cp.pathLength;
|
theta += (dTheta * length) / cp.pathLength;
|
||||||
return Path.getPointOnEllipticalArc(cx, cy, rx, ry, theta, psi);
|
return Path.getPointOnEllipticalArc(cx, cy, rx, ry, theta, psi);
|
||||||
}
|
}
|
||||||
@ -362,26 +370,33 @@ export class Path extends Shape<PathConfig> {
|
|||||||
return { x: ix + adjustedRun, y: iy + adjustedRise };
|
return { x: ix + adjustedRun, y: iy + adjustedRise };
|
||||||
}
|
}
|
||||||
|
|
||||||
static getPointOnCubicBezier(pct, P1x, P1y, P2x, P2y, P3x, P3y, P4x, P4y) {
|
static getPointOnCubicBezier(
|
||||||
function CB1(t) {
|
pct: number,
|
||||||
|
P1x: number,
|
||||||
|
P1y: number,
|
||||||
|
P2x: number,
|
||||||
|
P2y: number,
|
||||||
|
P3x: number,
|
||||||
|
P3y: number,
|
||||||
|
P4x: number,
|
||||||
|
P4y: number
|
||||||
|
) {
|
||||||
|
function CB1(t: number) {
|
||||||
return t * t * t;
|
return t * t * t;
|
||||||
}
|
}
|
||||||
function CB2(t) {
|
function CB2(t: number) {
|
||||||
return 3 * t * t * (1 - t);
|
return 3 * t * t * (1 - t);
|
||||||
}
|
}
|
||||||
function CB3(t) {
|
function CB3(t: number) {
|
||||||
return 3 * t * (1 - t) * (1 - t);
|
return 3 * t * (1 - t) * (1 - t);
|
||||||
}
|
}
|
||||||
function CB4(t) {
|
function CB4(t: number) {
|
||||||
return (1 - t) * (1 - t) * (1 - t);
|
return (1 - t) * (1 - t) * (1 - t);
|
||||||
}
|
}
|
||||||
const x = P4x * CB1(pct) + P3x * CB2(pct) + P2x * CB3(pct) + P1x * CB4(pct);
|
const x = P4x * CB1(pct) + P3x * CB2(pct) + P2x * CB3(pct) + P1x * CB4(pct);
|
||||||
const y = P4y * CB1(pct) + P3y * CB2(pct) + P2y * CB3(pct) + P1y * CB4(pct);
|
const y = P4y * CB1(pct) + P3y * CB2(pct) + P2y * CB3(pct) + P1y * CB4(pct);
|
||||||
|
|
||||||
return {
|
return { x, y };
|
||||||
x: x,
|
|
||||||
y: y,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
static getPointOnQuadraticBezier(pct, P1x, P1y, P2x, P2y, P3x, P3y) {
|
static getPointOnQuadraticBezier(pct, P1x, P1y, P2x, P2y, P3x, P3y) {
|
||||||
function QB1(t) {
|
function QB1(t) {
|
||||||
@ -396,10 +411,7 @@ export class Path extends Shape<PathConfig> {
|
|||||||
const x = P3x * QB1(pct) + P2x * QB2(pct) + P1x * QB3(pct);
|
const x = P3x * QB1(pct) + P2x * QB2(pct) + P1x * QB3(pct);
|
||||||
const y = P3y * QB1(pct) + P2y * QB2(pct) + P1y * QB3(pct);
|
const y = P3y * QB1(pct) + P2y * QB2(pct) + P1y * QB3(pct);
|
||||||
|
|
||||||
return {
|
return { x, y };
|
||||||
x: x,
|
|
||||||
y: y,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
static getPointOnEllipticalArc(
|
static getPointOnEllipticalArc(
|
||||||
cx: number,
|
cx: number,
|
||||||
@ -483,7 +495,7 @@ export class Path extends Shape<PathConfig> {
|
|||||||
// convert white spaces to commas
|
// convert white spaces to commas
|
||||||
cs = cs.replace(new RegExp(' ', 'g'), ',');
|
cs = cs.replace(new RegExp(' ', 'g'), ',');
|
||||||
// create pipes so that we can split the data
|
// create pipes so that we can split the data
|
||||||
for (var n = 0; n < cc.length; n++) {
|
for (let n = 0; n < cc.length; n++) {
|
||||||
cs = cs.replace(new RegExp(cc[n], 'g'), '|' + cc[n]);
|
cs = cs.replace(new RegExp(cc[n], 'g'), '|' + cc[n]);
|
||||||
}
|
}
|
||||||
// create array
|
// create array
|
||||||
@ -496,7 +508,7 @@ export class Path extends Shape<PathConfig> {
|
|||||||
|
|
||||||
const re = /([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:e[-+]?\d+)?)/gi;
|
const re = /([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:e[-+]?\d+)?)/gi;
|
||||||
let match;
|
let match;
|
||||||
for (n = 1; n < arr.length; n++) {
|
for (let n = 1; n < arr.length; n++) {
|
||||||
let str = arr[n];
|
let str = arr[n];
|
||||||
let c = str.charAt(0);
|
let c = str.charAt(0);
|
||||||
str = str.slice(1);
|
str = str.slice(1);
|
||||||
@ -536,8 +548,8 @@ export class Path extends Shape<PathConfig> {
|
|||||||
const startX = cpx,
|
const startX = cpx,
|
||||||
startY = cpy;
|
startY = cpy;
|
||||||
// Move var from within the switch to up here (jshint)
|
// Move var from within the switch to up here (jshint)
|
||||||
var prevCmd, ctlPtx, ctlPty; // Ss, Tt
|
let prevCmd, ctlPtx, ctlPty; // Ss, Tt
|
||||||
var rx, ry, psi, fa, fs, x1, y1; // Aa
|
let rx, ry, psi, fa, fs, x1, y1; // Aa
|
||||||
|
|
||||||
// convert l, H, h, V, and v to L
|
// convert l, H, h, V, and v to L
|
||||||
switch (c) {
|
switch (c) {
|
||||||
@ -555,8 +567,8 @@ export class Path extends Shape<PathConfig> {
|
|||||||
break;
|
break;
|
||||||
// Note: lineTo handlers need to be above this point
|
// Note: lineTo handlers need to be above this point
|
||||||
case 'm':
|
case 'm':
|
||||||
var dx = p.shift()!;
|
const dx = p.shift()!;
|
||||||
var dy = p.shift()!;
|
const dy = p.shift()!;
|
||||||
cpx += dx;
|
cpx += dx;
|
||||||
cpy += dy;
|
cpy += dy;
|
||||||
cmd = 'M';
|
cmd = 'M';
|
||||||
@ -782,12 +794,12 @@ export class Path extends Shape<PathConfig> {
|
|||||||
case 'A':
|
case 'A':
|
||||||
// Approximates by breaking curve into line segments
|
// Approximates by breaking curve into line segments
|
||||||
len = 0.0;
|
len = 0.0;
|
||||||
var start = points[4];
|
const start = points[4];
|
||||||
// 4 = theta
|
// 4 = theta
|
||||||
var dTheta = points[5];
|
const dTheta = points[5];
|
||||||
// 5 = dTheta
|
// 5 = dTheta
|
||||||
var end = points[4] + dTheta;
|
const end = points[4] + dTheta;
|
||||||
var inc = Math.PI / 180.0;
|
let inc = Math.PI / 180.0;
|
||||||
// 1 degree resolution
|
// 1 degree resolution
|
||||||
if (Math.abs(start - end) < inc) {
|
if (Math.abs(start - end) < inc) {
|
||||||
inc = Math.abs(start - end);
|
inc = Math.abs(start - end);
|
||||||
@ -846,15 +858,15 @@ export class Path extends Shape<PathConfig> {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
static convertEndpointToCenterParameterization(
|
static convertEndpointToCenterParameterization(
|
||||||
x1,
|
x1: number,
|
||||||
y1,
|
y1: number,
|
||||||
x2,
|
x2: number,
|
||||||
y2,
|
y2: number,
|
||||||
fa,
|
fa: number,
|
||||||
fs,
|
fs: number,
|
||||||
rx,
|
rx: number,
|
||||||
ry,
|
ry: number,
|
||||||
psiDeg
|
psiDeg: number
|
||||||
) {
|
) {
|
||||||
// Derived from: http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
|
// Derived from: http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
|
||||||
const psi = psiDeg * (Math.PI / 180.0);
|
const psi = psiDeg * (Math.PI / 180.0);
|
||||||
|
@ -16,13 +16,19 @@ import { GetSet } from '../types';
|
|||||||
export function stringToArray(string: string): string[] {
|
export function stringToArray(string: string): string[] {
|
||||||
// Use Unicode-aware splitting
|
// Use Unicode-aware splitting
|
||||||
return [...string].reduce((acc, char, index, array) => {
|
return [...string].reduce((acc, char, index, array) => {
|
||||||
// Handle emoji sequences (including ZWJ sequences)
|
// Handle emoji with skin tone modifiers and ZWJ sequences
|
||||||
if (
|
if (/\p{Emoji}/u.test(char)) {
|
||||||
/\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?(?:\u200D\p{Emoji_Presentation})+/u.test(
|
// Check if next character is a modifier or ZWJ sequence
|
||||||
char
|
const nextChar = array[index + 1];
|
||||||
)
|
if (nextChar && /\p{Emoji_Modifier}|\u200D/u.test(nextChar)) {
|
||||||
) {
|
// If we have a modifier, combine with current emoji
|
||||||
acc.push(char);
|
acc.push(char + nextChar);
|
||||||
|
// Skip the next character since we've used it
|
||||||
|
array[index + 1] = '';
|
||||||
|
} else {
|
||||||
|
// No modifier - treat as separate emoji
|
||||||
|
acc.push(char);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Handle regional indicator symbols (flags)
|
// Handle regional indicator symbols (flags)
|
||||||
else if (
|
else if (
|
||||||
@ -35,7 +41,8 @@ export function stringToArray(string: string): string[] {
|
|||||||
acc[acc.length - 1] += char;
|
acc[acc.length - 1] += char;
|
||||||
}
|
}
|
||||||
// Handle other characters
|
// Handle other characters
|
||||||
else {
|
else if (char) {
|
||||||
|
// Only push if not an empty string (skipped modifier)
|
||||||
acc.push(char);
|
acc.push(char);
|
||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
@ -164,7 +171,7 @@ function checkDefaultFill(config?: TextConfig) {
|
|||||||
* @param {String} [config.fontVariant] can be normal or small-caps. Default is normal
|
* @param {String} [config.fontVariant] can be normal or small-caps. Default is normal
|
||||||
* @param {String} [config.textDecoration] can be line-through, underline or empty string. Default is empty string.
|
* @param {String} [config.textDecoration] can be line-through, underline or empty string. Default is empty string.
|
||||||
* @param {String} config.text
|
* @param {String} config.text
|
||||||
* @param {String} [config.align] can be left, center, or right
|
* @param {String} [config.align] can be left, center, right or justify
|
||||||
* @param {String} [config.verticalAlign] can be top, middle or bottom
|
* @param {String} [config.verticalAlign] can be top, middle or bottom
|
||||||
* @param {Number} [config.padding]
|
* @param {Number} [config.padding]
|
||||||
* @param {Number} [config.lineHeight] default is 1
|
* @param {Number} [config.lineHeight] default is 1
|
||||||
@ -235,9 +242,6 @@ export class Text extends Shape<TextConfig> {
|
|||||||
lineHeightPx / 2;
|
lineHeightPx / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
var lineTranslateX = 0;
|
|
||||||
var lineTranslateY = 0;
|
|
||||||
|
|
||||||
if (direction === RTL) {
|
if (direction === RTL) {
|
||||||
context.setAttr('direction', direction);
|
context.setAttr('direction', direction);
|
||||||
}
|
}
|
||||||
@ -259,15 +263,12 @@ export class Text extends Shape<TextConfig> {
|
|||||||
|
|
||||||
// draw text lines
|
// draw text lines
|
||||||
for (n = 0; n < textArrLen; n++) {
|
for (n = 0; n < textArrLen; n++) {
|
||||||
var lineTranslateX = 0;
|
let lineTranslateX = 0;
|
||||||
var lineTranslateY = 0;
|
let lineTranslateY = 0;
|
||||||
var obj = textArr[n],
|
const obj = textArr[n],
|
||||||
text = obj.text,
|
text = obj.text,
|
||||||
width = obj.width,
|
width = obj.width,
|
||||||
lastLine = obj.lastInParagraph,
|
lastLine = obj.lastInParagraph;
|
||||||
spacesNumber,
|
|
||||||
oneWord,
|
|
||||||
lineWidth;
|
|
||||||
|
|
||||||
// horizontal alignment
|
// horizontal alignment
|
||||||
context.save();
|
context.save();
|
||||||
@ -287,9 +288,7 @@ export class Text extends Shape<TextConfig> {
|
|||||||
const x = lineTranslateX;
|
const x = lineTranslateX;
|
||||||
const y = translateY + lineTranslateY + yOffset;
|
const y = translateY + lineTranslateY + yOffset;
|
||||||
context.moveTo(x, y);
|
context.moveTo(x, y);
|
||||||
spacesNumber = text.split(' ').length - 1;
|
const lineWidth =
|
||||||
oneWord = spacesNumber === 0;
|
|
||||||
lineWidth =
|
|
||||||
align === JUSTIFY && !lastLine ? totalWidth - padding * 2 : width;
|
align === JUSTIFY && !lastLine ? totalWidth - padding * 2 : width;
|
||||||
context.lineTo(x + Math.round(lineWidth), y);
|
context.lineTo(x + Math.round(lineWidth), y);
|
||||||
|
|
||||||
@ -307,12 +306,8 @@ export class Text extends Shape<TextConfig> {
|
|||||||
context.beginPath();
|
context.beginPath();
|
||||||
const yOffset = Konva._fixTextRendering ? -Math.round(fontSize / 4) : 0;
|
const yOffset = Konva._fixTextRendering ? -Math.round(fontSize / 4) : 0;
|
||||||
context.moveTo(lineTranslateX, translateY + lineTranslateY + yOffset);
|
context.moveTo(lineTranslateX, translateY + lineTranslateY + yOffset);
|
||||||
spacesNumber = text.split(' ').length - 1;
|
const lineWidth =
|
||||||
oneWord = spacesNumber === 0;
|
align === JUSTIFY && !lastLine ? totalWidth - padding * 2 : width;
|
||||||
lineWidth =
|
|
||||||
align === JUSTIFY && lastLine && !oneWord
|
|
||||||
? totalWidth - padding * 2
|
|
||||||
: width;
|
|
||||||
context.lineTo(
|
context.lineTo(
|
||||||
lineTranslateX + Math.round(lineWidth),
|
lineTranslateX + Math.round(lineWidth),
|
||||||
translateY + lineTranslateY + yOffset
|
translateY + lineTranslateY + yOffset
|
||||||
@ -328,7 +323,7 @@ export class Text extends Shape<TextConfig> {
|
|||||||
// be supported otherwise.
|
// be supported otherwise.
|
||||||
if (direction !== RTL && (letterSpacing !== 0 || align === JUSTIFY)) {
|
if (direction !== RTL && (letterSpacing !== 0 || align === JUSTIFY)) {
|
||||||
// var words = text.split(' ');
|
// var words = text.split(' ');
|
||||||
spacesNumber = text.split(' ').length - 1;
|
const spacesNumber = text.split(' ').length - 1;
|
||||||
const array = stringToArray(text);
|
const array = stringToArray(text);
|
||||||
for (let li = 0; li < array.length; li++) {
|
for (let li = 0; li < array.length; li++) {
|
||||||
const letter = array[li];
|
const letter = array[li];
|
||||||
@ -385,7 +380,8 @@ export class Text extends Shape<TextConfig> {
|
|||||||
return isAuto ? this.getTextWidth() + this.padding() * 2 : this.attrs.width;
|
return isAuto ? this.getTextWidth() + this.padding() * 2 : this.attrs.width;
|
||||||
}
|
}
|
||||||
getHeight() {
|
getHeight() {
|
||||||
const isAuto = this.attrs.height === AUTO || this.attrs.height === undefined;
|
const isAuto =
|
||||||
|
this.attrs.height === AUTO || this.attrs.height === undefined;
|
||||||
return isAuto
|
return isAuto
|
||||||
? this.fontSize() * this.textArr.length * this.lineHeight() +
|
? this.fontSize() * this.textArr.length * this.lineHeight() +
|
||||||
this.padding() * 2
|
this.padding() * 2
|
||||||
@ -477,10 +473,9 @@ export class Text extends Shape<TextConfig> {
|
|||||||
_getTextWidth(text: string) {
|
_getTextWidth(text: string) {
|
||||||
const letterSpacing = this.letterSpacing();
|
const letterSpacing = this.letterSpacing();
|
||||||
const length = text.length;
|
const length = text.length;
|
||||||
return (
|
// letterSpacing * length is the total letter spacing for the text
|
||||||
getDummyContext().measureText(text).width +
|
// previously we used letterSpacing * (length - 1) but it doesn't match DOM behavior
|
||||||
(length ? letterSpacing * (length - 1) : 0)
|
return getDummyContext().measureText(text).width + letterSpacing * length;
|
||||||
);
|
|
||||||
}
|
}
|
||||||
_setTextData() {
|
_setTextData() {
|
||||||
let lines = this.text().split('\n'),
|
let lines = this.text().split('\n'),
|
||||||
@ -503,7 +498,9 @@ export class Text extends Shape<TextConfig> {
|
|||||||
|
|
||||||
this.textArr = [];
|
this.textArr = [];
|
||||||
getDummyContext().font = this._getContextFont();
|
getDummyContext().font = this._getContextFont();
|
||||||
const additionalWidth = shouldAddEllipsis ? this._getTextWidth(ELLIPSIS) : 0;
|
const additionalWidth = shouldAddEllipsis
|
||||||
|
? this._getTextWidth(ELLIPSIS)
|
||||||
|
: 0;
|
||||||
for (let i = 0, max = lines.length; i < max; ++i) {
|
for (let i = 0, max = lines.length; i < max; ++i) {
|
||||||
let line = lines[i];
|
let line = lines[i];
|
||||||
|
|
||||||
@ -519,17 +516,31 @@ export class Text extends Shape<TextConfig> {
|
|||||||
* that would fit in the specified width
|
* that would fit in the specified width
|
||||||
*/
|
*/
|
||||||
let low = 0,
|
let low = 0,
|
||||||
high = line.length,
|
high = stringToArray(line).length, // Convert to array for proper emoji handling
|
||||||
match = '',
|
match = '',
|
||||||
matchWidth = 0;
|
matchWidth = 0;
|
||||||
while (low < high) {
|
while (low < high) {
|
||||||
const mid = (low + high) >>> 1,
|
const mid = (low + high) >>> 1,
|
||||||
substr = line.slice(0, mid + 1),
|
// Convert array indices to string
|
||||||
substrWidth = this._getTextWidth(substr) + additionalWidth;
|
lineArray = stringToArray(line),
|
||||||
if (substrWidth <= maxWidth) {
|
substr = lineArray.slice(0, mid + 1).join(''),
|
||||||
|
substrWidth = this._getTextWidth(substr);
|
||||||
|
|
||||||
|
// Only add ellipsis width when we need to consider truncation
|
||||||
|
// for the current line (when it might be the last visible line)
|
||||||
|
const shouldConsiderEllipsis =
|
||||||
|
shouldAddEllipsis &&
|
||||||
|
fixedHeight &&
|
||||||
|
currentHeightPx + lineHeightPx > maxHeightPx;
|
||||||
|
|
||||||
|
const effectiveWidth = shouldConsiderEllipsis
|
||||||
|
? substrWidth + additionalWidth
|
||||||
|
: substrWidth;
|
||||||
|
|
||||||
|
if (effectiveWidth <= maxWidth) {
|
||||||
low = mid + 1;
|
low = mid + 1;
|
||||||
match = substr;
|
match = substr;
|
||||||
matchWidth = substrWidth;
|
matchWidth = substrWidth; // Store actual text width without ellipsis
|
||||||
} else {
|
} else {
|
||||||
high = mid;
|
high = mid;
|
||||||
}
|
}
|
||||||
@ -543,20 +554,24 @@ export class Text extends Shape<TextConfig> {
|
|||||||
// a fitting substring was found
|
// a fitting substring was found
|
||||||
if (wrapAtWord) {
|
if (wrapAtWord) {
|
||||||
// try to find a space or dash where wrapping could be done
|
// try to find a space or dash where wrapping could be done
|
||||||
var wrapIndex;
|
const lineArray = stringToArray(line);
|
||||||
const nextChar = line[match.length];
|
const matchArray = stringToArray(match);
|
||||||
|
const nextChar = lineArray[matchArray.length];
|
||||||
const nextIsSpaceOrDash = nextChar === SPACE || nextChar === DASH;
|
const nextIsSpaceOrDash = nextChar === SPACE || nextChar === DASH;
|
||||||
|
|
||||||
|
let wrapIndex;
|
||||||
if (nextIsSpaceOrDash && matchWidth <= maxWidth) {
|
if (nextIsSpaceOrDash && matchWidth <= maxWidth) {
|
||||||
wrapIndex = match.length;
|
wrapIndex = matchArray.length;
|
||||||
} else {
|
} else {
|
||||||
wrapIndex =
|
// Find last space or dash in the array
|
||||||
Math.max(match.lastIndexOf(SPACE), match.lastIndexOf(DASH)) +
|
const lastSpaceIndex = matchArray.lastIndexOf(SPACE);
|
||||||
1;
|
const lastDashIndex = matchArray.lastIndexOf(DASH);
|
||||||
|
wrapIndex = Math.max(lastSpaceIndex, lastDashIndex) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wrapIndex > 0) {
|
if (wrapIndex > 0) {
|
||||||
// re-cut the substring found at the space/dash position
|
|
||||||
low = wrapIndex;
|
low = wrapIndex;
|
||||||
match = match.slice(0, low);
|
match = lineArray.slice(0, low).join('');
|
||||||
matchWidth = this._getTextWidth(match);
|
matchWidth = this._getTextWidth(match);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -577,13 +592,14 @@ export class Text extends Shape<TextConfig> {
|
|||||||
*/
|
*/
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
line = line.slice(low);
|
|
||||||
line = line.trimLeft();
|
// Convert remaining text using array operations
|
||||||
|
const lineArray = stringToArray(line);
|
||||||
|
line = lineArray.slice(low).join('').trimLeft();
|
||||||
|
|
||||||
if (line.length > 0) {
|
if (line.length > 0) {
|
||||||
// Check if the remaining text would fit on one line
|
|
||||||
lineWidth = this._getTextWidth(line);
|
lineWidth = this._getTextWidth(line);
|
||||||
if (lineWidth <= maxWidth) {
|
if (lineWidth <= maxWidth) {
|
||||||
// if it does, add the line and break out of the loop
|
|
||||||
this._addTextLine(line);
|
this._addTextLine(line);
|
||||||
currentHeightPx += lineHeightPx;
|
currentHeightPx += lineHeightPx;
|
||||||
textWidth = Math.max(textWidth, lineWidth);
|
textWidth = Math.max(textWidth, lineWidth);
|
||||||
@ -625,7 +641,7 @@ export class Text extends Shape<TextConfig> {
|
|||||||
* 1. the current line is the last line
|
* 1. the current line is the last line
|
||||||
* 2. wrap is NONE
|
* 2. wrap is NONE
|
||||||
* @param {Number} currentHeightPx
|
* @param {Number} currentHeightPx
|
||||||
* @returns
|
* @returns {Boolean}
|
||||||
*/
|
*/
|
||||||
_shouldHandleEllipsis(currentHeightPx: number): boolean {
|
_shouldHandleEllipsis(currentHeightPx: number): boolean {
|
||||||
const fontSize = +this.fontSize(),
|
const fontSize = +this.fontSize(),
|
||||||
@ -859,6 +875,8 @@ Factory.addGetterSetter(Text, 'padding', 0, getNumberValidator());
|
|||||||
*
|
*
|
||||||
* // align text to right
|
* // align text to right
|
||||||
* text.align('right');
|
* text.align('right');
|
||||||
|
*
|
||||||
|
* // justify text
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Factory.addGetterSetter(Text, 'align', LEFT);
|
Factory.addGetterSetter(Text, 'align', LEFT);
|
||||||
|
@ -551,7 +551,7 @@ Factory.addGetterSetter(TextPath, 'text', EMPTY_STRING);
|
|||||||
* // underline text
|
* // underline text
|
||||||
* shape.textDecoration('underline');
|
* shape.textDecoration('underline');
|
||||||
*/
|
*/
|
||||||
Factory.addGetterSetter(TextPath, 'textDecoration', null);
|
Factory.addGetterSetter(TextPath, 'textDecoration', '');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get/set kerning function.
|
* get/set kerning function.
|
||||||
@ -568,4 +568,4 @@ Factory.addGetterSetter(TextPath, 'textDecoration', null);
|
|||||||
* return 1;
|
* return 1;
|
||||||
* });
|
* });
|
||||||
*/
|
*/
|
||||||
Factory.addGetterSetter(TextPath, 'kerningFunc', null);
|
Factory.addGetterSetter(TextPath, 'kerningFunc', undefined);
|
||||||
|
@ -335,10 +335,12 @@ export class Transformer extends Group {
|
|||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const additionalEvents = node._attrsAffectingSize
|
if (node._attrsAffectingSize.length) {
|
||||||
.map((prop) => prop + 'Change.' + this._getEventNamespace())
|
const additionalEvents = node._attrsAffectingSize
|
||||||
.join(' ');
|
.map((prop) => prop + 'Change.' + this._getEventNamespace())
|
||||||
node.on(additionalEvents, onChange);
|
.join(' ');
|
||||||
|
node.on(additionalEvents, onChange);
|
||||||
|
}
|
||||||
node.on(
|
node.on(
|
||||||
TRANSFORM_CHANGE_STR.map(
|
TRANSFORM_CHANGE_STR.map(
|
||||||
(e) => e + `.${this._getEventNamespace()}`
|
(e) => e + `.${this._getEventNamespace()}`
|
||||||
@ -768,11 +770,11 @@ export class Transformer extends Group {
|
|||||||
keepProportion = this.keepRatio() || e.shiftKey;
|
keepProportion = this.keepRatio() || e.shiftKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
var centeredScaling = this.centeredScaling() || e.altKey;
|
let centeredScaling = this.centeredScaling() || e.altKey;
|
||||||
|
|
||||||
if (this._movingAnchorName === 'top-left') {
|
if (this._movingAnchorName === 'top-left') {
|
||||||
if (keepProportion) {
|
if (keepProportion) {
|
||||||
var comparePoint = centeredScaling
|
const comparePoint = centeredScaling
|
||||||
? {
|
? {
|
||||||
x: this.width() / 2,
|
x: this.width() / 2,
|
||||||
y: this.height() / 2,
|
y: this.height() / 2,
|
||||||
@ -786,9 +788,9 @@ export class Transformer extends Group {
|
|||||||
Math.pow(comparePoint.y - anchorNode.y(), 2)
|
Math.pow(comparePoint.y - anchorNode.y(), 2)
|
||||||
);
|
);
|
||||||
|
|
||||||
var reverseX = this.findOne('.top-left')!.x() > comparePoint.x ? -1 : 1;
|
const reverseX = this.findOne('.top-left')!.x() > comparePoint.x ? -1 : 1;
|
||||||
|
|
||||||
var reverseY = this.findOne('.top-left')!.y() > comparePoint.y ? -1 : 1;
|
const reverseY = this.findOne('.top-left')!.y() > comparePoint.y ? -1 : 1;
|
||||||
|
|
||||||
x = newHypotenuse * this.cos * reverseX;
|
x = newHypotenuse * this.cos * reverseX;
|
||||||
y = newHypotenuse * this.sin * reverseY;
|
y = newHypotenuse * this.sin * reverseY;
|
||||||
@ -800,7 +802,7 @@ export class Transformer extends Group {
|
|||||||
this.findOne('.top-left')!.y(anchorNode.y());
|
this.findOne('.top-left')!.y(anchorNode.y());
|
||||||
} else if (this._movingAnchorName === 'top-right') {
|
} else if (this._movingAnchorName === 'top-right') {
|
||||||
if (keepProportion) {
|
if (keepProportion) {
|
||||||
var comparePoint = centeredScaling
|
const comparePoint = centeredScaling
|
||||||
? {
|
? {
|
||||||
x: this.width() / 2,
|
x: this.width() / 2,
|
||||||
y: this.height() / 2,
|
y: this.height() / 2,
|
||||||
@ -815,10 +817,10 @@ export class Transformer extends Group {
|
|||||||
Math.pow(comparePoint.y - anchorNode.y(), 2)
|
Math.pow(comparePoint.y - anchorNode.y(), 2)
|
||||||
);
|
);
|
||||||
|
|
||||||
var reverseX =
|
const reverseX =
|
||||||
this.findOne('.top-right')!.x() < comparePoint.x ? -1 : 1;
|
this.findOne('.top-right')!.x() < comparePoint.x ? -1 : 1;
|
||||||
|
|
||||||
var reverseY =
|
const reverseY =
|
||||||
this.findOne('.top-right')!.y() > comparePoint.y ? -1 : 1;
|
this.findOne('.top-right')!.y() > comparePoint.y ? -1 : 1;
|
||||||
|
|
||||||
x = newHypotenuse * this.cos * reverseX;
|
x = newHypotenuse * this.cos * reverseX;
|
||||||
@ -836,7 +838,7 @@ export class Transformer extends Group {
|
|||||||
this.findOne('.bottom-right')!.x(anchorNode.x());
|
this.findOne('.bottom-right')!.x(anchorNode.x());
|
||||||
} else if (this._movingAnchorName === 'bottom-left') {
|
} else if (this._movingAnchorName === 'bottom-left') {
|
||||||
if (keepProportion) {
|
if (keepProportion) {
|
||||||
var comparePoint = centeredScaling
|
const comparePoint = centeredScaling
|
||||||
? {
|
? {
|
||||||
x: this.width() / 2,
|
x: this.width() / 2,
|
||||||
y: this.height() / 2,
|
y: this.height() / 2,
|
||||||
@ -851,9 +853,9 @@ export class Transformer extends Group {
|
|||||||
Math.pow(anchorNode.y() - comparePoint.y, 2)
|
Math.pow(anchorNode.y() - comparePoint.y, 2)
|
||||||
);
|
);
|
||||||
|
|
||||||
var reverseX = comparePoint.x < anchorNode.x() ? -1 : 1;
|
const reverseX = comparePoint.x < anchorNode.x() ? -1 : 1;
|
||||||
|
|
||||||
var reverseY = anchorNode.y() < comparePoint.y ? -1 : 1;
|
const reverseY = anchorNode.y() < comparePoint.y ? -1 : 1;
|
||||||
|
|
||||||
x = newHypotenuse * this.cos * reverseX;
|
x = newHypotenuse * this.cos * reverseX;
|
||||||
y = newHypotenuse * this.sin * reverseY;
|
y = newHypotenuse * this.sin * reverseY;
|
||||||
@ -870,7 +872,7 @@ export class Transformer extends Group {
|
|||||||
this.findOne('.bottom-right')!.y(anchorNode.y());
|
this.findOne('.bottom-right')!.y(anchorNode.y());
|
||||||
} else if (this._movingAnchorName === 'bottom-right') {
|
} else if (this._movingAnchorName === 'bottom-right') {
|
||||||
if (keepProportion) {
|
if (keepProportion) {
|
||||||
var comparePoint = centeredScaling
|
const comparePoint = centeredScaling
|
||||||
? {
|
? {
|
||||||
x: this.width() / 2,
|
x: this.width() / 2,
|
||||||
y: this.height() / 2,
|
y: this.height() / 2,
|
||||||
@ -885,10 +887,10 @@ export class Transformer extends Group {
|
|||||||
Math.pow(anchorNode.y() - comparePoint.y, 2)
|
Math.pow(anchorNode.y() - comparePoint.y, 2)
|
||||||
);
|
);
|
||||||
|
|
||||||
var reverseX =
|
const reverseX =
|
||||||
this.findOne('.bottom-right')!.x() < comparePoint.x ? -1 : 1;
|
this.findOne('.bottom-right')!.x() < comparePoint.x ? -1 : 1;
|
||||||
|
|
||||||
var reverseY =
|
const reverseY =
|
||||||
this.findOne('.bottom-right')!.y() < comparePoint.y ? -1 : 1;
|
this.findOne('.bottom-right')!.y() < comparePoint.y ? -1 : 1;
|
||||||
|
|
||||||
x = newHypotenuse * this.cos * reverseX;
|
x = newHypotenuse * this.cos * reverseX;
|
||||||
@ -906,7 +908,7 @@ export class Transformer extends Group {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
var centeredScaling = this.centeredScaling() || e.altKey;
|
centeredScaling = this.centeredScaling() || e.altKey;
|
||||||
if (centeredScaling) {
|
if (centeredScaling) {
|
||||||
const topLeft = this.findOne('.top-left')!;
|
const topLeft = this.findOne('.top-left')!;
|
||||||
const bottomRight = this.findOne('.bottom-right')!;
|
const bottomRight = this.findOne('.bottom-right')!;
|
||||||
@ -1745,8 +1747,6 @@ Factory.addGetterSetter(Transformer, 'ignoreStroke', false);
|
|||||||
*/
|
*/
|
||||||
Factory.addGetterSetter(Transformer, 'padding', 0, getNumberValidator());
|
Factory.addGetterSetter(Transformer, 'padding', 0, getNumberValidator());
|
||||||
|
|
||||||
Factory.addGetterSetter(Transformer, 'node');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get/set attached nodes of the Transformer. Transformer will adapt to their size and listen to their events
|
* get/set attached nodes of the Transformer. Transformer will adapt to their size and listen to their events
|
||||||
* @method
|
* @method
|
||||||
@ -1767,6 +1767,9 @@ Factory.addGetterSetter(Transformer, 'node');
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Factory.addGetterSetter(Transformer, 'nodes');
|
Factory.addGetterSetter(Transformer, 'nodes');
|
||||||
|
// @ts-ignore
|
||||||
|
// deprecated
|
||||||
|
Factory.addGetterSetter(Transformer, 'node');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get/set bounding box function. **IMPORTANT!** boundBondFunc operates in absolute coordinates.
|
* get/set bounding box function. **IMPORTANT!** boundBondFunc operates in absolute coordinates.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
export interface GetSet<Type, This> {
|
export interface GetSet<Type, This> {
|
||||||
(): Type;
|
(): Type;
|
||||||
(v: Type): This;
|
(v: Type | null | undefined): This;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Vector2d {
|
export interface Vector2d {
|
||||||
|
@ -13,8 +13,13 @@
|
|||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
}
|
}
|
||||||
|
body {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<!-- <script src="https://cdn.rawgit.com/hammerjs/touchemulator/master/touch-emulator.js"></script> -->
|
<!-- <script src="https://cdn.rawgit.com/hammerjs/touchemulator/master/touch-emulator.js"></script> -->
|
||||||
|
<script src="https://unpkg.com/gifler@0.1.0/gifler.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
// TouchEmulator();
|
// TouchEmulator();
|
||||||
</script>
|
</script>
|
||||||
@ -26,46 +31,54 @@
|
|||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="container"></div>
|
<div id="container"></div>
|
||||||
|
<textarea class="test" id="text">Hello</textarea>
|
||||||
|
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import Konva from '../src/index.ts';
|
import Konva from '../src/index.ts';
|
||||||
|
|
||||||
var stageWidth = window.innerWidth;
|
var width = window.innerWidth;
|
||||||
var stageHeight = window.innerHeight;
|
var height = window.innerHeight;
|
||||||
|
|
||||||
Konva._fixTextRendering = true;
|
|
||||||
|
|
||||||
var stage = new Konva.Stage({
|
var stage = new Konva.Stage({
|
||||||
container: 'container',
|
container: 'container',
|
||||||
width: stageWidth,
|
width: width,
|
||||||
height: stageHeight,
|
height: height,
|
||||||
});
|
});
|
||||||
|
|
||||||
var layer = new Konva.Layer();
|
var layer = new Konva.Layer();
|
||||||
stage.add(layer);
|
stage.add(layer);
|
||||||
|
|
||||||
var rect = new Konva.Rect({
|
var canvas = document.createElement('canvas');
|
||||||
x: 10,
|
// use external library to parse and draw gif animation
|
||||||
y: 10,
|
function onDrawFrame(ctx, frame) {
|
||||||
width: 100,
|
// update canvas size
|
||||||
height: 100,
|
canvas.width = frame.width;
|
||||||
fill: 'green',
|
canvas.height = frame.height;
|
||||||
draggable: true,
|
// update canvas that we are using for Konva.Image
|
||||||
});
|
ctx.drawImage(frame.buffer, 0, 0);
|
||||||
layer.add(rect);
|
// redraw the layer
|
||||||
|
layer.draw();
|
||||||
|
}
|
||||||
|
|
||||||
window.addEventListener('touchend', () => {
|
gifler('https://konvajs.org/assets/yoda.gif').frames(canvas, onDrawFrame);
|
||||||
console.log('touchend');
|
|
||||||
});
|
function testKonvaImage() {
|
||||||
window.addEventListener('touchcancel', () => {
|
setInterval(() => {
|
||||||
console.log('touchcancel');
|
const image = new Konva.Image({
|
||||||
});
|
image: canvas,
|
||||||
window.addEventListener('lostpointercapture', () => {
|
x: Math.random() * width,
|
||||||
console.log('lostpointercapture');
|
y: Math.random() * height,
|
||||||
});
|
});
|
||||||
window.addEventListener('focusout', () => {
|
layer.add(image);
|
||||||
console.log('focusout');
|
|
||||||
});
|
setTimeout(() => {
|
||||||
|
image.image(canvas);
|
||||||
|
image.destroy();
|
||||||
|
}, 500);
|
||||||
|
}, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
testKonvaImage();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,10 +1,25 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES2015",
|
"target": "ES2015",
|
||||||
"noEmitOnError": true,
|
"noEmitOnError": false,
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"lib": ["ES2015", "dom"],
|
"lib": ["ES2015", "dom"],
|
||||||
"module": "CommonJS"
|
"module": "CommonJS",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"noImplicitAny": false,
|
||||||
|
"allowJs": true,
|
||||||
|
"noEmit": false,
|
||||||
|
"checkJs": false,
|
||||||
|
"allowUnreachableCode": true,
|
||||||
|
"allowUnusedLabels": true,
|
||||||
|
"noFallthroughCasesInSwitch": false,
|
||||||
|
"noImplicitReturns": false,
|
||||||
|
"noImplicitThis": false,
|
||||||
|
"noPropertyAccessFromIndexSignature": false,
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"noUnusedParameters": false,
|
||||||
|
"strict": false
|
||||||
},
|
},
|
||||||
"include": ["../src/**/*.ts"]
|
"include": ["../src/**/*.ts", "./**/*.ts"],
|
||||||
|
"exclude": ["../node_modules/**/*"]
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
<script type="module">
|
<script type="module">
|
||||||
// CORE
|
// CORE
|
||||||
import './unit/Animation-test.ts';
|
import './unit/Animation-test.ts';
|
||||||
|
import './unit/Tween-test.ts';
|
||||||
import './unit/Canvas-test.ts';
|
import './unit/Canvas-test.ts';
|
||||||
import './unit/Container-test.ts';
|
import './unit/Container-test.ts';
|
||||||
import './unit/Context-test.ts';
|
import './unit/Context-test.ts';
|
||||||
|
@ -229,4 +229,33 @@ describe('Arrow', function () {
|
|||||||
|
|
||||||
cloneAndCompareLayer(layer, 255, 50);
|
cloneAndCompareLayer(layer, 255, 50);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('getClientRect', function () {
|
||||||
|
var stage = addStage();
|
||||||
|
var layer = new Konva.Layer();
|
||||||
|
|
||||||
|
var arrow = new Konva.Arrow({
|
||||||
|
points: [50, 50, 150, 50],
|
||||||
|
stroke: 'blue',
|
||||||
|
fill: 'blue',
|
||||||
|
// large stroke width will not work :(
|
||||||
|
strokeWidth: 1,
|
||||||
|
draggable: true,
|
||||||
|
tension: 0,
|
||||||
|
pointerLength: 10,
|
||||||
|
pointerWidth: 20,
|
||||||
|
});
|
||||||
|
layer.add(arrow);
|
||||||
|
|
||||||
|
|
||||||
|
stage.add(layer);
|
||||||
|
|
||||||
|
var rect = arrow.getClientRect({ skipStroke: true });
|
||||||
|
layer.add(new Konva.Rect({...rect, stroke: 'red' }));
|
||||||
|
|
||||||
|
assert.equal(rect.x, 50);
|
||||||
|
assert.equal(rect.y, 40);
|
||||||
|
assert.equal(rect.width, 100);
|
||||||
|
assert.equal(rect.height, 20);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -107,17 +107,10 @@ describe('Image', function () {
|
|||||||
|
|
||||||
var trace = layer.getContext().getTrace();
|
var trace = layer.getContext().getTrace();
|
||||||
|
|
||||||
if (isBrowser) {
|
assert.equal(
|
||||||
assert.equal(
|
trace,
|
||||||
trace,
|
'clearRect(0,0,578,200);save();transform(1,0,0,1,150,30);drawImage([object HTMLImageElement],135,7,167,134,0,0,100,100);restore();clearRect(0,0,578,200);save();transform(1,0,0,1,150,30);drawImage([object HTMLImageElement],135,7,167,134,0,0,200,100);restore();clearRect(0,0,578,200);save();transform(1,0,0,1,150,30);drawImage([object HTMLImageElement],135,7,167,134,0,0,100,100);restore();'
|
||||||
'clearRect(0,0,578,200);save();transform(1,0,0,1,150,30);drawImage([object HTMLImageElement],135,7,167,134,0,0,100,100);restore();clearRect(0,0,578,200);save();transform(1,0,0,1,150,30);drawImage([object HTMLImageElement],135,7,167,134,0,0,200,100);restore();clearRect(0,0,578,200);save();transform(1,0,0,1,150,30);drawImage([object HTMLImageElement],135,7,167,134,0,0,100,100);restore();'
|
);
|
||||||
);
|
|
||||||
} else {
|
|
||||||
assert.equal(
|
|
||||||
trace,
|
|
||||||
'clearRect(0,0,578,200);save();transform(1,0,0,1,150,30);drawImage([object Object],135,7,167,134,0,0,100,100);restore();clearRect(0,0,578,200);save();transform(1,0,0,1,150,30);drawImage([object Object],135,7,167,134,0,0,200,100);restore();clearRect(0,0,578,200);save();transform(1,0,0,1,150,30);drawImage([object Object],135,7,167,134,0,0,100,100);restore();'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@ -241,17 +234,10 @@ describe('Image', function () {
|
|||||||
|
|
||||||
var trace = layer.getContext().getTrace();
|
var trace = layer.getContext().getTrace();
|
||||||
|
|
||||||
if (isBrowser) {
|
assert.equal(
|
||||||
assert.equal(
|
trace,
|
||||||
trace,
|
'clearRect(0,0,578,200);save();transform(1,0,0,1,150,30);globalAlpha=0.5;shadowColor=rgba(0,0,0,0.1);shadowBlur=10;shadowOffsetX=20;shadowOffsetY=20;drawImage([object HTMLImageElement],0,0,100,100);restore();'
|
||||||
'clearRect(0,0,578,200);save();transform(1,0,0,1,150,30);globalAlpha=0.5;shadowColor=rgba(0,0,0,0.1);shadowBlur=10;shadowOffsetX=20;shadowOffsetY=20;drawImage([object HTMLImageElement],0,0,100,100);restore();'
|
);
|
||||||
);
|
|
||||||
} else {
|
|
||||||
assert.equal(
|
|
||||||
trace,
|
|
||||||
'clearRect(0,0,578,200);save();transform(1,0,0,1,150,30);globalAlpha=0.5;shadowColor=rgba(0,0,0,0.1);shadowBlur=10;shadowOffsetX=20;shadowOffsetY=20;drawImage([object Object],0,0,100,100);restore();'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@ -285,17 +271,10 @@ describe('Image', function () {
|
|||||||
|
|
||||||
var trace = layer.getContext().getTrace();
|
var trace = layer.getContext().getTrace();
|
||||||
|
|
||||||
if (isBrowser) {
|
assert.equal(
|
||||||
assert.equal(
|
trace,
|
||||||
trace,
|
'clearRect(0,0,578,200);save();shadowColor=rgba(0,0,0,0.5);shadowBlur=10;shadowOffsetX=20;shadowOffsetY=20;globalAlpha=0.5;drawImage([object HTMLCanvasElement],0,0,578,200);restore();'
|
||||||
'clearRect(0,0,578,200);save();shadowColor=rgba(0,0,0,0.5);shadowBlur=10;shadowOffsetX=20;shadowOffsetY=20;globalAlpha=0.5;drawImage([object HTMLCanvasElement],0,0,578,200);restore();'
|
);
|
||||||
);
|
|
||||||
} else {
|
|
||||||
assert.equal(
|
|
||||||
trace,
|
|
||||||
'clearRect(0,0,578,200);save();shadowColor=rgba(0,0,0,0.5);shadowBlur=10;shadowOffsetX=20;shadowOffsetY=20;globalAlpha=0.5;drawImage([object Object],0,0,578,200);restore();'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -2296,17 +2296,10 @@ describe('Node', function () {
|
|||||||
|
|
||||||
var bufferTrace = stage.bufferCanvas.getContext().getTrace();
|
var bufferTrace = stage.bufferCanvas.getContext().getTrace();
|
||||||
|
|
||||||
if (isBrowser) {
|
assert.equal(
|
||||||
assert.equal(
|
sceneTrace,
|
||||||
sceneTrace,
|
'clearRect(0,0,578,200);save();globalAlpha=0.5;drawImage([object HTMLCanvasElement],0,0,578,200);restore();'
|
||||||
'clearRect(0,0,578,200);save();globalAlpha=0.5;drawImage([object HTMLCanvasElement],0,0,578,200);restore();'
|
);
|
||||||
);
|
|
||||||
} else {
|
|
||||||
assert.equal(
|
|
||||||
sceneTrace,
|
|
||||||
'clearRect(0,0,578,200);save();globalAlpha=0.5;drawImage([object Object],0,0,578,200);restore();'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
bufferTrace,
|
bufferTrace,
|
||||||
|
@ -1083,6 +1083,7 @@ describe('Path', function () {
|
|||||||
it('get point at path', function () {
|
it('get point at path', function () {
|
||||||
var stage = addStage();
|
var stage = addStage();
|
||||||
var layer = new Konva.Layer();
|
var layer = new Konva.Layer();
|
||||||
|
stage.add(layer);
|
||||||
const data =
|
const data =
|
||||||
'M 300,10 L 250,100 A 100 40 30 1 0 150 150 C 160,100, 290,100, 300,150';
|
'M 300,10 L 250,100 A 100 40 30 1 0 150 150 C 160,100, 290,100, 300,150';
|
||||||
var path = new Konva.Path({
|
var path = new Konva.Path({
|
||||||
@ -1102,7 +1103,7 @@ describe('Path', function () {
|
|||||||
var circle = new Konva.Circle({
|
var circle = new Konva.Circle({
|
||||||
x: p.x,
|
x: p.x,
|
||||||
y: p.y,
|
y: p.y,
|
||||||
radius: 2,
|
radius: 0.1,
|
||||||
fill: 'black',
|
fill: 'black',
|
||||||
stroke: 'black',
|
stroke: 'black',
|
||||||
});
|
});
|
||||||
@ -1164,13 +1165,49 @@ describe('Path', function () {
|
|||||||
{ x: 299.87435436448743, y: 149.4028482225714 },
|
{ x: 299.87435436448743, y: 149.4028482225714 },
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('get point at vertical path', function () {
|
||||||
|
var stage = addStage();
|
||||||
|
var layer = new Konva.Layer();
|
||||||
|
const data = 'M 614.96002,7.5147864 611.20262,429.59529';
|
||||||
|
var path = new Konva.Path({
|
||||||
|
stroke: 'red',
|
||||||
|
strokeWidth: 3,
|
||||||
|
data,
|
||||||
|
x: -600,
|
||||||
|
});
|
||||||
|
layer.add(path);
|
||||||
|
if (isBrowser) {
|
||||||
|
const SVGPath = document.createElementNS(
|
||||||
|
'http://www.w3.org/2000/svg',
|
||||||
|
'path'
|
||||||
|
) as SVGPathElement;
|
||||||
|
SVGPath.setAttribute('d', data);
|
||||||
|
for (var i = 0.001; i < path.getLength(); i += 1) {
|
||||||
|
var p = path.getPointAtLength(i);
|
||||||
|
console.log(p);
|
||||||
|
var circle = new Konva.Circle({
|
||||||
|
x: p.x + path.x(),
|
||||||
|
y: p.y + path.y(),
|
||||||
|
radius: 2,
|
||||||
|
fill: 'black',
|
||||||
|
stroke: 'black',
|
||||||
|
});
|
||||||
|
layer.add(circle);
|
||||||
|
const position = SVGPath.getPointAtLength(i);
|
||||||
|
console.log(position);
|
||||||
|
assert(Math.abs(p.x - position.x) <= 1);
|
||||||
|
assert(Math.abs(p.y - position.y) <= 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
stage.add(layer);
|
stage.add(layer);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('get point at path with float attrs', function () {
|
it('get point at path with float attrs', function () {
|
||||||
var stage = addStage();
|
var stage = addStage();
|
||||||
var layer = new Konva.Layer();
|
var layer = new Konva.Layer();
|
||||||
|
stage.add(layer);
|
||||||
|
|
||||||
const data =
|
const data =
|
||||||
'M419.0000314094981 342.88624187900973 L419.00003140949804 577.0038889378335 L465.014001082264 577.0038889378336 Z';
|
'M419.0000314094981 342.88624187900973 L419.00003140949804 577.0038889378335 L465.014001082264 577.0038889378336 Z';
|
||||||
|
@ -1550,6 +1550,83 @@ describe('Shape', function () {
|
|||||||
compareCanvases(canvas2, canvas1, 240, 110);
|
compareCanvases(canvas2, canvas1, 240, 110);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('export stage when buffer canvas is for line', async function () {
|
||||||
|
var stage = addStage();
|
||||||
|
|
||||||
|
var layer = new Konva.Layer();
|
||||||
|
stage.add(layer);
|
||||||
|
|
||||||
|
const group = new Konva.Group({
|
||||||
|
id: 'group01',
|
||||||
|
draggable: false,
|
||||||
|
opacity: 0.99,
|
||||||
|
});
|
||||||
|
layer.add(group);
|
||||||
|
|
||||||
|
const arrow = new Konva.Arrow({
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
points: [50, 25, 200, 25, 200, 225, 400, 225],
|
||||||
|
stroke: 'purple',
|
||||||
|
fill: 'purple',
|
||||||
|
strokeWidth: 4,
|
||||||
|
pointerAtEnding: true,
|
||||||
|
bezier: true,
|
||||||
|
});
|
||||||
|
group.add(arrow);
|
||||||
|
|
||||||
|
const bounds = layer.getClientRect({ relativeTo: stage });
|
||||||
|
const pos = stage.getPosition();
|
||||||
|
|
||||||
|
const canvas1 = layer.toCanvas({
|
||||||
|
pixelRatio: 1,
|
||||||
|
x: bounds.x + pos.x,
|
||||||
|
y: bounds.y + pos.y,
|
||||||
|
width: bounds.width,
|
||||||
|
height: bounds.height,
|
||||||
|
});
|
||||||
|
group.opacity(1);
|
||||||
|
const canvas2 = layer.toCanvas({
|
||||||
|
pixelRatio: 1,
|
||||||
|
x: bounds.x + pos.x,
|
||||||
|
y: bounds.y + pos.y,
|
||||||
|
width: bounds.width,
|
||||||
|
height: bounds.height,
|
||||||
|
});
|
||||||
|
|
||||||
|
compareCanvases(canvas1, canvas2, 240, 110);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('check stroke rendering on buffer canvas', async function () {
|
||||||
|
var stage = addStage();
|
||||||
|
|
||||||
|
var layer = new Konva.Layer();
|
||||||
|
stage.add(layer);
|
||||||
|
|
||||||
|
const rect = new Konva.Rect({
|
||||||
|
x: 150,
|
||||||
|
y: 50,
|
||||||
|
width: 50,
|
||||||
|
height: 50,
|
||||||
|
fill: '#039BE5',
|
||||||
|
stroke: 'yellow',
|
||||||
|
strokeWidth: 5,
|
||||||
|
shadowColor: 'black',
|
||||||
|
shadowOffset: { x: 10, y: 10 },
|
||||||
|
shadowOpacity: 0.5,
|
||||||
|
});
|
||||||
|
|
||||||
|
layer.add(rect);
|
||||||
|
|
||||||
|
const canvas1 = layer.toCanvas();
|
||||||
|
rect.cache();
|
||||||
|
const canvas2 = layer.toCanvas();
|
||||||
|
|
||||||
|
// throw new Error('stop');
|
||||||
|
|
||||||
|
compareCanvases(canvas1, canvas2);
|
||||||
|
});
|
||||||
|
|
||||||
// ======================================================
|
// ======================================================
|
||||||
it('optional disable shadow for stroke', function () {
|
it('optional disable shadow for stroke', function () {
|
||||||
var stage = addStage();
|
var stage = addStage();
|
||||||
|
@ -8,6 +8,7 @@ import {
|
|||||||
simulateTouchStart,
|
simulateTouchStart,
|
||||||
simulateTouchMove,
|
simulateTouchMove,
|
||||||
simulateTouchEnd,
|
simulateTouchEnd,
|
||||||
|
simulatePointerMove,
|
||||||
compareCanvases,
|
compareCanvases,
|
||||||
createCanvas,
|
createCanvas,
|
||||||
showHit,
|
showHit,
|
||||||
@ -1222,6 +1223,36 @@ describe('Stage', function () {
|
|||||||
assert.equal(count, 2);
|
assert.equal(count, 2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('stage pointerleave should not fire when leaving a child', function () {
|
||||||
|
var stage = addStage();
|
||||||
|
var layer = new Konva.Layer();
|
||||||
|
stage.add(layer);
|
||||||
|
|
||||||
|
var circle = new Konva.Circle({
|
||||||
|
fill: 'red',
|
||||||
|
radius: 30,
|
||||||
|
x: 50,
|
||||||
|
y: 50,
|
||||||
|
});
|
||||||
|
layer.add(circle);
|
||||||
|
layer.draw();
|
||||||
|
|
||||||
|
var stageLeave = 0;
|
||||||
|
var circleLeave = 0;
|
||||||
|
stage.on('pointerleave', function () {
|
||||||
|
stageLeave += 1;
|
||||||
|
});
|
||||||
|
circle.on('pointerleave', function () {
|
||||||
|
circleLeave += 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
simulatePointerMove(stage, { x: 50, y: 50 });
|
||||||
|
simulatePointerMove(stage, { x: 90, y: 50 });
|
||||||
|
|
||||||
|
assert.equal(circleLeave, 1, 'circle pointerleave should fire');
|
||||||
|
assert.equal(stageLeave, 0, 'stage pointerleave should not fire');
|
||||||
|
});
|
||||||
|
|
||||||
it('toDataURL with hidden layer', function () {
|
it('toDataURL with hidden layer', function () {
|
||||||
var stage = addStage();
|
var stage = addStage();
|
||||||
var layer = new Konva.Layer();
|
var layer = new Konva.Layer();
|
||||||
|
@ -146,7 +146,7 @@ describe('Text', function () {
|
|||||||
var text = new Konva.Text({
|
var text = new Konva.Text({
|
||||||
x: 10,
|
x: 10,
|
||||||
y: 10,
|
y: 10,
|
||||||
text: '😬',
|
text: '😬👧🏿',
|
||||||
fontSize: 50,
|
fontSize: 50,
|
||||||
letterSpacing: 1,
|
letterSpacing: 1,
|
||||||
});
|
});
|
||||||
@ -159,9 +159,42 @@ describe('Text', function () {
|
|||||||
context.textBaseline = 'middle';
|
context.textBaseline = 'middle';
|
||||||
context.font = 'normal normal 50px Arial';
|
context.font = 'normal normal 50px Arial';
|
||||||
context.fillStyle = 'darkgrey';
|
context.fillStyle = 'darkgrey';
|
||||||
context.fillText('😬', 10, 10 + 25);
|
context.fillText('😬👧🏿', 10, 10 + 25);
|
||||||
|
|
||||||
compareLayerAndCanvas(layer, canvas, 254);
|
if (isBrowser) {
|
||||||
|
compareLayerAndCanvas(layer, canvas, 254, 100);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('check emoji rendering', function () {
|
||||||
|
var stage = addStage();
|
||||||
|
var layer = new Konva.Layer();
|
||||||
|
|
||||||
|
var text = new Konva.Text({
|
||||||
|
text: '😁😁😁',
|
||||||
|
x: 10,
|
||||||
|
y: 10,
|
||||||
|
fontSize: 20,
|
||||||
|
draggable: true,
|
||||||
|
width: 65,
|
||||||
|
fill: 'black',
|
||||||
|
scaleY: 0.9999999999999973,
|
||||||
|
});
|
||||||
|
|
||||||
|
layer.add(text);
|
||||||
|
stage.add(layer);
|
||||||
|
|
||||||
|
var canvas = createCanvas();
|
||||||
|
var context = canvas.getContext('2d');
|
||||||
|
context.textBaseline = 'middle';
|
||||||
|
context.font = 'normal normal 20px Arial';
|
||||||
|
context.fillStyle = 'black';
|
||||||
|
context.fillText('😁😁', 10, 10 + 10);
|
||||||
|
context.fillText('😁', 10, 10 + 30);
|
||||||
|
|
||||||
|
if (isBrowser) {
|
||||||
|
compareLayerAndCanvas(layer, canvas, 254, 100);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('check hindi with letterSpacing', function () {
|
it('check hindi with letterSpacing', function () {
|
||||||
@ -282,7 +315,7 @@ describe('Text', function () {
|
|||||||
var oldWidth = text.width();
|
var oldWidth = text.width();
|
||||||
text.letterSpacing(10);
|
text.letterSpacing(10);
|
||||||
|
|
||||||
assert.equal(text.width(), oldWidth + 40);
|
assert.equal(text.width(), oldWidth + 50);
|
||||||
layer.draw();
|
layer.draw();
|
||||||
});
|
});
|
||||||
// ======================================================
|
// ======================================================
|
||||||
@ -543,7 +576,7 @@ describe('Text', function () {
|
|||||||
if (isBrowser) {
|
if (isBrowser) {
|
||||||
assert.equal(
|
assert.equal(
|
||||||
layer.getContext().getTrace(false, true),
|
layer.getContext().getTrace(false, true),
|
||||||
"clearRect(0,0,578,200);save();transform(1,0,0,1,10,10);font=normal normal 14px Arial;textBaseline=middle;textAlign=left;translate(0,0);save();fillStyle=black;fillText(HEADING,18,7);restore();save();fillStyle=black;fillText(,50,21);restore();save();fillStyle=black;fillText(All the world's,7,35);restore();save();fillStyle=black;fillText(a stage,,25,49);restore();save();fillStyle=black;fillText(merely,28,63);restore();save();fillStyle=black;fillText(players. They,7,77);restore();save();fillStyle=black;fillText(have…,27,91);restore();restore();"
|
"clearRect(0,0,578,200);save();transform(1,0,0,1,10,10);font=normal normal 14px Arial;textBaseline=middle;textAlign=left;translate(0,0);save();fillStyle=black;fillText(HEADING,18,7);restore();save();fillStyle=black;fillText(,50,21);restore();save();fillStyle=black;fillText(All the world's a,1,35);restore();save();fillStyle=black;fillText(stage, merely,7,49);restore();save();fillStyle=black;fillText(players. They,7,63);restore();save();fillStyle=black;fillText(have theirrrrrrr,5,77);restore();save();fillStyle=black;fillText(exits and…,14,91);restore();restore();"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -764,7 +797,7 @@ describe('Text', function () {
|
|||||||
width: 380,
|
width: 380,
|
||||||
align: 'justify',
|
align: 'justify',
|
||||||
letterSpacing: 5,
|
letterSpacing: 5,
|
||||||
textDecoration: 'underline',
|
textDecoration: 'underline line-through',
|
||||||
padding: 20,
|
padding: 20,
|
||||||
draggable: true,
|
draggable: true,
|
||||||
});
|
});
|
||||||
@ -780,7 +813,7 @@ describe('Text', function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var trace =
|
var trace =
|
||||||
'fillText(c,69.696,77);fillStyle=#555;fillText(e,81.696,77);fillStyle=#555;fillText(s,94.482,77);fillStyle=#555;fillText(;,106.482,77);fillStyle=#555;fillText( ,117.549,77);fillStyle=#555;fillText(A,126.438,77);fillStyle=#555;fillText(n,140.776,77);fillStyle=#555;fillText(d,153.563,77);fillStyle=#555;fillText( ,168.525,77);fillStyle=#555;fillText(o,177.415,77);fillStyle=#555;fillText(n,190.201,77);fillStyle=#555;fillText(e,202.987,77);fillStyle=#555;fillText( ,217.95,77);fillStyle=#555;fillText(m,226.84,77);fillStyle=#555;fillText(a,243.502,77);fillStyle=#555;fillText(n,256.288,77);fillStyle=#555;fillText( ,271.251,77);fillStyle=#555;fillText(i,280.141,77);fillStyle=#555;fillText(n,288.251,77);fillStyle=#555;fillText( ,303.214,77);fillStyle=#555;fillText(h,312.104,77);fillStyle=#555;fillText(i,324.89,77);fillStyle=#555;fillText(s,333,77);restore();save();save();beginPath();moveTo(0,98);lineTo(245,98);stroke();restore();fillStyle=#555;fillText(t,0,91);fillStyle=#555;fillText(i,8.89,91);fillStyle=#555;fillText(m,17,91);fillStyle=#555;fillText(e,33.662,91);fillStyle=#555;fillText( ,46.448,91);fillStyle=#555;fillText(p,55.338,91);fillStyle=#555;fillText(l,68.124,91);fillStyle=#555;fillText(a,76.234,91);fillStyle=#555;fillText(y,89.021,91);fillStyle=#555;fillText(s,101.021,91);fillStyle=#555;fillText( ,113.021,91);fillStyle=#555;fillText(m,121.91,91);fillStyle=#555;fillText(a,138.572,91);fillStyle=#555;fillText(n,151.358,91);fillStyle=#555;fillText(y,164.145,91);fillStyle=#555;fillText( ,176.145,91);fillStyle=#555;fillText(p,185.034,91);fillStyle=#555;fillText(a,197.82,91);fillStyle=#555;fillText(r,210.606,91);fillStyle=#555;fillText(t,220.269,91);fillStyle=#555;fillText(s,229.158,91);fillStyle=#555;fillText(.,241.158,91);restore();restore();';
|
'fillText(;,106.482,77);fillStyle=#555;fillText( ,116.549,77);fillStyle=#555;fillText(A,125.438,77);fillStyle=#555;fillText(n,139.776,77);fillStyle=#555;fillText(d,152.563,77);fillStyle=#555;fillText( ,166.525,77);fillStyle=#555;fillText(o,175.415,77);fillStyle=#555;fillText(n,188.201,77);fillStyle=#555;fillText(e,200.987,77);fillStyle=#555;fillText( ,214.95,77);fillStyle=#555;fillText(m,223.84,77);fillStyle=#555;fillText(a,240.502,77);fillStyle=#555;fillText(n,253.288,77);fillStyle=#555;fillText( ,267.251,77);fillStyle=#555;fillText(i,276.141,77);fillStyle=#555;fillText(n,284.251,77);fillStyle=#555;fillText( ,298.214,77);fillStyle=#555;fillText(h,307.104,77);fillStyle=#555;fillText(i,319.89,77);fillStyle=#555;fillText(s,328,77);restore();save();save();beginPath();moveTo(0,98);lineTo(250,98);stroke();restore();save();beginPath();moveTo(0,91);lineTo(250,91);stroke();restore();fillStyle=#555;fillText(t,0,91);fillStyle=#555;fillText(i,8.89,91);fillStyle=#555;fillText(m,17,91);fillStyle=#555;fillText(e,33.662,91);fillStyle=#555;fillText( ,46.448,91);fillStyle=#555;fillText(p,55.338,91);fillStyle=#555;fillText(l,68.124,91);fillStyle=#555;fillText(a,76.234,91);fillStyle=#555;fillText(y,89.021,91);fillStyle=#555;fillText(s,101.021,91);fillStyle=#555;fillText( ,113.021,91);fillStyle=#555;fillText(m,121.91,91);fillStyle=#555;fillText(a,138.572,91);fillStyle=#555;fillText(n,151.358,91);fillStyle=#555;fillText(y,164.145,91);fillStyle=#555;fillText( ,176.145,91);fillStyle=#555;fillText(p,185.034,91);fillStyle=#555;fillText(a,197.82,91);fillStyle=#555;fillText(r,210.606,91);fillStyle=#555;fillText(t,220.269,91);fillStyle=#555;fillText(s,229.158,91);fillStyle=#555;fillText(.,241.158,91);restore();restore();';
|
||||||
|
|
||||||
assert.equal(layer.getContext().getTrace(), trace);
|
assert.equal(layer.getContext().getTrace(), trace);
|
||||||
});
|
});
|
||||||
@ -1244,9 +1277,9 @@ describe('Text', function () {
|
|||||||
// so we need to adjust offset
|
// so we need to adjust offset
|
||||||
const diff = isBrowser ? 4 : 50;
|
const diff = isBrowser ? 4 : 50;
|
||||||
assert.equal(Math.abs(Math.round(text1.width()) - 1725) < diff, true);
|
assert.equal(Math.abs(Math.round(text1.width()) - 1725) < diff, true);
|
||||||
assert.equal(Math.abs(Math.round(text2.width()) - 2613) < diff, true);
|
assert.equal(Math.abs(Math.round(text2.width()) - 2616) < diff, true);
|
||||||
assert.equal(Math.abs(Math.round(text3.width()) - 2005) < diff, true);
|
assert.equal(Math.abs(Math.round(text3.width()) - 2009) < diff, true);
|
||||||
assert.equal(Math.abs(Math.round(text4.width()) - 1932) < diff, true);
|
assert.equal(Math.abs(Math.round(text4.width()) - 1936) < diff, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('default text color should be black', function () {
|
it('default text color should be black', function () {
|
||||||
|
@ -5088,4 +5088,49 @@ describe('Transformer', function () {
|
|||||||
}, 100);
|
}, 100);
|
||||||
}, 100);
|
}, 100);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should properly clean up subscriptions on detach/destroy', function () {
|
||||||
|
var stage = addStage();
|
||||||
|
var layer = new Konva.Layer();
|
||||||
|
stage.add(layer);
|
||||||
|
|
||||||
|
var rect = new Konva.Rect({
|
||||||
|
x: 100,
|
||||||
|
y: 60,
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
fill: 'yellow',
|
||||||
|
});
|
||||||
|
layer.add(rect);
|
||||||
|
// draw to attach all listeners
|
||||||
|
layer.draw();
|
||||||
|
|
||||||
|
// Count initial number of event listeners
|
||||||
|
var initialListeners = Object.keys(rect.eventListeners).length;
|
||||||
|
|
||||||
|
// Create and attach first transformer
|
||||||
|
var tr1 = new Konva.Transformer({
|
||||||
|
nodes: [rect],
|
||||||
|
});
|
||||||
|
layer.add(tr1);
|
||||||
|
|
||||||
|
// Destroy first transformer
|
||||||
|
tr1.destroy();
|
||||||
|
|
||||||
|
// Create and attach second transformer
|
||||||
|
var tr2 = new Konva.Transformer({
|
||||||
|
nodes: [rect],
|
||||||
|
});
|
||||||
|
layer.add(tr2);
|
||||||
|
|
||||||
|
// Destroy second transformer
|
||||||
|
tr2.destroy();
|
||||||
|
|
||||||
|
// Check that we have same number of listeners as initially
|
||||||
|
assert.equal(
|
||||||
|
Object.keys(rect.eventListeners).length,
|
||||||
|
initialListeners,
|
||||||
|
'Event listeners should be cleaned up properly'
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -98,8 +98,8 @@ describe('Tween', function () {
|
|||||||
|
|
||||||
tween.destroy();
|
tween.destroy();
|
||||||
|
|
||||||
assert.equal(Konva.Tween.tweens[circle._id].x, undefined);
|
assert.equal(Konva.Tween.tweens[circle._id], undefined);
|
||||||
assert.equal(Konva.Tween.attrs[circle._id][tween._id], undefined);
|
assert.equal(Konva.Tween.attrs[circle._id], undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
// ======================================================
|
// ======================================================
|
||||||
@ -238,7 +238,7 @@ describe('Tween', function () {
|
|||||||
duration: 0.1,
|
duration: 0.1,
|
||||||
onFinish: function () {
|
onFinish: function () {
|
||||||
assert.equal(circle.x(), stage.width() / 2);
|
assert.equal(circle.x(), stage.width() / 2);
|
||||||
assert.equal(Object.keys(Konva.Tween.attrs[circle._id]).length, 0);
|
assert.equal(Konva.Tween.attrs[circle._id], undefined);
|
||||||
done();
|
done();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -305,16 +305,10 @@ describe('Tween', function () {
|
|||||||
points: [100, 100, 200, 100, 200, 200, 100, 200],
|
points: [100, 100, 200, 100, 200, 200, 100, 200],
|
||||||
duration: 0.1,
|
duration: 0.1,
|
||||||
onFinish: function () {
|
onFinish: function () {
|
||||||
assert.deepEqual(line.points(), [
|
assert.deepEqual(
|
||||||
100,
|
line.points(),
|
||||||
100,
|
[100, 100, 200, 100, 200, 200, 100, 200]
|
||||||
200,
|
);
|
||||||
100,
|
|
||||||
200,
|
|
||||||
200,
|
|
||||||
100,
|
|
||||||
200,
|
|
||||||
]);
|
|
||||||
done();
|
done();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -364,16 +358,10 @@ describe('Tween', function () {
|
|||||||
tween.reverse();
|
tween.reverse();
|
||||||
},
|
},
|
||||||
onReset: function () {
|
onReset: function () {
|
||||||
assert.deepEqual(line.points(), [
|
assert.deepEqual(
|
||||||
100,
|
line.points(),
|
||||||
100,
|
[100, 100, 200, 100, 200, 200, 100, 200]
|
||||||
200,
|
);
|
||||||
100,
|
|
||||||
200,
|
|
||||||
200,
|
|
||||||
100,
|
|
||||||
200,
|
|
||||||
]);
|
|
||||||
done();
|
done();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -57,19 +57,16 @@ function isType(object, type) {
|
|||||||
|
|
||||||
// Type Conversion
|
// Type Conversion
|
||||||
function copyImageData(imageData) {
|
function copyImageData(imageData) {
|
||||||
var height = imageData.height,
|
const height = imageData.height,
|
||||||
width = imageData.width,
|
width = imageData.width,
|
||||||
data = imageData.data,
|
data = imageData.data;
|
||||||
newImageData,
|
|
||||||
newData,
|
|
||||||
i;
|
|
||||||
|
|
||||||
canvas.width = width;
|
canvas.width = width;
|
||||||
canvas.height = height;
|
canvas.height = height;
|
||||||
newImageData = context.getImageData(0, 0, width, height);
|
const newImageData = context.getImageData(0, 0, width, height);
|
||||||
newData = newImageData.data;
|
const newData = newImageData.data;
|
||||||
|
|
||||||
for (i = imageData.data.length; i--; ) {
|
for (let i = imageData.data.length; i--; ) {
|
||||||
newData[i] = data[i];
|
newData[i] = data[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,7 +87,7 @@ function toImageData(object) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
function toImageDataFromImage(image) {
|
function toImageDataFromImage(image) {
|
||||||
var height = image.height,
|
const height = image.height,
|
||||||
width = image.width;
|
width = image.width;
|
||||||
canvas.width = width;
|
canvas.width = width;
|
||||||
canvas.height = height;
|
canvas.height = height;
|
||||||
@ -99,7 +96,7 @@ function toImageDataFromImage(image) {
|
|||||||
return context.getImageData(0, 0, width, height);
|
return context.getImageData(0, 0, width, height);
|
||||||
}
|
}
|
||||||
function toImageDataFromCanvas(canvas) {
|
function toImageDataFromCanvas(canvas) {
|
||||||
var height = canvas.height,
|
const height = canvas.height,
|
||||||
width = canvas.width,
|
width = canvas.width,
|
||||||
context = canvas.getContext('2d');
|
context = canvas.getContext('2d');
|
||||||
if (!width || !height) {
|
if (!width || !height) {
|
||||||
@ -109,13 +106,13 @@ function toImageDataFromCanvas(canvas) {
|
|||||||
return context.getImageData(0, 0, width, height);
|
return context.getImageData(0, 0, width, height);
|
||||||
}
|
}
|
||||||
function toImageDataFromContext(context) {
|
function toImageDataFromContext(context) {
|
||||||
var canvas = context.canvas,
|
const canvas = context.canvas,
|
||||||
height = canvas.height,
|
height = canvas.height,
|
||||||
width = canvas.width;
|
width = canvas.width;
|
||||||
return context.getImageData(0, 0, width, height);
|
return context.getImageData(0, 0, width, height);
|
||||||
}
|
}
|
||||||
function toCanvas(object) {
|
function toCanvas(object) {
|
||||||
var data = toImageData(object),
|
const data = toImageData(object),
|
||||||
canvas = getCanvas(data.width, data.height),
|
canvas = getCanvas(data.width, data.height),
|
||||||
context = canvas.getContext('2d');
|
context = canvas.getContext('2d');
|
||||||
|
|
||||||
@ -135,16 +132,15 @@ function equalDimensions(a, b) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function equal(a, b, tolerance, secondTol) {
|
export function equal(a, b, tolerance, secondTol) {
|
||||||
var aData = a.data,
|
const aData = a.data,
|
||||||
bData = b.data,
|
bData = b.data,
|
||||||
length = aData.length,
|
length = aData.length;
|
||||||
i;
|
|
||||||
|
|
||||||
tolerance = tolerance || 0;
|
tolerance = tolerance || 0;
|
||||||
|
|
||||||
var count = 0;
|
let count = 0;
|
||||||
if (!equalDimensions(a, b)) return false;
|
if (!equalDimensions(a, b)) return false;
|
||||||
for (i = length; i--; ) {
|
for (let i = length; i--; ) {
|
||||||
const d = Math.abs(aData[i] - bData[i]);
|
const d = Math.abs(aData[i] - bData[i]);
|
||||||
if (aData[i] !== bData[i] && d > tolerance) {
|
if (aData[i] !== bData[i] && d > tolerance) {
|
||||||
if (!secondTol) {
|
if (!secondTol) {
|
||||||
@ -168,21 +164,15 @@ function diff(a, b, options) {
|
|||||||
return (equalDimensions(a, b) ? diffEqual : diffUnequal)(a, b, options);
|
return (equalDimensions(a, b) ? diffEqual : diffUnequal)(a, b, options);
|
||||||
}
|
}
|
||||||
function diffEqual(a, b, options) {
|
function diffEqual(a, b, options) {
|
||||||
var height = a.height,
|
const height = a.height,
|
||||||
width = a.width,
|
width = a.width,
|
||||||
c = getImageData(width, height), // c = a - b
|
c = getImageData(width, height), // c = a - b
|
||||||
aData = a.data,
|
aData = a.data,
|
||||||
bData = b.data,
|
bData = b.data,
|
||||||
cData = c.data,
|
cData = c.data,
|
||||||
length = cData.length,
|
length = cData.length;
|
||||||
row,
|
|
||||||
column,
|
|
||||||
i,
|
|
||||||
j,
|
|
||||||
k,
|
|
||||||
v;
|
|
||||||
|
|
||||||
for (i = 0; i < length; i += 4) {
|
for (let i = 0; i < length; i += 4) {
|
||||||
cData[i] = Math.abs(aData[i] - bData[i]);
|
cData[i] = Math.abs(aData[i] - bData[i]);
|
||||||
cData[i + 1] = Math.abs(aData[i + 1] - bData[i + 1]);
|
cData[i + 1] = Math.abs(aData[i + 1] - bData[i + 1]);
|
||||||
cData[i + 2] = Math.abs(aData[i + 2] - bData[i + 2]);
|
cData[i + 2] = Math.abs(aData[i + 2] - bData[i + 2]);
|
||||||
@ -192,32 +182,26 @@ function diffEqual(a, b, options) {
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
function diffUnequal(a, b, options) {
|
function diffUnequal(a, b, options) {
|
||||||
var height = Math.max(a.height, b.height),
|
const height = Math.max(a.height, b.height),
|
||||||
width = Math.max(a.width, b.width),
|
width = Math.max(a.width, b.width),
|
||||||
c = getImageData(width, height), // c = a - b
|
c = getImageData(width, height), // c = a - b
|
||||||
aData = a.data,
|
aData = a.data,
|
||||||
bData = b.data,
|
bData = b.data,
|
||||||
cData = c.data,
|
cData = c.data,
|
||||||
align = options && options.align,
|
align = options && options.align;
|
||||||
rowOffset,
|
var rowOffset,
|
||||||
columnOffset,
|
columnOffset;
|
||||||
row,
|
|
||||||
column,
|
|
||||||
i,
|
|
||||||
j,
|
|
||||||
k,
|
|
||||||
v;
|
|
||||||
|
|
||||||
for (i = cData.length - 1; i > 0; i = i - 4) {
|
for (let i = cData.length - 1; i > 0; i = i - 4) {
|
||||||
cData[i] = 255;
|
cData[i] = 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add First Image
|
// Add First Image
|
||||||
offsets(a);
|
offsets(a);
|
||||||
for (row = a.height; row--; ) {
|
for (let row = a.height; row--; ) {
|
||||||
for (column = a.width; column--; ) {
|
for (let column = a.width; column--; ) {
|
||||||
i = 4 * ((row + rowOffset) * width + (column + columnOffset));
|
const i = 4 * ((row + rowOffset) * width + (column + columnOffset));
|
||||||
j = 4 * (row * a.width + column);
|
const j = 4 * (row * a.width + column);
|
||||||
cData[i + 0] = aData[j + 0]; // r
|
cData[i + 0] = aData[j + 0]; // r
|
||||||
cData[i + 1] = aData[j + 1]; // g
|
cData[i + 1] = aData[j + 1]; // g
|
||||||
cData[i + 2] = aData[j + 2]; // b
|
cData[i + 2] = aData[j + 2]; // b
|
||||||
@ -227,10 +211,10 @@ function diffUnequal(a, b, options) {
|
|||||||
|
|
||||||
// Subtract Second Image
|
// Subtract Second Image
|
||||||
offsets(b);
|
offsets(b);
|
||||||
for (row = b.height; row--; ) {
|
for (let row = b.height; row--; ) {
|
||||||
for (column = b.width; column--; ) {
|
for (let column = b.width; column--; ) {
|
||||||
i = 4 * ((row + rowOffset) * width + (column + columnOffset));
|
const i = 4 * ((row + rowOffset) * width + (column + columnOffset));
|
||||||
j = 4 * (row * b.width + column);
|
const j = 4 * (row * b.width + column);
|
||||||
cData[i + 0] = Math.abs(cData[i + 0] - bData[j + 0]); // r
|
cData[i + 0] = Math.abs(cData[i + 0] - bData[j + 0]); // r
|
||||||
cData[i + 1] = Math.abs(cData[i + 1] - bData[j + 1]); // g
|
cData[i + 1] = Math.abs(cData[i + 1] - bData[j + 1]); // g
|
||||||
cData[i + 2] = Math.abs(cData[i + 2] - bData[j + 2]); // b
|
cData[i + 2] = Math.abs(cData[i + 2] - bData[j + 2]); // b
|
||||||
@ -253,8 +237,7 @@ function diffUnequal(a, b, options) {
|
|||||||
|
|
||||||
// Validation
|
// Validation
|
||||||
function checkType(...args) {
|
function checkType(...args) {
|
||||||
var i;
|
for (let i = 0; i < args.length; i++) {
|
||||||
for (i = 0; i < args.length; i++) {
|
|
||||||
if (!isImageType(args[i])) {
|
if (!isImageType(args[i])) {
|
||||||
throw {
|
throw {
|
||||||
name: 'ImageTypeError',
|
name: 'ImageTypeError',
|
||||||
|
@ -131,6 +131,14 @@ export function compareCanvases(canvas1, canvas2, tol?, secondTol?) {
|
|||||||
b.appendChild(canvas2);
|
b.appendChild(canvas2);
|
||||||
c.appendChild(diffCanvas);
|
c.appendChild(diffCanvas);
|
||||||
div.appendChild(b);
|
div.appendChild(b);
|
||||||
|
if (!canvas1.parentNode) {
|
||||||
|
const d = get('div', '<div>Original:</div>');
|
||||||
|
canvas1.style.position = '';
|
||||||
|
canvas1.style.display = '';
|
||||||
|
d.style.float = 'left';
|
||||||
|
d.appendChild(canvas1);
|
||||||
|
div.appendChild(d);
|
||||||
|
}
|
||||||
div.appendChild(c);
|
div.appendChild(c);
|
||||||
getContainer().appendChild(div);
|
getContainer().appendChild(div);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user