@nasser-sw/fabric 7.0.1-beta3 → 7.0.1-beta4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/0 +0 -0
- package/dist/index.js +323 -155
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/index.min.mjs +1 -1
- package/dist/index.min.mjs.map +1 -1
- package/dist/index.mjs +323 -155
- package/dist/index.mjs.map +1 -1
- package/dist/index.node.cjs +323 -155
- package/dist/index.node.cjs.map +1 -1
- package/dist/index.node.mjs +323 -155
- package/dist/index.node.mjs.map +1 -1
- package/dist/package.json.min.mjs +1 -1
- package/dist/package.json.mjs +1 -1
- package/dist/src/shapes/Line.d.ts +31 -86
- package/dist/src/shapes/Line.d.ts.map +1 -1
- package/dist/src/shapes/Line.min.mjs +1 -1
- package/dist/src/shapes/Line.min.mjs.map +1 -1
- package/dist/src/shapes/Line.mjs +323 -154
- package/dist/src/shapes/Line.mjs.map +1 -1
- package/dist-extensions/src/shapes/CustomLine.d.ts +10 -0
- package/dist-extensions/src/shapes/CustomLine.d.ts.map +1 -0
- package/dist-extensions/src/shapes/Line.d.ts +31 -86
- package/dist-extensions/src/shapes/Line.d.ts.map +1 -1
- package/fabric-test-editor.html +157 -8
- package/fabric-test2.html +513 -0
- package/fabric.ts +182 -182
- package/package.json +1 -1
- package/src/shapes/Line.ts +372 -158
- package/debug/konva/CHANGELOG.md +0 -1474
- package/debug/konva/LICENSE +0 -22
- package/debug/konva/README.md +0 -205
- package/debug/konva/gulpfile.mjs +0 -110
- package/debug/konva/package.json +0 -139
- package/debug/konva/release.sh +0 -65
- package/debug/konva/resources/doc-includes/ContainerParams.txt +0 -6
- package/debug/konva/resources/doc-includes/NodeParams.txt +0 -20
- package/debug/konva/resources/doc-includes/ShapeParams.txt +0 -53
- package/debug/konva/resources/jsdoc.conf.json +0 -28
- package/debug/konva/rollup.config.mjs +0 -32
- package/debug/konva/src/Animation.ts +0 -237
- package/debug/konva/src/BezierFunctions.ts +0 -826
- package/debug/konva/src/Canvas.ts +0 -193
- package/debug/konva/src/Container.ts +0 -649
- package/debug/konva/src/Context.ts +0 -1017
- package/debug/konva/src/Core.ts +0 -5
- package/debug/konva/src/DragAndDrop.ts +0 -173
- package/debug/konva/src/Factory.ts +0 -246
- package/debug/konva/src/FastLayer.ts +0 -29
- package/debug/konva/src/Global.ts +0 -210
- package/debug/konva/src/Group.ts +0 -31
- package/debug/konva/src/Layer.ts +0 -546
- package/debug/konva/src/Node.ts +0 -3477
- package/debug/konva/src/PointerEvents.ts +0 -67
- package/debug/konva/src/Shape.ts +0 -2081
- package/debug/konva/src/Stage.ts +0 -1000
- package/debug/konva/src/Tween.ts +0 -811
- package/debug/konva/src/Util.ts +0 -1123
- package/debug/konva/src/Validators.ts +0 -210
- package/debug/konva/src/_CoreInternals.ts +0 -85
- package/debug/konva/src/_FullInternals.ts +0 -171
- package/debug/konva/src/canvas-backend.ts +0 -36
- package/debug/konva/src/filters/Blur.ts +0 -388
- package/debug/konva/src/filters/Brighten.ts +0 -48
- package/debug/konva/src/filters/Brightness.ts +0 -30
- package/debug/konva/src/filters/Contrast.ts +0 -75
- package/debug/konva/src/filters/Emboss.ts +0 -207
- package/debug/konva/src/filters/Enhance.ts +0 -154
- package/debug/konva/src/filters/Grayscale.ts +0 -25
- package/debug/konva/src/filters/HSL.ts +0 -108
- package/debug/konva/src/filters/HSV.ts +0 -106
- package/debug/konva/src/filters/Invert.ts +0 -23
- package/debug/konva/src/filters/Kaleidoscope.ts +0 -274
- package/debug/konva/src/filters/Mask.ts +0 -220
- package/debug/konva/src/filters/Noise.ts +0 -44
- package/debug/konva/src/filters/Pixelate.ts +0 -107
- package/debug/konva/src/filters/Posterize.ts +0 -46
- package/debug/konva/src/filters/RGB.ts +0 -82
- package/debug/konva/src/filters/RGBA.ts +0 -103
- package/debug/konva/src/filters/Sepia.ts +0 -27
- package/debug/konva/src/filters/Solarize.ts +0 -29
- package/debug/konva/src/filters/Threshold.ts +0 -44
- package/debug/konva/src/index.ts +0 -3
- package/debug/konva/src/shapes/Arc.ts +0 -176
- package/debug/konva/src/shapes/Arrow.ts +0 -231
- package/debug/konva/src/shapes/Circle.ts +0 -76
- package/debug/konva/src/shapes/Ellipse.ts +0 -121
- package/debug/konva/src/shapes/Image.ts +0 -319
- package/debug/konva/src/shapes/Label.ts +0 -386
- package/debug/konva/src/shapes/Line.ts +0 -364
- package/debug/konva/src/shapes/Path.ts +0 -1013
- package/debug/konva/src/shapes/Rect.ts +0 -79
- package/debug/konva/src/shapes/RegularPolygon.ts +0 -167
- package/debug/konva/src/shapes/Ring.ts +0 -94
- package/debug/konva/src/shapes/Sprite.ts +0 -370
- package/debug/konva/src/shapes/Star.ts +0 -125
- package/debug/konva/src/shapes/Text.ts +0 -1065
- package/debug/konva/src/shapes/TextPath.ts +0 -583
- package/debug/konva/src/shapes/Transformer.ts +0 -1889
- package/debug/konva/src/shapes/Wedge.ts +0 -129
- package/debug/konva/src/skia-backend.ts +0 -35
- package/debug/konva/src/types.ts +0 -84
- package/debug/konva/tsconfig.json +0 -31
- package/debug/konva/tsconfig.test.json +0 -7
package/debug/konva/src/Node.ts
DELETED
|
@@ -1,3477 +0,0 @@
|
|
|
1
|
-
import type { Canvas } from './Canvas.ts';
|
|
2
|
-
import { HitCanvas, SceneCanvas } from './Canvas.ts';
|
|
3
|
-
import type { Container } from './Container.ts';
|
|
4
|
-
import type { Context } from './Context.ts';
|
|
5
|
-
import { isCSSFiltersSupported } from './Context.ts';
|
|
6
|
-
import { DD } from './DragAndDrop.ts';
|
|
7
|
-
import { Factory } from './Factory.ts';
|
|
8
|
-
import { Konva } from './Global.ts';
|
|
9
|
-
import type { Layer } from './Layer.ts';
|
|
10
|
-
import type { Shape } from './Shape.ts';
|
|
11
|
-
import type { Stage } from './Stage.ts';
|
|
12
|
-
import type { GetSet, IRect, Vector2d } from './types.ts';
|
|
13
|
-
import { Transform, Util } from './Util.ts';
|
|
14
|
-
import {
|
|
15
|
-
getBooleanValidator,
|
|
16
|
-
getNumberValidator,
|
|
17
|
-
getStringValidator,
|
|
18
|
-
} from './Validators.ts';
|
|
19
|
-
|
|
20
|
-
export type FilterFunction = (this: Node, imageData: ImageData) => void;
|
|
21
|
-
export type Filter = FilterFunction | string;
|
|
22
|
-
type Filters = Array<FilterFunction | string>;
|
|
23
|
-
|
|
24
|
-
// CSS filter parser for fallback to function filters
|
|
25
|
-
function parseCSSFilters(cssFilter: string): FilterFunction {
|
|
26
|
-
// Parse common CSS filter functions and map to Konva filters
|
|
27
|
-
const filterRegex = /(\w+)\(([^)]+)\)/g;
|
|
28
|
-
let match;
|
|
29
|
-
|
|
30
|
-
while ((match = filterRegex.exec(cssFilter)) !== null) {
|
|
31
|
-
const [, filterName, filterValue] = match;
|
|
32
|
-
|
|
33
|
-
switch (filterName) {
|
|
34
|
-
case 'blur': {
|
|
35
|
-
const blurRadius = parseFloat(filterValue.replace('px', ''));
|
|
36
|
-
return function (imageData) {
|
|
37
|
-
// CSS blur uses standard deviation, Stack Blur uses radius
|
|
38
|
-
// Empirical testing shows CSS blur needs ~0.5 scaling for visual match
|
|
39
|
-
(this as any).blurRadius(blurRadius * 0.5);
|
|
40
|
-
// Access filters through dynamic import to avoid circular dependency
|
|
41
|
-
const KonvaFilters = (Konva as any).Filters;
|
|
42
|
-
if (KonvaFilters && KonvaFilters.Blur) {
|
|
43
|
-
KonvaFilters.Blur.call(this, imageData);
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
case 'brightness': {
|
|
49
|
-
const brightness = filterValue.includes('%')
|
|
50
|
-
? parseFloat(filterValue) / 100
|
|
51
|
-
: parseFloat(filterValue);
|
|
52
|
-
return function (imageData) {
|
|
53
|
-
(this as any).brightness(brightness); // CSS uses multiplier
|
|
54
|
-
const KonvaFilters = (Konva as any).Filters;
|
|
55
|
-
if (KonvaFilters && KonvaFilters.Brightness) {
|
|
56
|
-
KonvaFilters.Brightness.call(this, imageData);
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
case 'contrast': {
|
|
61
|
-
const contrast = parseFloat(filterValue);
|
|
62
|
-
return function (imageData) {
|
|
63
|
-
// Convert CSS contrast to Konva parameter using square root conversion
|
|
64
|
-
// to account for Konva's quadratic scaling: Math.pow((param + 100) / 100, 2)
|
|
65
|
-
const konvaContrast = 100 * (Math.sqrt(contrast) - 1);
|
|
66
|
-
(this as any).contrast(konvaContrast);
|
|
67
|
-
const KonvaFilters = (Konva as any).Filters;
|
|
68
|
-
if (KonvaFilters && KonvaFilters.Contrast) {
|
|
69
|
-
KonvaFilters.Contrast.call(this, imageData);
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
case 'grayscale': {
|
|
74
|
-
return function (imageData) {
|
|
75
|
-
const KonvaFilters = (Konva as any).Filters;
|
|
76
|
-
if (KonvaFilters && KonvaFilters.Grayscale) {
|
|
77
|
-
KonvaFilters.Grayscale.call(this, imageData);
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
case 'sepia': {
|
|
82
|
-
return function (imageData) {
|
|
83
|
-
const KonvaFilters = (Konva as any).Filters;
|
|
84
|
-
if (KonvaFilters && KonvaFilters.Sepia) {
|
|
85
|
-
KonvaFilters.Sepia.call(this, imageData);
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
case 'invert': {
|
|
90
|
-
return function (imageData) {
|
|
91
|
-
const KonvaFilters = (Konva as any).Filters;
|
|
92
|
-
if (KonvaFilters && KonvaFilters.Invert) {
|
|
93
|
-
KonvaFilters.Invert.call(this, imageData);
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
default:
|
|
98
|
-
Util.warn(
|
|
99
|
-
`CSS filter "${filterName}" is not supported in fallback mode. Consider using function filters for better compatibility.`
|
|
100
|
-
);
|
|
101
|
-
break;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return () => {};
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
type globalCompositeOperationType =
|
|
109
|
-
| ''
|
|
110
|
-
| 'source-over'
|
|
111
|
-
| 'source-in'
|
|
112
|
-
| 'source-out'
|
|
113
|
-
| 'source-atop'
|
|
114
|
-
| 'destination-over'
|
|
115
|
-
| 'destination-in'
|
|
116
|
-
| 'destination-out'
|
|
117
|
-
| 'destination-atop'
|
|
118
|
-
| 'lighter'
|
|
119
|
-
| 'copy'
|
|
120
|
-
| 'xor'
|
|
121
|
-
| 'multiply'
|
|
122
|
-
| 'screen'
|
|
123
|
-
| 'overlay'
|
|
124
|
-
| 'darken'
|
|
125
|
-
| 'lighten'
|
|
126
|
-
| 'color-dodge'
|
|
127
|
-
| 'color-burn'
|
|
128
|
-
| 'hard-light'
|
|
129
|
-
| 'soft-light'
|
|
130
|
-
| 'difference'
|
|
131
|
-
| 'exclusion'
|
|
132
|
-
| 'hue'
|
|
133
|
-
| 'saturation'
|
|
134
|
-
| 'color'
|
|
135
|
-
| 'luminosity';
|
|
136
|
-
|
|
137
|
-
export interface NodeConfig {
|
|
138
|
-
// allow any custom attribute
|
|
139
|
-
[index: string]: any;
|
|
140
|
-
x?: number;
|
|
141
|
-
y?: number;
|
|
142
|
-
width?: number;
|
|
143
|
-
height?: number;
|
|
144
|
-
visible?: boolean;
|
|
145
|
-
listening?: boolean;
|
|
146
|
-
id?: string;
|
|
147
|
-
name?: string;
|
|
148
|
-
opacity?: number;
|
|
149
|
-
scale?: Vector2d;
|
|
150
|
-
scaleX?: number;
|
|
151
|
-
skewX?: number;
|
|
152
|
-
skewY?: number;
|
|
153
|
-
scaleY?: number;
|
|
154
|
-
rotation?: number;
|
|
155
|
-
rotationDeg?: number;
|
|
156
|
-
offset?: Vector2d;
|
|
157
|
-
offsetX?: number;
|
|
158
|
-
offsetY?: number;
|
|
159
|
-
draggable?: boolean;
|
|
160
|
-
dragDistance?: number;
|
|
161
|
-
dragBoundFunc?: (this: Node, pos: Vector2d) => Vector2d;
|
|
162
|
-
preventDefault?: boolean;
|
|
163
|
-
globalCompositeOperation?: globalCompositeOperationType;
|
|
164
|
-
filters?: Filters;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// CONSTANTS
|
|
168
|
-
const ABSOLUTE_OPACITY = 'absoluteOpacity',
|
|
169
|
-
ALL_LISTENERS = 'allEventListeners',
|
|
170
|
-
ABSOLUTE_TRANSFORM = 'absoluteTransform',
|
|
171
|
-
ABSOLUTE_SCALE = 'absoluteScale',
|
|
172
|
-
CANVAS = 'canvas',
|
|
173
|
-
CHANGE = 'Change',
|
|
174
|
-
CHILDREN = 'children',
|
|
175
|
-
KONVA = 'konva',
|
|
176
|
-
LISTENING = 'listening',
|
|
177
|
-
MOUSEENTER = 'mouseenter',
|
|
178
|
-
MOUSELEAVE = 'mouseleave',
|
|
179
|
-
POINTERENTER = 'pointerenter',
|
|
180
|
-
POINTERLEAVE = 'pointerleave',
|
|
181
|
-
TOUCHENTER = 'touchenter',
|
|
182
|
-
TOUCHLEAVE = 'touchleave',
|
|
183
|
-
NAME = 'name',
|
|
184
|
-
SET = 'set',
|
|
185
|
-
SHAPE = 'Shape',
|
|
186
|
-
SPACE = ' ',
|
|
187
|
-
STAGE = 'stage',
|
|
188
|
-
TRANSFORM = 'transform',
|
|
189
|
-
UPPER_STAGE = 'Stage',
|
|
190
|
-
VISIBLE = 'visible',
|
|
191
|
-
TRANSFORM_CHANGE_STR = [
|
|
192
|
-
'xChange.konva',
|
|
193
|
-
'yChange.konva',
|
|
194
|
-
'scaleXChange.konva',
|
|
195
|
-
'scaleYChange.konva',
|
|
196
|
-
'skewXChange.konva',
|
|
197
|
-
'skewYChange.konva',
|
|
198
|
-
'rotationChange.konva',
|
|
199
|
-
'offsetXChange.konva',
|
|
200
|
-
'offsetYChange.konva',
|
|
201
|
-
'transformsEnabledChange.konva',
|
|
202
|
-
].join(SPACE);
|
|
203
|
-
|
|
204
|
-
let idCounter = 1;
|
|
205
|
-
|
|
206
|
-
// create all the events here
|
|
207
|
-
type NodeEventMap = GlobalEventHandlersEventMap & {
|
|
208
|
-
[index: string]: any;
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
export interface KonvaEventObject<EventType, This = Node> {
|
|
212
|
-
type: string;
|
|
213
|
-
target: Shape | Stage;
|
|
214
|
-
evt: EventType;
|
|
215
|
-
pointerId: number;
|
|
216
|
-
currentTarget: This;
|
|
217
|
-
cancelBubble: boolean;
|
|
218
|
-
child?: Node;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
export type KonvaEventListener<This, EventType> = (
|
|
222
|
-
this: This,
|
|
223
|
-
ev: KonvaEventObject<EventType, This>
|
|
224
|
-
) => void;
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Node constructor. Nodes are entities that can be transformed, layered,
|
|
228
|
-
* and have bound events. The stage, layers, groups, and shapes all extend Node.
|
|
229
|
-
* @constructor
|
|
230
|
-
* @memberof Konva
|
|
231
|
-
* @param {Object} config
|
|
232
|
-
* @@nodeParams
|
|
233
|
-
*/
|
|
234
|
-
export abstract class Node<Config extends NodeConfig = NodeConfig> {
|
|
235
|
-
_id = idCounter++;
|
|
236
|
-
eventListeners: {
|
|
237
|
-
[index: string]: Array<{ name: string; handler: Function }>;
|
|
238
|
-
} = {};
|
|
239
|
-
attrs: any = {};
|
|
240
|
-
index = 0;
|
|
241
|
-
_allEventListeners: null | Array<Function> = null;
|
|
242
|
-
parent: Container | null = null;
|
|
243
|
-
_cache: Map<string, any> = new Map<string, any>();
|
|
244
|
-
_attachedDepsListeners: Map<string, boolean> = new Map<string, boolean>();
|
|
245
|
-
_lastPos: Vector2d | null = null;
|
|
246
|
-
_attrsAffectingSize!: string[];
|
|
247
|
-
_batchingTransformChange = false;
|
|
248
|
-
_needClearTransformCache = false;
|
|
249
|
-
|
|
250
|
-
_filterUpToDate = false;
|
|
251
|
-
_isUnderCache = false;
|
|
252
|
-
nodeType!: string;
|
|
253
|
-
className!: string;
|
|
254
|
-
|
|
255
|
-
_dragEventId: number | null = null;
|
|
256
|
-
_shouldFireChangeEvents = false;
|
|
257
|
-
|
|
258
|
-
constructor(config?: Config) {
|
|
259
|
-
// on initial set attrs wi don't need to fire change events
|
|
260
|
-
// because nobody is listening to them yet
|
|
261
|
-
this.setAttrs(config);
|
|
262
|
-
this._shouldFireChangeEvents = true;
|
|
263
|
-
|
|
264
|
-
// all change event listeners are attached to the prototype
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
hasChildren() {
|
|
268
|
-
return false;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
_clearCache(attr?: string) {
|
|
272
|
-
// if we want to clear transform cache
|
|
273
|
-
// we don't really need to remove it from the cache
|
|
274
|
-
// but instead mark as "dirty"
|
|
275
|
-
// so we don't need to create a new instance next time
|
|
276
|
-
if (
|
|
277
|
-
(attr === TRANSFORM || attr === ABSOLUTE_TRANSFORM) &&
|
|
278
|
-
this._cache.get(attr)
|
|
279
|
-
) {
|
|
280
|
-
(this._cache.get(attr) as Transform).dirty = true;
|
|
281
|
-
} else if (attr) {
|
|
282
|
-
this._cache.delete(attr);
|
|
283
|
-
} else {
|
|
284
|
-
this._cache.clear();
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
_getCache(attr: string, privateGetter: Function) {
|
|
288
|
-
let cache = this._cache.get(attr);
|
|
289
|
-
|
|
290
|
-
// for transform the cache can be NOT empty
|
|
291
|
-
// but we still need to recalculate it if it is dirty
|
|
292
|
-
const isTransform = attr === TRANSFORM || attr === ABSOLUTE_TRANSFORM;
|
|
293
|
-
const invalid =
|
|
294
|
-
cache === undefined || (isTransform && cache.dirty === true);
|
|
295
|
-
|
|
296
|
-
// if not cached, we need to set it using the private getter method.
|
|
297
|
-
if (invalid) {
|
|
298
|
-
cache = privateGetter.call(this);
|
|
299
|
-
this._cache.set(attr, cache);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
return cache;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
_calculate(name: string, deps: Array<string>, getter: Function) {
|
|
306
|
-
// if we are trying to calculate function for the first time
|
|
307
|
-
// we need to attach listeners for change events
|
|
308
|
-
if (!this._attachedDepsListeners.get(name)) {
|
|
309
|
-
const depsString = deps.map((dep) => dep + 'Change.konva').join(SPACE);
|
|
310
|
-
this.on(depsString, () => {
|
|
311
|
-
this._clearCache(name);
|
|
312
|
-
});
|
|
313
|
-
this._attachedDepsListeners.set(name, true);
|
|
314
|
-
}
|
|
315
|
-
// just use cache function
|
|
316
|
-
return this._getCache(name, getter);
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
_getCanvasCache() {
|
|
320
|
-
return this._cache.get(CANVAS);
|
|
321
|
-
}
|
|
322
|
-
/*
|
|
323
|
-
* when the logic for a cached result depends on ancestor propagation, use this
|
|
324
|
-
* method to clear self and children cache
|
|
325
|
-
*/
|
|
326
|
-
_clearSelfAndDescendantCache(attr?: string) {
|
|
327
|
-
this._clearCache(attr);
|
|
328
|
-
// trigger clear cache, so transformer can use it
|
|
329
|
-
if (attr === ABSOLUTE_TRANSFORM) {
|
|
330
|
-
this.fire('absoluteTransformChange');
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
/**
|
|
334
|
-
* clear cached canvas
|
|
335
|
-
* @method
|
|
336
|
-
* @name Konva.Node#clearCache
|
|
337
|
-
* @returns {Konva.Node}
|
|
338
|
-
* @example
|
|
339
|
-
* node.clearCache();
|
|
340
|
-
*/
|
|
341
|
-
clearCache() {
|
|
342
|
-
if (this._cache.has(CANVAS)) {
|
|
343
|
-
const { scene, filter, hit, buffer } = this._cache.get(CANVAS);
|
|
344
|
-
Util.releaseCanvas(scene, filter, hit, buffer);
|
|
345
|
-
this._cache.delete(CANVAS);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
this._clearSelfAndDescendantCache();
|
|
349
|
-
this._requestDraw();
|
|
350
|
-
return this;
|
|
351
|
-
}
|
|
352
|
-
/**
|
|
353
|
-
* cache node to improve drawing performance, apply filters, or create more accurate
|
|
354
|
-
* hit regions. For all basic shapes size of cache canvas will be automatically detected.
|
|
355
|
-
* If you need to cache your custom `Konva.Shape` instance you have to pass shape's bounding box
|
|
356
|
-
* properties. Look at [https://konvajs.org/docs/performance/Shape_Caching.html](https://konvajs.org/docs/performance/Shape_Caching.html) for more information.
|
|
357
|
-
* @method
|
|
358
|
-
* @name Konva.Node#cache
|
|
359
|
-
* @param {Object} [config]
|
|
360
|
-
* @param {Number} [config.x]
|
|
361
|
-
* @param {Number} [config.y]
|
|
362
|
-
* @param {Number} [config.width]
|
|
363
|
-
* @param {Number} [config.height]
|
|
364
|
-
* @param {Number} [config.offset] increase canvas size by `offset` pixel in all directions.
|
|
365
|
-
* @param {Boolean} [config.drawBorder] when set to true, a red border will be drawn around the cached
|
|
366
|
-
* region for debugging purposes
|
|
367
|
-
* @param {Number} [config.pixelRatio] change quality (or pixel ratio) of cached image. pixelRatio = 2 will produce 2x sized cache.
|
|
368
|
-
* @param {Boolean} [config.imageSmoothingEnabled] control imageSmoothingEnabled property of created canvas for cache
|
|
369
|
-
* @param {Number} [config.hitCanvasPixelRatio] change quality (or pixel ratio) of cached hit canvas.
|
|
370
|
-
* @returns {Konva.Node}
|
|
371
|
-
* @example
|
|
372
|
-
* // cache a shape with the x,y position of the bounding box at the center and
|
|
373
|
-
* // the width and height of the bounding box equal to the width and height of
|
|
374
|
-
* // the shape obtained from shape.width() and shape.height()
|
|
375
|
-
* image.cache();
|
|
376
|
-
*
|
|
377
|
-
* // cache a node and define the bounding box position and size
|
|
378
|
-
* node.cache({
|
|
379
|
-
* x: -30,
|
|
380
|
-
* y: -30,
|
|
381
|
-
* width: 100,
|
|
382
|
-
* height: 200
|
|
383
|
-
* });
|
|
384
|
-
*
|
|
385
|
-
* // cache a node and draw a red border around the bounding box
|
|
386
|
-
* // for debugging purposes
|
|
387
|
-
* node.cache({
|
|
388
|
-
* x: -30,
|
|
389
|
-
* y: -30,
|
|
390
|
-
* width: 100,
|
|
391
|
-
* height: 200,
|
|
392
|
-
* offset : 10,
|
|
393
|
-
* drawBorder: true
|
|
394
|
-
* });
|
|
395
|
-
*/
|
|
396
|
-
cache(config?: {
|
|
397
|
-
x?: number;
|
|
398
|
-
y?: number;
|
|
399
|
-
width?: number;
|
|
400
|
-
height?: number;
|
|
401
|
-
drawBorder?: boolean;
|
|
402
|
-
offset?: number;
|
|
403
|
-
pixelRatio?: number;
|
|
404
|
-
imageSmoothingEnabled?: boolean;
|
|
405
|
-
hitCanvasPixelRatio?: number;
|
|
406
|
-
}) {
|
|
407
|
-
const conf = config || {};
|
|
408
|
-
let rect = {} as IRect;
|
|
409
|
-
|
|
410
|
-
// don't call getClientRect if we have all attributes
|
|
411
|
-
// it means call it only if have one undefined
|
|
412
|
-
if (
|
|
413
|
-
conf.x === undefined ||
|
|
414
|
-
conf.y === undefined ||
|
|
415
|
-
conf.width === undefined ||
|
|
416
|
-
conf.height === undefined
|
|
417
|
-
) {
|
|
418
|
-
rect = this.getClientRect({
|
|
419
|
-
skipTransform: true,
|
|
420
|
-
relativeTo: this.getParent() || undefined,
|
|
421
|
-
});
|
|
422
|
-
}
|
|
423
|
-
let width = Math.ceil(conf.width || rect.width),
|
|
424
|
-
height = Math.ceil(conf.height || rect.height),
|
|
425
|
-
pixelRatio = conf.pixelRatio,
|
|
426
|
-
x = conf.x === undefined ? Math.floor(rect.x) : conf.x,
|
|
427
|
-
y = conf.y === undefined ? Math.floor(rect.y) : conf.y,
|
|
428
|
-
offset = conf.offset || 0,
|
|
429
|
-
drawBorder = conf.drawBorder || false,
|
|
430
|
-
hitCanvasPixelRatio = conf.hitCanvasPixelRatio || 1;
|
|
431
|
-
|
|
432
|
-
if (!width || !height) {
|
|
433
|
-
Util.error(
|
|
434
|
-
'Can not cache the node. Width or height of the node equals 0. Caching is skipped.'
|
|
435
|
-
);
|
|
436
|
-
return;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
// because using Math.floor on x, y position may shift drawing
|
|
440
|
-
// to avoid shift we need to increase size
|
|
441
|
-
// but we better to avoid it, for better filters flows
|
|
442
|
-
const extraPaddingX = Math.abs(Math.round(rect.x) - x) > 0.5 ? 1 : 0;
|
|
443
|
-
const extraPaddingY = Math.abs(Math.round(rect.y) - y) > 0.5 ? 1 : 0;
|
|
444
|
-
width += offset * 2 + extraPaddingX;
|
|
445
|
-
height += offset * 2 + extraPaddingY;
|
|
446
|
-
|
|
447
|
-
x -= offset;
|
|
448
|
-
y -= offset;
|
|
449
|
-
|
|
450
|
-
// if (Math.floor(x) < x) {
|
|
451
|
-
// x = Math.floor(x);
|
|
452
|
-
// // width += 1;
|
|
453
|
-
// }
|
|
454
|
-
// if (Math.floor(y) < y) {
|
|
455
|
-
// y = Math.floor(y);
|
|
456
|
-
// // height += 1;
|
|
457
|
-
// }
|
|
458
|
-
|
|
459
|
-
// console.log({ x, y, width, height }, rect);
|
|
460
|
-
|
|
461
|
-
const cachedSceneCanvas = new SceneCanvas({
|
|
462
|
-
pixelRatio: pixelRatio,
|
|
463
|
-
width: width,
|
|
464
|
-
height: height,
|
|
465
|
-
}),
|
|
466
|
-
cachedFilterCanvas = new SceneCanvas({
|
|
467
|
-
pixelRatio: pixelRatio,
|
|
468
|
-
width: 0,
|
|
469
|
-
height: 0,
|
|
470
|
-
willReadFrequently: true,
|
|
471
|
-
}),
|
|
472
|
-
cachedHitCanvas = new HitCanvas({
|
|
473
|
-
pixelRatio: hitCanvasPixelRatio,
|
|
474
|
-
width: width,
|
|
475
|
-
height: height,
|
|
476
|
-
}),
|
|
477
|
-
sceneContext = cachedSceneCanvas.getContext(),
|
|
478
|
-
hitContext = cachedHitCanvas.getContext();
|
|
479
|
-
|
|
480
|
-
const bufferCanvas = new SceneCanvas({
|
|
481
|
-
// width and height already multiplied by pixelRatio
|
|
482
|
-
// so we need to revert that
|
|
483
|
-
// also increase size by x nd y offset to make sure content fits canvas
|
|
484
|
-
width:
|
|
485
|
-
cachedSceneCanvas.width / cachedSceneCanvas.pixelRatio + Math.abs(x),
|
|
486
|
-
height:
|
|
487
|
-
cachedSceneCanvas.height / cachedSceneCanvas.pixelRatio + Math.abs(y),
|
|
488
|
-
pixelRatio: cachedSceneCanvas.pixelRatio,
|
|
489
|
-
}),
|
|
490
|
-
bufferContext = bufferCanvas.getContext();
|
|
491
|
-
|
|
492
|
-
cachedHitCanvas.isCache = true;
|
|
493
|
-
cachedSceneCanvas.isCache = true;
|
|
494
|
-
|
|
495
|
-
this._cache.delete(CANVAS);
|
|
496
|
-
this._filterUpToDate = false;
|
|
497
|
-
|
|
498
|
-
if (conf.imageSmoothingEnabled === false) {
|
|
499
|
-
cachedSceneCanvas.getContext()._context.imageSmoothingEnabled = false;
|
|
500
|
-
cachedFilterCanvas.getContext()._context.imageSmoothingEnabled = false;
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
sceneContext.save();
|
|
504
|
-
hitContext.save();
|
|
505
|
-
bufferContext.save();
|
|
506
|
-
|
|
507
|
-
sceneContext.translate(-x, -y);
|
|
508
|
-
hitContext.translate(-x, -y);
|
|
509
|
-
bufferContext.translate(-x, -y);
|
|
510
|
-
// hard-code offset to make sure content fits canvas
|
|
511
|
-
// @ts-ignore
|
|
512
|
-
bufferCanvas.x = x;
|
|
513
|
-
// @ts-ignore
|
|
514
|
-
bufferCanvas.y = y;
|
|
515
|
-
|
|
516
|
-
// extra flag to skip on getAbsolute opacity calc
|
|
517
|
-
this._isUnderCache = true;
|
|
518
|
-
this._clearSelfAndDescendantCache(ABSOLUTE_OPACITY);
|
|
519
|
-
this._clearSelfAndDescendantCache(ABSOLUTE_SCALE);
|
|
520
|
-
|
|
521
|
-
this.drawScene(cachedSceneCanvas, this, bufferCanvas);
|
|
522
|
-
this.drawHit(cachedHitCanvas, this);
|
|
523
|
-
this._isUnderCache = false;
|
|
524
|
-
|
|
525
|
-
sceneContext.restore();
|
|
526
|
-
hitContext.restore();
|
|
527
|
-
|
|
528
|
-
// this will draw a red border around the cached box for
|
|
529
|
-
// debugging purposes
|
|
530
|
-
if (drawBorder) {
|
|
531
|
-
sceneContext.save();
|
|
532
|
-
sceneContext.beginPath();
|
|
533
|
-
sceneContext.rect(0, 0, width, height);
|
|
534
|
-
sceneContext.closePath();
|
|
535
|
-
sceneContext.setAttr('strokeStyle', 'red');
|
|
536
|
-
sceneContext.setAttr('lineWidth', 5);
|
|
537
|
-
sceneContext.stroke();
|
|
538
|
-
sceneContext.restore();
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
this._cache.set(CANVAS, {
|
|
542
|
-
scene: cachedSceneCanvas,
|
|
543
|
-
filter: cachedFilterCanvas,
|
|
544
|
-
hit: cachedHitCanvas,
|
|
545
|
-
buffer: bufferCanvas,
|
|
546
|
-
x: x,
|
|
547
|
-
y: y,
|
|
548
|
-
});
|
|
549
|
-
|
|
550
|
-
this._requestDraw();
|
|
551
|
-
|
|
552
|
-
return this;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
/**
|
|
556
|
-
* determine if node is currently cached
|
|
557
|
-
* @method
|
|
558
|
-
* @name Konva.Node#isCached
|
|
559
|
-
* @returns {Boolean}
|
|
560
|
-
*/
|
|
561
|
-
isCached() {
|
|
562
|
-
return this._cache.has(CANVAS);
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
abstract drawScene(canvas?: Canvas, top?: Node, bufferCanvas?: Canvas): void;
|
|
566
|
-
abstract drawHit(canvas?: Canvas, top?: Node): void;
|
|
567
|
-
/**
|
|
568
|
-
* Return client rectangle {x, y, width, height} of node. This rectangle also include all styling (strokes, shadows, etc).
|
|
569
|
-
* The purpose of the method is similar to getBoundingClientRect API of the DOM.
|
|
570
|
-
* @method
|
|
571
|
-
* @name Konva.Node#getClientRect
|
|
572
|
-
* @param {Object} config
|
|
573
|
-
* @param {Boolean} [config.skipTransform] should we apply transform to node for calculating rect?
|
|
574
|
-
* @param {Boolean} [config.skipShadow] should we apply shadow to the node for calculating bound box?
|
|
575
|
-
* @param {Boolean} [config.skipStroke] should we apply stroke to the node for calculating bound box?
|
|
576
|
-
* @param {Object} [config.relativeTo] calculate client rect relative to one of the parents
|
|
577
|
-
* @returns {Object} rect with {x, y, width, height} properties
|
|
578
|
-
* @example
|
|
579
|
-
* var rect = new Konva.Rect({
|
|
580
|
-
* width : 100,
|
|
581
|
-
* height : 100,
|
|
582
|
-
* x : 50,
|
|
583
|
-
* y : 50,
|
|
584
|
-
* strokeWidth : 4,
|
|
585
|
-
* stroke : 'black',
|
|
586
|
-
* offsetX : 50,
|
|
587
|
-
* scaleY : 2
|
|
588
|
-
* });
|
|
589
|
-
*
|
|
590
|
-
* // get client rect without think off transformations (position, rotation, scale, offset, etc)
|
|
591
|
-
* rect.getClientRect({ skipTransform: true});
|
|
592
|
-
* // returns {
|
|
593
|
-
* // x : -2, // two pixels for stroke / 2
|
|
594
|
-
* // y : -2,
|
|
595
|
-
* // width : 104, // increased by 4 for stroke
|
|
596
|
-
* // height : 104
|
|
597
|
-
* //}
|
|
598
|
-
*
|
|
599
|
-
* // get client rect with transformation applied
|
|
600
|
-
* rect.getClientRect();
|
|
601
|
-
* // returns Object {x: -2, y: 46, width: 104, height: 208}
|
|
602
|
-
*/
|
|
603
|
-
getClientRect(config?: {
|
|
604
|
-
skipTransform?: boolean;
|
|
605
|
-
skipShadow?: boolean;
|
|
606
|
-
skipStroke?: boolean;
|
|
607
|
-
relativeTo?: Container;
|
|
608
|
-
}): { x: number; y: number; width: number; height: number } {
|
|
609
|
-
// abstract method
|
|
610
|
-
// redefine in Container and Shape
|
|
611
|
-
throw new Error('abstract "getClientRect" method call');
|
|
612
|
-
}
|
|
613
|
-
_transformedRect(rect: IRect, top?: Node | null) {
|
|
614
|
-
const points = [
|
|
615
|
-
{ x: rect.x, y: rect.y },
|
|
616
|
-
{ x: rect.x + rect.width, y: rect.y },
|
|
617
|
-
{ x: rect.x + rect.width, y: rect.y + rect.height },
|
|
618
|
-
{ x: rect.x, y: rect.y + rect.height },
|
|
619
|
-
];
|
|
620
|
-
let minX: number = Infinity,
|
|
621
|
-
minY: number = Infinity,
|
|
622
|
-
maxX: number = -Infinity,
|
|
623
|
-
maxY: number = -Infinity;
|
|
624
|
-
const trans = this.getAbsoluteTransform(top);
|
|
625
|
-
points.forEach(function (point) {
|
|
626
|
-
const transformed = trans.point(point);
|
|
627
|
-
if (minX === undefined) {
|
|
628
|
-
minX = maxX = transformed.x;
|
|
629
|
-
minY = maxY = transformed.y;
|
|
630
|
-
}
|
|
631
|
-
minX = Math.min(minX, transformed.x);
|
|
632
|
-
minY = Math.min(minY, transformed.y);
|
|
633
|
-
maxX = Math.max(maxX, transformed.x);
|
|
634
|
-
maxY = Math.max(maxY, transformed.y);
|
|
635
|
-
});
|
|
636
|
-
return {
|
|
637
|
-
x: minX,
|
|
638
|
-
y: minY,
|
|
639
|
-
width: maxX - minX,
|
|
640
|
-
height: maxY - minY,
|
|
641
|
-
};
|
|
642
|
-
}
|
|
643
|
-
_drawCachedSceneCanvas(context: Context) {
|
|
644
|
-
context.save();
|
|
645
|
-
context._applyOpacity(this);
|
|
646
|
-
context._applyGlobalCompositeOperation(this);
|
|
647
|
-
|
|
648
|
-
const canvasCache = this._getCanvasCache();
|
|
649
|
-
context.translate(canvasCache.x, canvasCache.y);
|
|
650
|
-
|
|
651
|
-
const cacheCanvas = this._getCachedSceneCanvas();
|
|
652
|
-
const ratio = cacheCanvas.pixelRatio;
|
|
653
|
-
|
|
654
|
-
context.drawImage(
|
|
655
|
-
cacheCanvas._canvas,
|
|
656
|
-
0,
|
|
657
|
-
0,
|
|
658
|
-
cacheCanvas.width / ratio,
|
|
659
|
-
cacheCanvas.height / ratio
|
|
660
|
-
);
|
|
661
|
-
context.restore();
|
|
662
|
-
}
|
|
663
|
-
_drawCachedHitCanvas(context: Context) {
|
|
664
|
-
const canvasCache = this._getCanvasCache(),
|
|
665
|
-
hitCanvas = canvasCache.hit;
|
|
666
|
-
context.save();
|
|
667
|
-
context.translate(canvasCache.x, canvasCache.y);
|
|
668
|
-
context.drawImage(
|
|
669
|
-
hitCanvas._canvas,
|
|
670
|
-
0,
|
|
671
|
-
0,
|
|
672
|
-
hitCanvas.width / hitCanvas.pixelRatio,
|
|
673
|
-
hitCanvas.height / hitCanvas.pixelRatio
|
|
674
|
-
);
|
|
675
|
-
context.restore();
|
|
676
|
-
}
|
|
677
|
-
_getCachedSceneCanvas() {
|
|
678
|
-
let filters = this.filters(),
|
|
679
|
-
cachedCanvas = this._getCanvasCache(),
|
|
680
|
-
sceneCanvas = cachedCanvas.scene as Canvas,
|
|
681
|
-
filterCanvas = cachedCanvas.filter as Canvas,
|
|
682
|
-
filterContext = filterCanvas.getContext(),
|
|
683
|
-
len,
|
|
684
|
-
imageData,
|
|
685
|
-
n,
|
|
686
|
-
filter;
|
|
687
|
-
|
|
688
|
-
if (!filters || filters.length === 0) {
|
|
689
|
-
return sceneCanvas;
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
if (this._filterUpToDate) {
|
|
693
|
-
return filterCanvas;
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
let useNativeOnly = true;
|
|
697
|
-
for (let i = 0; i < filters.length; i++) {
|
|
698
|
-
const fallbackRequired =
|
|
699
|
-
typeof filters[i] === 'string' && !isCSSFiltersSupported();
|
|
700
|
-
if (fallbackRequired) {
|
|
701
|
-
// Util.warn(
|
|
702
|
-
// `CSS filter "${filters[i]}" is not supported in native mode.`
|
|
703
|
-
// );
|
|
704
|
-
}
|
|
705
|
-
if (typeof filters[i] !== 'string' || !isCSSFiltersSupported()) {
|
|
706
|
-
useNativeOnly = false;
|
|
707
|
-
break;
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
const ratio = sceneCanvas.pixelRatio;
|
|
712
|
-
filterCanvas.setSize(
|
|
713
|
-
sceneCanvas.width / sceneCanvas.pixelRatio,
|
|
714
|
-
sceneCanvas.height / sceneCanvas.pixelRatio
|
|
715
|
-
);
|
|
716
|
-
if (useNativeOnly) {
|
|
717
|
-
const finalFilter = (filters as unknown as string[]).join(' ');
|
|
718
|
-
filterContext.save();
|
|
719
|
-
filterContext.setAttr('filter', finalFilter);
|
|
720
|
-
filterContext.drawImage(
|
|
721
|
-
sceneCanvas._canvas,
|
|
722
|
-
0,
|
|
723
|
-
0,
|
|
724
|
-
sceneCanvas.getWidth() / ratio,
|
|
725
|
-
sceneCanvas.getHeight() / ratio
|
|
726
|
-
);
|
|
727
|
-
filterContext.restore();
|
|
728
|
-
this._filterUpToDate = true;
|
|
729
|
-
return filterCanvas;
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
try {
|
|
733
|
-
len = filters.length;
|
|
734
|
-
filterContext.clear();
|
|
735
|
-
|
|
736
|
-
// copy cached canvas onto filter context
|
|
737
|
-
filterContext.drawImage(
|
|
738
|
-
sceneCanvas._canvas,
|
|
739
|
-
0,
|
|
740
|
-
0,
|
|
741
|
-
sceneCanvas.getWidth() / ratio,
|
|
742
|
-
sceneCanvas.getHeight() / ratio
|
|
743
|
-
);
|
|
744
|
-
imageData = filterContext.getImageData(
|
|
745
|
-
0,
|
|
746
|
-
0,
|
|
747
|
-
filterCanvas.getWidth(),
|
|
748
|
-
filterCanvas.getHeight()
|
|
749
|
-
);
|
|
750
|
-
|
|
751
|
-
// apply filters to filter context
|
|
752
|
-
for (n = 0; n < len; n++) {
|
|
753
|
-
filter = filters[n];
|
|
754
|
-
if (typeof filter === 'string') {
|
|
755
|
-
filter = parseCSSFilters(filter);
|
|
756
|
-
}
|
|
757
|
-
filter.call(this, imageData);
|
|
758
|
-
filterContext.putImageData(imageData, 0, 0);
|
|
759
|
-
}
|
|
760
|
-
} catch (e: any) {
|
|
761
|
-
Util.error(
|
|
762
|
-
'Unable to apply filter. ' +
|
|
763
|
-
e.message +
|
|
764
|
-
' This post my help you https://konvajs.org/docs/posts/Tainted_Canvas.html.'
|
|
765
|
-
);
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
this._filterUpToDate = true;
|
|
769
|
-
|
|
770
|
-
return filterCanvas;
|
|
771
|
-
}
|
|
772
|
-
/**
|
|
773
|
-
* bind events to the node. KonvaJS supports mouseover, mousemove,
|
|
774
|
-
* mouseout, mouseenter, mouseleave, mousedown, mouseup, wheel, contextmenu, click, dblclick, touchstart, touchmove,
|
|
775
|
-
* touchend, tap, dbltap, dragstart, dragmove, and dragend events.
|
|
776
|
-
* Pass in a string of events delimited by a space to bind multiple events at once
|
|
777
|
-
* such as 'mousedown mouseup mousemove'. Include a namespace to bind an
|
|
778
|
-
* event by name such as 'click.foobar'.
|
|
779
|
-
* @method
|
|
780
|
-
* @name Konva.Node#on
|
|
781
|
-
* @param {String} evtStr e.g. 'click', 'mousedown touchstart', 'mousedown.foo touchstart.foo'
|
|
782
|
-
* @param {Function} handler The handler function. The first argument of that function is event object. Event object has `target` as main target of the event, `currentTarget` as current node listener and `evt` as native browser event.
|
|
783
|
-
* @returns {Konva.Node}
|
|
784
|
-
* @example
|
|
785
|
-
* // add click listener
|
|
786
|
-
* node.on('click', function() {
|
|
787
|
-
* console.log('you clicked me!');
|
|
788
|
-
* });
|
|
789
|
-
*
|
|
790
|
-
* // get the target node
|
|
791
|
-
* node.on('click', function(evt) {
|
|
792
|
-
* console.log(evt.target);
|
|
793
|
-
* });
|
|
794
|
-
*
|
|
795
|
-
* // stop event propagation
|
|
796
|
-
* node.on('click', function(evt) {
|
|
797
|
-
* evt.cancelBubble = true;
|
|
798
|
-
* });
|
|
799
|
-
*
|
|
800
|
-
* // bind multiple listeners
|
|
801
|
-
* node.on('click touchstart', function() {
|
|
802
|
-
* console.log('you clicked/touched me!');
|
|
803
|
-
* });
|
|
804
|
-
*
|
|
805
|
-
* // namespace listener
|
|
806
|
-
* node.on('click.foo', function() {
|
|
807
|
-
* console.log('you clicked/touched me!');
|
|
808
|
-
* });
|
|
809
|
-
*
|
|
810
|
-
* // get the event type
|
|
811
|
-
* node.on('click tap', function(evt) {
|
|
812
|
-
* var eventType = evt.type;
|
|
813
|
-
* });
|
|
814
|
-
*
|
|
815
|
-
* // get native event object
|
|
816
|
-
* node.on('click tap', function(evt) {
|
|
817
|
-
* var nativeEvent = evt.evt;
|
|
818
|
-
* });
|
|
819
|
-
*
|
|
820
|
-
* // for change events, get the old and new val
|
|
821
|
-
* node.on('xChange', function(evt) {
|
|
822
|
-
* var oldVal = evt.oldVal;
|
|
823
|
-
* var newVal = evt.newVal;
|
|
824
|
-
* });
|
|
825
|
-
*
|
|
826
|
-
* // get event targets
|
|
827
|
-
* // with event delegations
|
|
828
|
-
* layer.on('click', 'Group', function(evt) {
|
|
829
|
-
* var shape = evt.target;
|
|
830
|
-
* var group = evt.currentTarget;
|
|
831
|
-
* });
|
|
832
|
-
*/
|
|
833
|
-
on<K extends keyof NodeEventMap>(
|
|
834
|
-
evtStr: K,
|
|
835
|
-
handler: KonvaEventListener<this, NodeEventMap[K]>
|
|
836
|
-
) {
|
|
837
|
-
if (this._cache) {
|
|
838
|
-
this._cache.delete(ALL_LISTENERS);
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
if (arguments.length === 3) {
|
|
842
|
-
return this._delegate.apply(this, arguments as any);
|
|
843
|
-
}
|
|
844
|
-
const events = (evtStr as string).split(SPACE);
|
|
845
|
-
|
|
846
|
-
/*
|
|
847
|
-
* loop through types and attach event listeners to
|
|
848
|
-
* each one. eg. 'click mouseover.namespace mouseout'
|
|
849
|
-
* will create three event bindings
|
|
850
|
-
*/
|
|
851
|
-
for (let n = 0; n < events.length; n++) {
|
|
852
|
-
const event = events[n];
|
|
853
|
-
const parts = event.split('.');
|
|
854
|
-
const baseEvent = parts[0];
|
|
855
|
-
const name = parts[1] || '';
|
|
856
|
-
|
|
857
|
-
// create events array if it doesn't exist
|
|
858
|
-
if (!this.eventListeners[baseEvent]) {
|
|
859
|
-
this.eventListeners[baseEvent] = [];
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
this.eventListeners[baseEvent].push({ name, handler });
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
return this;
|
|
866
|
-
}
|
|
867
|
-
/**
|
|
868
|
-
* remove event bindings from the node. Pass in a string of
|
|
869
|
-
* event types delimmited by a space to remove multiple event
|
|
870
|
-
* bindings at once such as 'mousedown mouseup mousemove'.
|
|
871
|
-
* include a namespace to remove an event binding by name
|
|
872
|
-
* such as 'click.foobar'. If you only give a name like '.foobar',
|
|
873
|
-
* all events in that namespace will be removed.
|
|
874
|
-
* @method
|
|
875
|
-
* @name Konva.Node#off
|
|
876
|
-
* @param {String} evtStr e.g. 'click', 'mousedown touchstart', '.foobar'
|
|
877
|
-
* @returns {Konva.Node}
|
|
878
|
-
* @example
|
|
879
|
-
* // remove listener
|
|
880
|
-
* node.off('click');
|
|
881
|
-
*
|
|
882
|
-
* // remove multiple listeners
|
|
883
|
-
* node.off('click touchstart');
|
|
884
|
-
*
|
|
885
|
-
* // remove listener by name
|
|
886
|
-
* node.off('click.foo');
|
|
887
|
-
*/
|
|
888
|
-
off(evtStr?: string, callback?: Function) {
|
|
889
|
-
let events = (evtStr || '').split(SPACE),
|
|
890
|
-
len = events.length,
|
|
891
|
-
n,
|
|
892
|
-
t,
|
|
893
|
-
event,
|
|
894
|
-
parts,
|
|
895
|
-
baseEvent,
|
|
896
|
-
name;
|
|
897
|
-
|
|
898
|
-
this._cache && this._cache.delete(ALL_LISTENERS);
|
|
899
|
-
|
|
900
|
-
if (!evtStr) {
|
|
901
|
-
// remove all events
|
|
902
|
-
for (t in this.eventListeners) {
|
|
903
|
-
this._off(t);
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
for (n = 0; n < len; n++) {
|
|
907
|
-
event = events[n];
|
|
908
|
-
parts = event.split('.');
|
|
909
|
-
baseEvent = parts[0];
|
|
910
|
-
name = parts[1];
|
|
911
|
-
|
|
912
|
-
if (baseEvent) {
|
|
913
|
-
if (this.eventListeners[baseEvent]) {
|
|
914
|
-
this._off(baseEvent, name, callback);
|
|
915
|
-
}
|
|
916
|
-
} else {
|
|
917
|
-
for (t in this.eventListeners) {
|
|
918
|
-
this._off(t, name, callback);
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
return this;
|
|
923
|
-
}
|
|
924
|
-
// some event aliases for third party integration like HammerJS
|
|
925
|
-
dispatchEvent(evt: any) {
|
|
926
|
-
const e = {
|
|
927
|
-
target: this,
|
|
928
|
-
type: evt.type,
|
|
929
|
-
evt: evt,
|
|
930
|
-
};
|
|
931
|
-
this.fire(evt.type, e);
|
|
932
|
-
return this;
|
|
933
|
-
}
|
|
934
|
-
addEventListener(type: string, handler: (e: Event) => void) {
|
|
935
|
-
// we have to pass native event to handler
|
|
936
|
-
this.on(type, function (evt) {
|
|
937
|
-
handler.call(this, evt.evt);
|
|
938
|
-
});
|
|
939
|
-
return this;
|
|
940
|
-
}
|
|
941
|
-
removeEventListener(type: string) {
|
|
942
|
-
this.off(type);
|
|
943
|
-
return this;
|
|
944
|
-
}
|
|
945
|
-
// like node.on
|
|
946
|
-
_delegate(event: string, selector: string, handler: (e: Event) => void) {
|
|
947
|
-
const stopNode = this;
|
|
948
|
-
this.on(event, function (evt) {
|
|
949
|
-
const targets = evt.target.findAncestors(selector, true, stopNode);
|
|
950
|
-
for (let i = 0; i < targets.length; i++) {
|
|
951
|
-
evt = Util.cloneObject(evt);
|
|
952
|
-
evt.currentTarget = targets[i] as any;
|
|
953
|
-
handler.call(targets[i], evt as any);
|
|
954
|
-
}
|
|
955
|
-
});
|
|
956
|
-
}
|
|
957
|
-
/**
|
|
958
|
-
* remove a node from parent, but don't destroy. You can reuse the node later.
|
|
959
|
-
* @method
|
|
960
|
-
* @name Konva.Node#remove
|
|
961
|
-
* @returns {Konva.Node}
|
|
962
|
-
* @example
|
|
963
|
-
* node.remove();
|
|
964
|
-
*/
|
|
965
|
-
remove() {
|
|
966
|
-
if (this.isDragging()) {
|
|
967
|
-
this.stopDrag();
|
|
968
|
-
}
|
|
969
|
-
// we can have drag element but that is not dragged yet
|
|
970
|
-
// so just clear it
|
|
971
|
-
DD._dragElements.delete(this._id);
|
|
972
|
-
this._remove();
|
|
973
|
-
return this;
|
|
974
|
-
}
|
|
975
|
-
_clearCaches() {
|
|
976
|
-
this._clearSelfAndDescendantCache(ABSOLUTE_TRANSFORM);
|
|
977
|
-
this._clearSelfAndDescendantCache(ABSOLUTE_OPACITY);
|
|
978
|
-
this._clearSelfAndDescendantCache(ABSOLUTE_SCALE);
|
|
979
|
-
this._clearSelfAndDescendantCache(STAGE);
|
|
980
|
-
this._clearSelfAndDescendantCache(VISIBLE);
|
|
981
|
-
this._clearSelfAndDescendantCache(LISTENING);
|
|
982
|
-
}
|
|
983
|
-
_remove() {
|
|
984
|
-
// every cached attr that is calculated via node tree
|
|
985
|
-
// traversal must be cleared when removing a node
|
|
986
|
-
this._clearCaches();
|
|
987
|
-
|
|
988
|
-
const parent = this.getParent();
|
|
989
|
-
|
|
990
|
-
if (parent && parent.children) {
|
|
991
|
-
parent.children.splice(this.index, 1);
|
|
992
|
-
parent._setChildrenIndices();
|
|
993
|
-
this.parent = null;
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
/**
|
|
997
|
-
* remove and destroy a node. Kill it and delete forever! You should not reuse node after destroy().
|
|
998
|
-
* If the node is a container (Group, Stage or Layer) it will destroy all children too.
|
|
999
|
-
* @method
|
|
1000
|
-
* @name Konva.Node#destroy
|
|
1001
|
-
* @example
|
|
1002
|
-
* node.destroy();
|
|
1003
|
-
*/
|
|
1004
|
-
destroy() {
|
|
1005
|
-
this.remove();
|
|
1006
|
-
this.clearCache();
|
|
1007
|
-
return this;
|
|
1008
|
-
}
|
|
1009
|
-
/**
|
|
1010
|
-
* get attr
|
|
1011
|
-
* @method
|
|
1012
|
-
* @name Konva.Node#getAttr
|
|
1013
|
-
* @param {String} attr
|
|
1014
|
-
* @returns {Integer|String|Object|Array}
|
|
1015
|
-
* @example
|
|
1016
|
-
* var x = node.getAttr('x');
|
|
1017
|
-
*/
|
|
1018
|
-
getAttr<T>(attr: string) {
|
|
1019
|
-
const method = 'get' + Util._capitalize(attr);
|
|
1020
|
-
if (Util._isFunction((this as any)[method])) {
|
|
1021
|
-
return (this as any)[method]();
|
|
1022
|
-
}
|
|
1023
|
-
// otherwise get directly
|
|
1024
|
-
return this.attrs[attr] as T | undefined;
|
|
1025
|
-
}
|
|
1026
|
-
/**
|
|
1027
|
-
* get ancestors
|
|
1028
|
-
* @method
|
|
1029
|
-
* @name Konva.Node#getAncestors
|
|
1030
|
-
* @returns {Array}
|
|
1031
|
-
* @example
|
|
1032
|
-
* shape.getAncestors().forEach(function(node) {
|
|
1033
|
-
* console.log(node.id());
|
|
1034
|
-
* })
|
|
1035
|
-
*/
|
|
1036
|
-
getAncestors() {
|
|
1037
|
-
let parent = this.getParent(),
|
|
1038
|
-
ancestors: Array<Node> = [];
|
|
1039
|
-
|
|
1040
|
-
while (parent) {
|
|
1041
|
-
ancestors.push(parent);
|
|
1042
|
-
parent = parent.getParent();
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
return ancestors;
|
|
1046
|
-
}
|
|
1047
|
-
/**
|
|
1048
|
-
* get attrs object literal
|
|
1049
|
-
* @method
|
|
1050
|
-
* @name Konva.Node#getAttrs
|
|
1051
|
-
* @returns {Object}
|
|
1052
|
-
*/
|
|
1053
|
-
getAttrs() {
|
|
1054
|
-
return (this.attrs || {}) as Config & Record<string, any>;
|
|
1055
|
-
}
|
|
1056
|
-
/**
|
|
1057
|
-
* set multiple attrs at once using an object literal
|
|
1058
|
-
* @method
|
|
1059
|
-
* @name Konva.Node#setAttrs
|
|
1060
|
-
* @param {Object} config object containing key value pairs
|
|
1061
|
-
* @returns {Konva.Node}
|
|
1062
|
-
* @example
|
|
1063
|
-
* node.setAttrs({
|
|
1064
|
-
* x: 5,
|
|
1065
|
-
* fill: 'red'
|
|
1066
|
-
* });
|
|
1067
|
-
*/
|
|
1068
|
-
setAttrs(config: any) {
|
|
1069
|
-
this._batchTransformChanges(() => {
|
|
1070
|
-
let key, method;
|
|
1071
|
-
if (!config) {
|
|
1072
|
-
return this;
|
|
1073
|
-
}
|
|
1074
|
-
for (key in config) {
|
|
1075
|
-
if (key === CHILDREN) {
|
|
1076
|
-
continue;
|
|
1077
|
-
}
|
|
1078
|
-
method = SET + Util._capitalize(key);
|
|
1079
|
-
// use setter if available
|
|
1080
|
-
if (Util._isFunction(this[method])) {
|
|
1081
|
-
this[method](config[key]);
|
|
1082
|
-
} else {
|
|
1083
|
-
// otherwise set directly
|
|
1084
|
-
this._setAttr(key, config[key]);
|
|
1085
|
-
}
|
|
1086
|
-
}
|
|
1087
|
-
});
|
|
1088
|
-
|
|
1089
|
-
return this;
|
|
1090
|
-
}
|
|
1091
|
-
/**
|
|
1092
|
-
* determine if node is listening for events by taking into account ancestors.
|
|
1093
|
-
*
|
|
1094
|
-
* Parent | Self | isListening
|
|
1095
|
-
* listening | listening |
|
|
1096
|
-
* ----------+-----------+------------
|
|
1097
|
-
* T | T | T
|
|
1098
|
-
* T | F | F
|
|
1099
|
-
* F | T | F
|
|
1100
|
-
* F | F | F
|
|
1101
|
-
*
|
|
1102
|
-
* @method
|
|
1103
|
-
* @name Konva.Node#isListening
|
|
1104
|
-
* @returns {Boolean}
|
|
1105
|
-
*/
|
|
1106
|
-
isListening() {
|
|
1107
|
-
return this._getCache(LISTENING, this._isListening);
|
|
1108
|
-
}
|
|
1109
|
-
_isListening(relativeTo?: Node): boolean {
|
|
1110
|
-
const listening = this.listening();
|
|
1111
|
-
if (!listening) {
|
|
1112
|
-
return false;
|
|
1113
|
-
}
|
|
1114
|
-
const parent = this.getParent();
|
|
1115
|
-
if (parent && parent !== relativeTo && this !== relativeTo) {
|
|
1116
|
-
return parent._isListening(relativeTo);
|
|
1117
|
-
} else {
|
|
1118
|
-
return true;
|
|
1119
|
-
}
|
|
1120
|
-
}
|
|
1121
|
-
/**
|
|
1122
|
-
* determine if node is visible by taking into account ancestors.
|
|
1123
|
-
*
|
|
1124
|
-
* Parent | Self | isVisible
|
|
1125
|
-
* visible | visible |
|
|
1126
|
-
* ----------+-----------+------------
|
|
1127
|
-
* T | T | T
|
|
1128
|
-
* T | F | F
|
|
1129
|
-
* F | T | F
|
|
1130
|
-
* F | F | F
|
|
1131
|
-
* @method
|
|
1132
|
-
* @name Konva.Node#isVisible
|
|
1133
|
-
* @returns {Boolean}
|
|
1134
|
-
*/
|
|
1135
|
-
isVisible() {
|
|
1136
|
-
return this._getCache(VISIBLE, this._isVisible);
|
|
1137
|
-
}
|
|
1138
|
-
_isVisible(relativeTo?: Node): boolean {
|
|
1139
|
-
const visible = this.visible();
|
|
1140
|
-
if (!visible) {
|
|
1141
|
-
return false;
|
|
1142
|
-
}
|
|
1143
|
-
const parent = this.getParent();
|
|
1144
|
-
if (parent && parent !== relativeTo && this !== relativeTo) {
|
|
1145
|
-
return parent._isVisible(relativeTo);
|
|
1146
|
-
} else {
|
|
1147
|
-
return true;
|
|
1148
|
-
}
|
|
1149
|
-
}
|
|
1150
|
-
shouldDrawHit(top?: Node, skipDragCheck = false) {
|
|
1151
|
-
if (top) {
|
|
1152
|
-
return this._isVisible(top) && this._isListening(top);
|
|
1153
|
-
}
|
|
1154
|
-
const layer = this.getLayer();
|
|
1155
|
-
|
|
1156
|
-
let layerUnderDrag = false;
|
|
1157
|
-
DD._dragElements.forEach((elem) => {
|
|
1158
|
-
if (elem.dragStatus !== 'dragging') {
|
|
1159
|
-
return;
|
|
1160
|
-
} else if (elem.node.nodeType === 'Stage') {
|
|
1161
|
-
layerUnderDrag = true;
|
|
1162
|
-
} else if (elem.node.getLayer() === layer) {
|
|
1163
|
-
layerUnderDrag = true;
|
|
1164
|
-
}
|
|
1165
|
-
});
|
|
1166
|
-
|
|
1167
|
-
const dragSkip =
|
|
1168
|
-
!skipDragCheck &&
|
|
1169
|
-
!Konva.hitOnDragEnabled &&
|
|
1170
|
-
(layerUnderDrag || Konva.isTransforming());
|
|
1171
|
-
return this.isListening() && this.isVisible() && !dragSkip;
|
|
1172
|
-
}
|
|
1173
|
-
|
|
1174
|
-
/**
|
|
1175
|
-
* show node. set visible = true
|
|
1176
|
-
* @method
|
|
1177
|
-
* @name Konva.Node#show
|
|
1178
|
-
* @returns {Konva.Node}
|
|
1179
|
-
*/
|
|
1180
|
-
show() {
|
|
1181
|
-
this.visible(true);
|
|
1182
|
-
return this;
|
|
1183
|
-
}
|
|
1184
|
-
/**
|
|
1185
|
-
* hide node. Hidden nodes are no longer detectable
|
|
1186
|
-
* @method
|
|
1187
|
-
* @name Konva.Node#hide
|
|
1188
|
-
* @returns {Konva.Node}
|
|
1189
|
-
*/
|
|
1190
|
-
hide() {
|
|
1191
|
-
this.visible(false);
|
|
1192
|
-
return this;
|
|
1193
|
-
}
|
|
1194
|
-
getZIndex() {
|
|
1195
|
-
return this.index || 0;
|
|
1196
|
-
}
|
|
1197
|
-
/**
|
|
1198
|
-
* get absolute z-index which takes into account sibling
|
|
1199
|
-
* and ancestor indices
|
|
1200
|
-
* @method
|
|
1201
|
-
* @name Konva.Node#getAbsoluteZIndex
|
|
1202
|
-
* @returns {Integer}
|
|
1203
|
-
*/
|
|
1204
|
-
getAbsoluteZIndex() {
|
|
1205
|
-
let depth = this.getDepth(),
|
|
1206
|
-
that = this,
|
|
1207
|
-
index = 0,
|
|
1208
|
-
nodes,
|
|
1209
|
-
len,
|
|
1210
|
-
n,
|
|
1211
|
-
child;
|
|
1212
|
-
|
|
1213
|
-
function addChildren(children) {
|
|
1214
|
-
nodes = [];
|
|
1215
|
-
len = children.length;
|
|
1216
|
-
for (n = 0; n < len; n++) {
|
|
1217
|
-
child = children[n];
|
|
1218
|
-
index++;
|
|
1219
|
-
|
|
1220
|
-
if (child.nodeType !== SHAPE) {
|
|
1221
|
-
nodes = nodes.concat(child.getChildren().slice());
|
|
1222
|
-
}
|
|
1223
|
-
|
|
1224
|
-
if (child._id === that._id) {
|
|
1225
|
-
n = len;
|
|
1226
|
-
}
|
|
1227
|
-
}
|
|
1228
|
-
|
|
1229
|
-
if (nodes.length > 0 && nodes[0].getDepth() <= depth) {
|
|
1230
|
-
addChildren(nodes);
|
|
1231
|
-
}
|
|
1232
|
-
}
|
|
1233
|
-
const stage = this.getStage();
|
|
1234
|
-
if (that.nodeType !== UPPER_STAGE && stage) {
|
|
1235
|
-
addChildren(stage.getChildren());
|
|
1236
|
-
}
|
|
1237
|
-
|
|
1238
|
-
return index;
|
|
1239
|
-
}
|
|
1240
|
-
/**
|
|
1241
|
-
* get node depth in node tree. Returns an integer.
|
|
1242
|
-
* e.g. Stage depth will always be 0. Layers will always be 1. Groups and Shapes will always
|
|
1243
|
-
* be >= 2
|
|
1244
|
-
* @method
|
|
1245
|
-
* @name Konva.Node#getDepth
|
|
1246
|
-
* @returns {Integer}
|
|
1247
|
-
*/
|
|
1248
|
-
getDepth() {
|
|
1249
|
-
let depth = 0,
|
|
1250
|
-
parent = this.parent;
|
|
1251
|
-
|
|
1252
|
-
while (parent) {
|
|
1253
|
-
depth++;
|
|
1254
|
-
parent = parent.parent;
|
|
1255
|
-
}
|
|
1256
|
-
return depth;
|
|
1257
|
-
}
|
|
1258
|
-
|
|
1259
|
-
// sometimes we do several attributes changes
|
|
1260
|
-
// like node.position(pos)
|
|
1261
|
-
// for performance reasons, lets batch transform reset
|
|
1262
|
-
// so it work faster
|
|
1263
|
-
_batchTransformChanges(func) {
|
|
1264
|
-
this._batchingTransformChange = true;
|
|
1265
|
-
func();
|
|
1266
|
-
this._batchingTransformChange = false;
|
|
1267
|
-
if (this._needClearTransformCache) {
|
|
1268
|
-
this._clearCache(TRANSFORM);
|
|
1269
|
-
this._clearSelfAndDescendantCache(ABSOLUTE_TRANSFORM);
|
|
1270
|
-
}
|
|
1271
|
-
this._needClearTransformCache = false;
|
|
1272
|
-
}
|
|
1273
|
-
|
|
1274
|
-
setPosition(pos: Vector2d) {
|
|
1275
|
-
this._batchTransformChanges(() => {
|
|
1276
|
-
this.x(pos.x);
|
|
1277
|
-
this.y(pos.y);
|
|
1278
|
-
});
|
|
1279
|
-
return this;
|
|
1280
|
-
}
|
|
1281
|
-
getPosition() {
|
|
1282
|
-
return {
|
|
1283
|
-
x: this.x(),
|
|
1284
|
-
y: this.y(),
|
|
1285
|
-
};
|
|
1286
|
-
}
|
|
1287
|
-
/**
|
|
1288
|
-
* get position of first pointer (like mouse or first touch) relative to local coordinates of current node
|
|
1289
|
-
* @method
|
|
1290
|
-
* @name Konva.Node#getRelativePointerPosition
|
|
1291
|
-
* @returns {Konva.Node}
|
|
1292
|
-
* @example
|
|
1293
|
-
*
|
|
1294
|
-
* // let's think we have a rectangle at position x = 10, y = 10
|
|
1295
|
-
* // now we clicked at x = 15, y = 15 of the stage
|
|
1296
|
-
* // if you want to know position of the click, related to the rectangle you can use
|
|
1297
|
-
* rect.getRelativePointerPosition();
|
|
1298
|
-
*/
|
|
1299
|
-
getRelativePointerPosition() {
|
|
1300
|
-
const stage = this.getStage();
|
|
1301
|
-
if (!stage) {
|
|
1302
|
-
return null;
|
|
1303
|
-
}
|
|
1304
|
-
// get pointer (say mouse or touch) position
|
|
1305
|
-
const pos = stage.getPointerPosition();
|
|
1306
|
-
if (!pos) {
|
|
1307
|
-
return null;
|
|
1308
|
-
}
|
|
1309
|
-
const transform = this.getAbsoluteTransform().copy();
|
|
1310
|
-
// to detect relative position we need to invert transform
|
|
1311
|
-
transform.invert();
|
|
1312
|
-
// now we can find relative point
|
|
1313
|
-
return transform.point(pos);
|
|
1314
|
-
}
|
|
1315
|
-
/**
|
|
1316
|
-
* get absolute position of a node. That function can be used to calculate absolute position, but relative to any ancestor
|
|
1317
|
-
* @method
|
|
1318
|
-
* @name Konva.Node#getAbsolutePosition
|
|
1319
|
-
* @param {Object} Ancestor optional ancestor node
|
|
1320
|
-
* @returns {Konva.Node}
|
|
1321
|
-
* @example
|
|
1322
|
-
*
|
|
1323
|
-
* // returns absolute position relative to top-left corner of canvas
|
|
1324
|
-
* node.getAbsolutePosition();
|
|
1325
|
-
*
|
|
1326
|
-
* // calculate absolute position of node, inside stage
|
|
1327
|
-
* // so stage transforms are ignored
|
|
1328
|
-
* node.getAbsolutePosition(stage)
|
|
1329
|
-
*/
|
|
1330
|
-
getAbsolutePosition(top?: Node) {
|
|
1331
|
-
let haveCachedParent = false;
|
|
1332
|
-
let parent = this.parent;
|
|
1333
|
-
while (parent) {
|
|
1334
|
-
if (parent.isCached()) {
|
|
1335
|
-
haveCachedParent = true;
|
|
1336
|
-
break;
|
|
1337
|
-
}
|
|
1338
|
-
parent = parent.parent;
|
|
1339
|
-
}
|
|
1340
|
-
if (haveCachedParent && !top) {
|
|
1341
|
-
// make fake top element
|
|
1342
|
-
// "true" is not a node, but it will just allow skip all caching
|
|
1343
|
-
top = true as any;
|
|
1344
|
-
}
|
|
1345
|
-
const absoluteMatrix = this.getAbsoluteTransform(top).getMatrix(),
|
|
1346
|
-
absoluteTransform = new Transform(),
|
|
1347
|
-
offset = this.offset();
|
|
1348
|
-
|
|
1349
|
-
// clone the matrix array
|
|
1350
|
-
absoluteTransform.m = absoluteMatrix.slice();
|
|
1351
|
-
absoluteTransform.translate(offset.x, offset.y);
|
|
1352
|
-
|
|
1353
|
-
return absoluteTransform.getTranslation();
|
|
1354
|
-
}
|
|
1355
|
-
setAbsolutePosition(pos: Vector2d) {
|
|
1356
|
-
const { x, y, ...origTrans } = this._clearTransform();
|
|
1357
|
-
|
|
1358
|
-
// don't clear translation
|
|
1359
|
-
this.attrs.x = x;
|
|
1360
|
-
this.attrs.y = y;
|
|
1361
|
-
|
|
1362
|
-
// important, use non cached value
|
|
1363
|
-
this._clearCache(TRANSFORM);
|
|
1364
|
-
const it = this._getAbsoluteTransform().copy();
|
|
1365
|
-
|
|
1366
|
-
it.invert();
|
|
1367
|
-
it.translate(pos.x, pos.y);
|
|
1368
|
-
pos = {
|
|
1369
|
-
x: this.attrs.x + it.getTranslation().x,
|
|
1370
|
-
y: this.attrs.y + it.getTranslation().y,
|
|
1371
|
-
};
|
|
1372
|
-
this._setTransform(origTrans);
|
|
1373
|
-
this.setPosition({ x: pos.x, y: pos.y });
|
|
1374
|
-
this._clearCache(TRANSFORM);
|
|
1375
|
-
this._clearSelfAndDescendantCache(ABSOLUTE_TRANSFORM);
|
|
1376
|
-
|
|
1377
|
-
return this;
|
|
1378
|
-
}
|
|
1379
|
-
_setTransform(trans) {
|
|
1380
|
-
let key;
|
|
1381
|
-
|
|
1382
|
-
for (key in trans) {
|
|
1383
|
-
this.attrs[key] = trans[key];
|
|
1384
|
-
}
|
|
1385
|
-
// this._clearCache(TRANSFORM);
|
|
1386
|
-
// this._clearSelfAndDescendantCache(ABSOLUTE_TRANSFORM);
|
|
1387
|
-
}
|
|
1388
|
-
_clearTransform() {
|
|
1389
|
-
const trans = {
|
|
1390
|
-
x: this.x(),
|
|
1391
|
-
y: this.y(),
|
|
1392
|
-
rotation: this.rotation(),
|
|
1393
|
-
scaleX: this.scaleX(),
|
|
1394
|
-
scaleY: this.scaleY(),
|
|
1395
|
-
offsetX: this.offsetX(),
|
|
1396
|
-
offsetY: this.offsetY(),
|
|
1397
|
-
skewX: this.skewX(),
|
|
1398
|
-
skewY: this.skewY(),
|
|
1399
|
-
};
|
|
1400
|
-
|
|
1401
|
-
this.attrs.x = 0;
|
|
1402
|
-
this.attrs.y = 0;
|
|
1403
|
-
this.attrs.rotation = 0;
|
|
1404
|
-
this.attrs.scaleX = 1;
|
|
1405
|
-
this.attrs.scaleY = 1;
|
|
1406
|
-
this.attrs.offsetX = 0;
|
|
1407
|
-
this.attrs.offsetY = 0;
|
|
1408
|
-
this.attrs.skewX = 0;
|
|
1409
|
-
this.attrs.skewY = 0;
|
|
1410
|
-
|
|
1411
|
-
// return original transform
|
|
1412
|
-
return trans;
|
|
1413
|
-
}
|
|
1414
|
-
/**
|
|
1415
|
-
* move node by an amount relative to its current position
|
|
1416
|
-
* @method
|
|
1417
|
-
* @name Konva.Node#move
|
|
1418
|
-
* @param {Object} change
|
|
1419
|
-
* @param {Number} change.x
|
|
1420
|
-
* @param {Number} change.y
|
|
1421
|
-
* @returns {Konva.Node}
|
|
1422
|
-
* @example
|
|
1423
|
-
* // move node in x direction by 1px and y direction by 2px
|
|
1424
|
-
* node.move({
|
|
1425
|
-
* x: 1,
|
|
1426
|
-
* y: 2
|
|
1427
|
-
* });
|
|
1428
|
-
*/
|
|
1429
|
-
move(change: Vector2d) {
|
|
1430
|
-
let changeX = change.x,
|
|
1431
|
-
changeY = change.y,
|
|
1432
|
-
x = this.x(),
|
|
1433
|
-
y = this.y();
|
|
1434
|
-
|
|
1435
|
-
if (changeX !== undefined) {
|
|
1436
|
-
x += changeX;
|
|
1437
|
-
}
|
|
1438
|
-
|
|
1439
|
-
if (changeY !== undefined) {
|
|
1440
|
-
y += changeY;
|
|
1441
|
-
}
|
|
1442
|
-
|
|
1443
|
-
this.setPosition({ x: x, y: y });
|
|
1444
|
-
return this;
|
|
1445
|
-
}
|
|
1446
|
-
_eachAncestorReverse(func, top) {
|
|
1447
|
-
let family: Array<Node> = [],
|
|
1448
|
-
parent = this.getParent(),
|
|
1449
|
-
len,
|
|
1450
|
-
n;
|
|
1451
|
-
|
|
1452
|
-
// if top node is defined, and this node is top node,
|
|
1453
|
-
// there's no need to build a family tree. just execute
|
|
1454
|
-
// func with this because it will be the only node
|
|
1455
|
-
if (top && top._id === this._id) {
|
|
1456
|
-
// func(this);
|
|
1457
|
-
return;
|
|
1458
|
-
}
|
|
1459
|
-
|
|
1460
|
-
family.unshift(this);
|
|
1461
|
-
|
|
1462
|
-
while (parent && (!top || parent._id !== top._id)) {
|
|
1463
|
-
family.unshift(parent);
|
|
1464
|
-
parent = parent.parent;
|
|
1465
|
-
}
|
|
1466
|
-
|
|
1467
|
-
len = family.length;
|
|
1468
|
-
for (n = 0; n < len; n++) {
|
|
1469
|
-
func(family[n]);
|
|
1470
|
-
}
|
|
1471
|
-
}
|
|
1472
|
-
/**
|
|
1473
|
-
* rotate node by an amount in degrees relative to its current rotation
|
|
1474
|
-
* @method
|
|
1475
|
-
* @name Konva.Node#rotate
|
|
1476
|
-
* @param {Number} theta
|
|
1477
|
-
* @returns {Konva.Node}
|
|
1478
|
-
*/
|
|
1479
|
-
rotate(theta: number) {
|
|
1480
|
-
this.rotation(this.rotation() + theta);
|
|
1481
|
-
return this;
|
|
1482
|
-
}
|
|
1483
|
-
/**
|
|
1484
|
-
* move node to the top of its siblings
|
|
1485
|
-
* @method
|
|
1486
|
-
* @name Konva.Node#moveToTop
|
|
1487
|
-
* @returns {Boolean}
|
|
1488
|
-
*/
|
|
1489
|
-
moveToTop() {
|
|
1490
|
-
if (!this.parent) {
|
|
1491
|
-
Util.warn('Node has no parent. moveToTop function is ignored.');
|
|
1492
|
-
return false;
|
|
1493
|
-
}
|
|
1494
|
-
const index = this.index,
|
|
1495
|
-
len = this.parent.getChildren().length;
|
|
1496
|
-
if (index < len - 1) {
|
|
1497
|
-
this.parent.children.splice(index, 1);
|
|
1498
|
-
this.parent.children.push(this);
|
|
1499
|
-
this.parent._setChildrenIndices();
|
|
1500
|
-
return true;
|
|
1501
|
-
}
|
|
1502
|
-
return false;
|
|
1503
|
-
}
|
|
1504
|
-
/**
|
|
1505
|
-
* move node up
|
|
1506
|
-
* @method
|
|
1507
|
-
* @name Konva.Node#moveUp
|
|
1508
|
-
* @returns {Boolean} flag is moved or not
|
|
1509
|
-
*/
|
|
1510
|
-
moveUp() {
|
|
1511
|
-
if (!this.parent) {
|
|
1512
|
-
Util.warn('Node has no parent. moveUp function is ignored.');
|
|
1513
|
-
return false;
|
|
1514
|
-
}
|
|
1515
|
-
const index = this.index,
|
|
1516
|
-
len = this.parent.getChildren().length;
|
|
1517
|
-
if (index < len - 1) {
|
|
1518
|
-
this.parent.children.splice(index, 1);
|
|
1519
|
-
this.parent.children.splice(index + 1, 0, this);
|
|
1520
|
-
this.parent._setChildrenIndices();
|
|
1521
|
-
return true;
|
|
1522
|
-
}
|
|
1523
|
-
return false;
|
|
1524
|
-
}
|
|
1525
|
-
/**
|
|
1526
|
-
* move node down
|
|
1527
|
-
* @method
|
|
1528
|
-
* @name Konva.Node#moveDown
|
|
1529
|
-
* @returns {Boolean}
|
|
1530
|
-
*/
|
|
1531
|
-
moveDown() {
|
|
1532
|
-
if (!this.parent) {
|
|
1533
|
-
Util.warn('Node has no parent. moveDown function is ignored.');
|
|
1534
|
-
return false;
|
|
1535
|
-
}
|
|
1536
|
-
const index = this.index;
|
|
1537
|
-
if (index > 0) {
|
|
1538
|
-
this.parent.children.splice(index, 1);
|
|
1539
|
-
this.parent.children.splice(index - 1, 0, this);
|
|
1540
|
-
this.parent._setChildrenIndices();
|
|
1541
|
-
return true;
|
|
1542
|
-
}
|
|
1543
|
-
return false;
|
|
1544
|
-
}
|
|
1545
|
-
/**
|
|
1546
|
-
* move node to the bottom of its siblings
|
|
1547
|
-
* @method
|
|
1548
|
-
* @name Konva.Node#moveToBottom
|
|
1549
|
-
* @returns {Boolean}
|
|
1550
|
-
*/
|
|
1551
|
-
moveToBottom() {
|
|
1552
|
-
if (!this.parent) {
|
|
1553
|
-
Util.warn('Node has no parent. moveToBottom function is ignored.');
|
|
1554
|
-
return false;
|
|
1555
|
-
}
|
|
1556
|
-
const index = this.index;
|
|
1557
|
-
if (index > 0) {
|
|
1558
|
-
this.parent.children.splice(index, 1);
|
|
1559
|
-
this.parent.children.unshift(this);
|
|
1560
|
-
this.parent._setChildrenIndices();
|
|
1561
|
-
return true;
|
|
1562
|
-
}
|
|
1563
|
-
return false;
|
|
1564
|
-
}
|
|
1565
|
-
setZIndex(zIndex) {
|
|
1566
|
-
if (!this.parent) {
|
|
1567
|
-
Util.warn('Node has no parent. zIndex parameter is ignored.');
|
|
1568
|
-
return this;
|
|
1569
|
-
}
|
|
1570
|
-
if (zIndex < 0 || zIndex >= this.parent.children.length) {
|
|
1571
|
-
Util.warn(
|
|
1572
|
-
'Unexpected value ' +
|
|
1573
|
-
zIndex +
|
|
1574
|
-
' for zIndex property. zIndex is just index of a node in children of its parent. Expected value is from 0 to ' +
|
|
1575
|
-
(this.parent.children.length - 1) +
|
|
1576
|
-
'.'
|
|
1577
|
-
);
|
|
1578
|
-
}
|
|
1579
|
-
const index = this.index;
|
|
1580
|
-
this.parent.children.splice(index, 1);
|
|
1581
|
-
this.parent.children.splice(zIndex, 0, this);
|
|
1582
|
-
this.parent._setChildrenIndices();
|
|
1583
|
-
return this;
|
|
1584
|
-
}
|
|
1585
|
-
/**
|
|
1586
|
-
* get absolute opacity
|
|
1587
|
-
* @method
|
|
1588
|
-
* @name Konva.Node#getAbsoluteOpacity
|
|
1589
|
-
* @returns {Number}
|
|
1590
|
-
*/
|
|
1591
|
-
getAbsoluteOpacity() {
|
|
1592
|
-
return this._getCache(ABSOLUTE_OPACITY, this._getAbsoluteOpacity);
|
|
1593
|
-
}
|
|
1594
|
-
_getAbsoluteOpacity() {
|
|
1595
|
-
let absOpacity = this.opacity();
|
|
1596
|
-
const parent = this.getParent();
|
|
1597
|
-
if (parent && !parent._isUnderCache) {
|
|
1598
|
-
absOpacity *= parent.getAbsoluteOpacity();
|
|
1599
|
-
}
|
|
1600
|
-
return absOpacity;
|
|
1601
|
-
}
|
|
1602
|
-
/**
|
|
1603
|
-
* move node to another container
|
|
1604
|
-
* @method
|
|
1605
|
-
* @name Konva.Node#moveTo
|
|
1606
|
-
* @param {Container} newContainer
|
|
1607
|
-
* @returns {Konva.Node}
|
|
1608
|
-
* @example
|
|
1609
|
-
* // move node from current layer into layer2
|
|
1610
|
-
* node.moveTo(layer2);
|
|
1611
|
-
*/
|
|
1612
|
-
moveTo(newContainer: any) {
|
|
1613
|
-
// do nothing if new container is already parent
|
|
1614
|
-
if (this.getParent() !== newContainer) {
|
|
1615
|
-
this._remove();
|
|
1616
|
-
newContainer.add(this);
|
|
1617
|
-
}
|
|
1618
|
-
return this;
|
|
1619
|
-
}
|
|
1620
|
-
/**
|
|
1621
|
-
* convert Node into an object for serialization. Returns an object.
|
|
1622
|
-
* @method
|
|
1623
|
-
* @name Konva.Node#toObject
|
|
1624
|
-
* @returns {Object}
|
|
1625
|
-
*/
|
|
1626
|
-
toObject() {
|
|
1627
|
-
let attrs = this.getAttrs() as any,
|
|
1628
|
-
key,
|
|
1629
|
-
val,
|
|
1630
|
-
getter,
|
|
1631
|
-
defaultValue,
|
|
1632
|
-
nonPlainObject;
|
|
1633
|
-
|
|
1634
|
-
const obj: {
|
|
1635
|
-
attrs: Config & Record<string, any>;
|
|
1636
|
-
className: string;
|
|
1637
|
-
children?: Array<any>;
|
|
1638
|
-
} = {
|
|
1639
|
-
attrs: {} as Config & Record<string, any>,
|
|
1640
|
-
className: this.getClassName(),
|
|
1641
|
-
};
|
|
1642
|
-
|
|
1643
|
-
for (key in attrs) {
|
|
1644
|
-
val = attrs[key];
|
|
1645
|
-
// if value is object and object is not plain
|
|
1646
|
-
// like class instance, we should skip it and to not include
|
|
1647
|
-
nonPlainObject =
|
|
1648
|
-
Util.isObject(val) && !Util._isPlainObject(val) && !Util._isArray(val);
|
|
1649
|
-
if (nonPlainObject) {
|
|
1650
|
-
continue;
|
|
1651
|
-
}
|
|
1652
|
-
getter = typeof this[key] === 'function' && this[key];
|
|
1653
|
-
// remove attr value so that we can extract the default value from the getter
|
|
1654
|
-
delete attrs[key];
|
|
1655
|
-
defaultValue = getter ? getter.call(this) : null;
|
|
1656
|
-
// restore attr value
|
|
1657
|
-
attrs[key] = val;
|
|
1658
|
-
if (defaultValue !== val) {
|
|
1659
|
-
(obj.attrs as any)[key] = val;
|
|
1660
|
-
}
|
|
1661
|
-
}
|
|
1662
|
-
|
|
1663
|
-
return Util._prepareToStringify(obj) as typeof obj;
|
|
1664
|
-
}
|
|
1665
|
-
/**
|
|
1666
|
-
* convert Node into a JSON string. Returns a JSON string.
|
|
1667
|
-
* @method
|
|
1668
|
-
* @name Konva.Node#toJSON
|
|
1669
|
-
* @returns {String}
|
|
1670
|
-
*/
|
|
1671
|
-
toJSON() {
|
|
1672
|
-
return JSON.stringify(this.toObject());
|
|
1673
|
-
}
|
|
1674
|
-
/**
|
|
1675
|
-
* get parent container
|
|
1676
|
-
* @method
|
|
1677
|
-
* @name Konva.Node#getParent
|
|
1678
|
-
* @returns {Konva.Node}
|
|
1679
|
-
*/
|
|
1680
|
-
getParent() {
|
|
1681
|
-
return this.parent;
|
|
1682
|
-
}
|
|
1683
|
-
/**
|
|
1684
|
-
* get all ancestors (parent then parent of the parent, etc) of the node
|
|
1685
|
-
* @method
|
|
1686
|
-
* @name Konva.Node#findAncestors
|
|
1687
|
-
* @param {String} selector selector for search
|
|
1688
|
-
* @param {Boolean} [includeSelf] show we think that node is ancestro itself?
|
|
1689
|
-
* @param {Konva.Node} [stopNode] optional node where we need to stop searching (one of ancestors)
|
|
1690
|
-
* @returns {Array} [ancestors]
|
|
1691
|
-
* @example
|
|
1692
|
-
* // get one of the parent group
|
|
1693
|
-
* var parentGroups = node.findAncestors('Group');
|
|
1694
|
-
*/
|
|
1695
|
-
findAncestors(
|
|
1696
|
-
selector: string | Function,
|
|
1697
|
-
includeSelf?: boolean,
|
|
1698
|
-
stopNode?: Node
|
|
1699
|
-
) {
|
|
1700
|
-
const res: Array<Node> = [];
|
|
1701
|
-
|
|
1702
|
-
if (includeSelf && this._isMatch(selector)) {
|
|
1703
|
-
res.push(this);
|
|
1704
|
-
}
|
|
1705
|
-
let ancestor = this.parent;
|
|
1706
|
-
while (ancestor) {
|
|
1707
|
-
if (ancestor === stopNode) {
|
|
1708
|
-
return res;
|
|
1709
|
-
}
|
|
1710
|
-
if (ancestor._isMatch(selector)) {
|
|
1711
|
-
res.push(ancestor);
|
|
1712
|
-
}
|
|
1713
|
-
ancestor = ancestor.parent;
|
|
1714
|
-
}
|
|
1715
|
-
return res;
|
|
1716
|
-
}
|
|
1717
|
-
isAncestorOf(node: Node) {
|
|
1718
|
-
return false;
|
|
1719
|
-
}
|
|
1720
|
-
/**
|
|
1721
|
-
* get ancestor (parent or parent of the parent, etc) of the node that match passed selector
|
|
1722
|
-
* @method
|
|
1723
|
-
* @name Konva.Node#findAncestor
|
|
1724
|
-
* @param {String} selector selector for search
|
|
1725
|
-
* @param {Boolean} [includeSelf] show we think that node is ancestro itself?
|
|
1726
|
-
* @param {Konva.Node} [stopNode] optional node where we need to stop searching (one of ancestors)
|
|
1727
|
-
* @returns {Konva.Node} ancestor
|
|
1728
|
-
* @example
|
|
1729
|
-
* // get one of the parent group
|
|
1730
|
-
* var group = node.findAncestors('.mygroup');
|
|
1731
|
-
*/
|
|
1732
|
-
findAncestor(
|
|
1733
|
-
selector: string | Function,
|
|
1734
|
-
includeSelf?: boolean,
|
|
1735
|
-
stopNode?: Container
|
|
1736
|
-
) {
|
|
1737
|
-
return this.findAncestors(selector, includeSelf, stopNode)[0];
|
|
1738
|
-
}
|
|
1739
|
-
// is current node match passed selector?
|
|
1740
|
-
_isMatch(selector: string | Function) {
|
|
1741
|
-
if (!selector) {
|
|
1742
|
-
return false;
|
|
1743
|
-
}
|
|
1744
|
-
if (typeof selector === 'function') {
|
|
1745
|
-
return selector(this);
|
|
1746
|
-
}
|
|
1747
|
-
let selectorArr = selector.replace(/ /g, '').split(','),
|
|
1748
|
-
len = selectorArr.length,
|
|
1749
|
-
n,
|
|
1750
|
-
sel;
|
|
1751
|
-
|
|
1752
|
-
for (n = 0; n < len; n++) {
|
|
1753
|
-
sel = selectorArr[n];
|
|
1754
|
-
if (!Util.isValidSelector(sel)) {
|
|
1755
|
-
Util.warn(
|
|
1756
|
-
'Selector "' +
|
|
1757
|
-
sel +
|
|
1758
|
-
'" is invalid. Allowed selectors examples are "#foo", ".bar" or "Group".'
|
|
1759
|
-
);
|
|
1760
|
-
Util.warn(
|
|
1761
|
-
'If you have a custom shape with such className, please change it to start with upper letter like "Triangle".'
|
|
1762
|
-
);
|
|
1763
|
-
Util.warn('Konva is awesome, right?');
|
|
1764
|
-
}
|
|
1765
|
-
// id selector
|
|
1766
|
-
if (sel.charAt(0) === '#') {
|
|
1767
|
-
if (this.id() === sel.slice(1)) {
|
|
1768
|
-
return true;
|
|
1769
|
-
}
|
|
1770
|
-
} else if (sel.charAt(0) === '.') {
|
|
1771
|
-
// name selector
|
|
1772
|
-
if (this.hasName(sel.slice(1))) {
|
|
1773
|
-
return true;
|
|
1774
|
-
}
|
|
1775
|
-
} else if (this.className === sel || this.nodeType === sel) {
|
|
1776
|
-
return true;
|
|
1777
|
-
}
|
|
1778
|
-
}
|
|
1779
|
-
return false;
|
|
1780
|
-
}
|
|
1781
|
-
/**
|
|
1782
|
-
* get layer ancestor
|
|
1783
|
-
* @method
|
|
1784
|
-
* @name Konva.Node#getLayer
|
|
1785
|
-
* @returns {Konva.Layer}
|
|
1786
|
-
*/
|
|
1787
|
-
getLayer(): Layer | null {
|
|
1788
|
-
const parent = this.getParent();
|
|
1789
|
-
return parent ? parent.getLayer() : null;
|
|
1790
|
-
}
|
|
1791
|
-
/**
|
|
1792
|
-
* get stage ancestor
|
|
1793
|
-
* @method
|
|
1794
|
-
* @name Konva.Node#getStage
|
|
1795
|
-
* @returns {Konva.Stage}
|
|
1796
|
-
*/
|
|
1797
|
-
getStage(): Stage | null {
|
|
1798
|
-
return this._getCache(STAGE, this._getStage);
|
|
1799
|
-
}
|
|
1800
|
-
|
|
1801
|
-
_getStage() {
|
|
1802
|
-
const parent = this.getParent();
|
|
1803
|
-
if (parent) {
|
|
1804
|
-
return parent.getStage();
|
|
1805
|
-
} else {
|
|
1806
|
-
return null;
|
|
1807
|
-
}
|
|
1808
|
-
}
|
|
1809
|
-
/**
|
|
1810
|
-
* fire event
|
|
1811
|
-
* @method
|
|
1812
|
-
* @name Konva.Node#fire
|
|
1813
|
-
* @param {String} eventType event type. can be a regular event, like click, mouseover, or mouseout, or it can be a custom event, like myCustomEvent
|
|
1814
|
-
* @param {Event} [evt] event object
|
|
1815
|
-
* @param {Boolean} [bubble] setting the value to false, or leaving it undefined, will result in the event
|
|
1816
|
-
* not bubbling. Setting the value to true will result in the event bubbling.
|
|
1817
|
-
* @returns {Konva.Node}
|
|
1818
|
-
* @example
|
|
1819
|
-
* // manually fire click event
|
|
1820
|
-
* node.fire('click');
|
|
1821
|
-
*
|
|
1822
|
-
* // fire custom event
|
|
1823
|
-
* node.fire('foo');
|
|
1824
|
-
*
|
|
1825
|
-
* // fire custom event with custom event object
|
|
1826
|
-
* node.fire('foo', {
|
|
1827
|
-
* bar: 10
|
|
1828
|
-
* });
|
|
1829
|
-
*
|
|
1830
|
-
* // fire click event that bubbles
|
|
1831
|
-
* node.fire('click', null, true);
|
|
1832
|
-
*/
|
|
1833
|
-
fire(eventType: string, evt: any = {}, bubble?: boolean) {
|
|
1834
|
-
evt.target = evt.target || this;
|
|
1835
|
-
// bubble
|
|
1836
|
-
if (bubble) {
|
|
1837
|
-
this._fireAndBubble(eventType, evt);
|
|
1838
|
-
} else {
|
|
1839
|
-
// no bubble
|
|
1840
|
-
this._fire(eventType, evt);
|
|
1841
|
-
}
|
|
1842
|
-
return this;
|
|
1843
|
-
}
|
|
1844
|
-
/**
|
|
1845
|
-
* get absolute transform of the node which takes into
|
|
1846
|
-
* account its ancestor transforms
|
|
1847
|
-
* @method
|
|
1848
|
-
* @name Konva.Node#getAbsoluteTransform
|
|
1849
|
-
* @returns {Konva.Transform}
|
|
1850
|
-
*/
|
|
1851
|
-
getAbsoluteTransform(top?: Node | null) {
|
|
1852
|
-
// if using an argument, we can't cache the result.
|
|
1853
|
-
if (top) {
|
|
1854
|
-
return this._getAbsoluteTransform(top);
|
|
1855
|
-
} else {
|
|
1856
|
-
// if no argument, we can cache the result
|
|
1857
|
-
return this._getCache(
|
|
1858
|
-
ABSOLUTE_TRANSFORM,
|
|
1859
|
-
this._getAbsoluteTransform
|
|
1860
|
-
) as Transform;
|
|
1861
|
-
}
|
|
1862
|
-
}
|
|
1863
|
-
_getAbsoluteTransform(top?: Node) {
|
|
1864
|
-
let at: Transform;
|
|
1865
|
-
// we we need position relative to an ancestor, we will iterate for all
|
|
1866
|
-
if (top) {
|
|
1867
|
-
at = new Transform();
|
|
1868
|
-
// start with stage and traverse downwards to self
|
|
1869
|
-
this._eachAncestorReverse(function (node: Node) {
|
|
1870
|
-
const transformsEnabled = node.transformsEnabled();
|
|
1871
|
-
|
|
1872
|
-
if (transformsEnabled === 'all') {
|
|
1873
|
-
at.multiply(node.getTransform());
|
|
1874
|
-
} else if (transformsEnabled === 'position') {
|
|
1875
|
-
at.translate(node.x() - node.offsetX(), node.y() - node.offsetY());
|
|
1876
|
-
}
|
|
1877
|
-
}, top);
|
|
1878
|
-
return at;
|
|
1879
|
-
} else {
|
|
1880
|
-
// try to use a cached value
|
|
1881
|
-
at = this._cache.get(ABSOLUTE_TRANSFORM) || new Transform();
|
|
1882
|
-
if (this.parent) {
|
|
1883
|
-
// transform will be cached
|
|
1884
|
-
this.parent.getAbsoluteTransform().copyInto(at);
|
|
1885
|
-
} else {
|
|
1886
|
-
at.reset();
|
|
1887
|
-
}
|
|
1888
|
-
const transformsEnabled = this.transformsEnabled();
|
|
1889
|
-
if (transformsEnabled === 'all') {
|
|
1890
|
-
at.multiply(this.getTransform());
|
|
1891
|
-
} else if (transformsEnabled === 'position') {
|
|
1892
|
-
// use "attrs" directly, because it is a bit faster
|
|
1893
|
-
const x = this.attrs.x || 0;
|
|
1894
|
-
const y = this.attrs.y || 0;
|
|
1895
|
-
const offsetX = this.attrs.offsetX || 0;
|
|
1896
|
-
const offsetY = this.attrs.offsetY || 0;
|
|
1897
|
-
|
|
1898
|
-
at.translate(x - offsetX, y - offsetY);
|
|
1899
|
-
}
|
|
1900
|
-
at.dirty = false;
|
|
1901
|
-
return at;
|
|
1902
|
-
}
|
|
1903
|
-
}
|
|
1904
|
-
/**
|
|
1905
|
-
* get absolute scale of the node which takes into
|
|
1906
|
-
* account its ancestor scales
|
|
1907
|
-
* @method
|
|
1908
|
-
* @name Konva.Node#getAbsoluteScale
|
|
1909
|
-
* @returns {Object}
|
|
1910
|
-
* @example
|
|
1911
|
-
* // get absolute scale x
|
|
1912
|
-
* var scaleX = node.getAbsoluteScale().x;
|
|
1913
|
-
*/
|
|
1914
|
-
getAbsoluteScale(top?: Node) {
|
|
1915
|
-
// do not cache this calculations,
|
|
1916
|
-
// because it use cache transform
|
|
1917
|
-
// this is special logic for caching with some shapes with shadow
|
|
1918
|
-
let parent: Node | null = this;
|
|
1919
|
-
while (parent) {
|
|
1920
|
-
if (parent._isUnderCache) {
|
|
1921
|
-
top = parent;
|
|
1922
|
-
}
|
|
1923
|
-
parent = parent.getParent();
|
|
1924
|
-
}
|
|
1925
|
-
|
|
1926
|
-
const transform = this.getAbsoluteTransform(top);
|
|
1927
|
-
const attrs = transform.decompose();
|
|
1928
|
-
|
|
1929
|
-
return {
|
|
1930
|
-
x: attrs.scaleX,
|
|
1931
|
-
y: attrs.scaleY,
|
|
1932
|
-
};
|
|
1933
|
-
}
|
|
1934
|
-
/**
|
|
1935
|
-
* get absolute rotation of the node which takes into
|
|
1936
|
-
* account its ancestor rotations
|
|
1937
|
-
* @method
|
|
1938
|
-
* @name Konva.Node#getAbsoluteRotation
|
|
1939
|
-
* @returns {Number}
|
|
1940
|
-
* @example
|
|
1941
|
-
* // get absolute rotation
|
|
1942
|
-
* var rotation = node.getAbsoluteRotation();
|
|
1943
|
-
*/
|
|
1944
|
-
getAbsoluteRotation() {
|
|
1945
|
-
// var parent: Node = this;
|
|
1946
|
-
// var rotation = 0;
|
|
1947
|
-
|
|
1948
|
-
// while (parent) {
|
|
1949
|
-
// rotation += parent.rotation();
|
|
1950
|
-
// parent = parent.getParent();
|
|
1951
|
-
// }
|
|
1952
|
-
// return rotation;
|
|
1953
|
-
return this.getAbsoluteTransform().decompose().rotation;
|
|
1954
|
-
}
|
|
1955
|
-
/**
|
|
1956
|
-
* get transform of the node
|
|
1957
|
-
* @method
|
|
1958
|
-
* @name Konva.Node#getTransform
|
|
1959
|
-
* @returns {Konva.Transform}
|
|
1960
|
-
*/
|
|
1961
|
-
getTransform() {
|
|
1962
|
-
return this._getCache(TRANSFORM, this._getTransform) as Transform;
|
|
1963
|
-
}
|
|
1964
|
-
_getTransform(): Transform {
|
|
1965
|
-
const m: Transform = this._cache.get(TRANSFORM) || new Transform();
|
|
1966
|
-
m.reset();
|
|
1967
|
-
|
|
1968
|
-
// I was trying to use attributes directly here
|
|
1969
|
-
// but it doesn't work for Transformer well
|
|
1970
|
-
// because it overwrite x,y getters
|
|
1971
|
-
const x = this.x(),
|
|
1972
|
-
y = this.y(),
|
|
1973
|
-
rotation = Konva.getAngle(this.rotation()),
|
|
1974
|
-
scaleX = this.attrs.scaleX ?? 1,
|
|
1975
|
-
scaleY = this.attrs.scaleY ?? 1,
|
|
1976
|
-
skewX = this.attrs.skewX || 0,
|
|
1977
|
-
skewY = this.attrs.skewY || 0,
|
|
1978
|
-
offsetX = this.attrs.offsetX || 0,
|
|
1979
|
-
offsetY = this.attrs.offsetY || 0;
|
|
1980
|
-
|
|
1981
|
-
if (x !== 0 || y !== 0) {
|
|
1982
|
-
m.translate(x, y);
|
|
1983
|
-
}
|
|
1984
|
-
if (rotation !== 0) {
|
|
1985
|
-
m.rotate(rotation);
|
|
1986
|
-
}
|
|
1987
|
-
if (skewX !== 0 || skewY !== 0) {
|
|
1988
|
-
m.skew(skewX, skewY);
|
|
1989
|
-
}
|
|
1990
|
-
if (scaleX !== 1 || scaleY !== 1) {
|
|
1991
|
-
m.scale(scaleX, scaleY);
|
|
1992
|
-
}
|
|
1993
|
-
if (offsetX !== 0 || offsetY !== 0) {
|
|
1994
|
-
m.translate(-1 * offsetX, -1 * offsetY);
|
|
1995
|
-
}
|
|
1996
|
-
|
|
1997
|
-
m.dirty = false;
|
|
1998
|
-
|
|
1999
|
-
return m;
|
|
2000
|
-
}
|
|
2001
|
-
/**
|
|
2002
|
-
* clone node. Returns a new Node instance with identical attributes. You can also override
|
|
2003
|
-
* the node properties with an object literal, enabling you to use an existing node as a template
|
|
2004
|
-
* for another node
|
|
2005
|
-
* @method
|
|
2006
|
-
* @name Konva.Node#clone
|
|
2007
|
-
* @param {Object} obj override attrs
|
|
2008
|
-
* @returns {Konva.Node}
|
|
2009
|
-
* @example
|
|
2010
|
-
* // simple clone
|
|
2011
|
-
* var clone = node.clone();
|
|
2012
|
-
*
|
|
2013
|
-
* // clone a node and override the x position
|
|
2014
|
-
* var clone = rect.clone({
|
|
2015
|
-
* x: 5
|
|
2016
|
-
* });
|
|
2017
|
-
*/
|
|
2018
|
-
clone(obj?: any) {
|
|
2019
|
-
// instantiate new node
|
|
2020
|
-
let attrs = Util.cloneObject(this.attrs),
|
|
2021
|
-
key,
|
|
2022
|
-
allListeners,
|
|
2023
|
-
len,
|
|
2024
|
-
n,
|
|
2025
|
-
listener;
|
|
2026
|
-
// apply attr overrides
|
|
2027
|
-
for (key in obj) {
|
|
2028
|
-
attrs[key] = obj[key];
|
|
2029
|
-
}
|
|
2030
|
-
|
|
2031
|
-
const node = new (this.constructor as any)(attrs);
|
|
2032
|
-
// copy over listeners
|
|
2033
|
-
for (key in this.eventListeners) {
|
|
2034
|
-
allListeners = this.eventListeners[key];
|
|
2035
|
-
len = allListeners.length;
|
|
2036
|
-
for (n = 0; n < len; n++) {
|
|
2037
|
-
listener = allListeners[n];
|
|
2038
|
-
/*
|
|
2039
|
-
* don't include konva namespaced listeners because
|
|
2040
|
-
* these are generated by the constructors
|
|
2041
|
-
*/
|
|
2042
|
-
if (listener.name.indexOf(KONVA) < 0) {
|
|
2043
|
-
// if listeners array doesn't exist, then create it
|
|
2044
|
-
if (!node.eventListeners[key]) {
|
|
2045
|
-
node.eventListeners[key] = [];
|
|
2046
|
-
}
|
|
2047
|
-
node.eventListeners[key].push(listener);
|
|
2048
|
-
}
|
|
2049
|
-
}
|
|
2050
|
-
}
|
|
2051
|
-
return node;
|
|
2052
|
-
}
|
|
2053
|
-
_toKonvaCanvas(config) {
|
|
2054
|
-
config = config || {};
|
|
2055
|
-
|
|
2056
|
-
const box = this.getClientRect();
|
|
2057
|
-
|
|
2058
|
-
const stage = this.getStage(),
|
|
2059
|
-
x = config.x !== undefined ? config.x : Math.floor(box.x),
|
|
2060
|
-
y = config.y !== undefined ? config.y : Math.floor(box.y),
|
|
2061
|
-
pixelRatio = config.pixelRatio || 1,
|
|
2062
|
-
canvas = new SceneCanvas({
|
|
2063
|
-
width:
|
|
2064
|
-
config.width || Math.ceil(box.width) || (stage ? stage.width() : 0),
|
|
2065
|
-
height:
|
|
2066
|
-
config.height ||
|
|
2067
|
-
Math.ceil(box.height) ||
|
|
2068
|
-
(stage ? stage.height() : 0),
|
|
2069
|
-
pixelRatio: pixelRatio,
|
|
2070
|
-
}),
|
|
2071
|
-
context = canvas.getContext();
|
|
2072
|
-
|
|
2073
|
-
const bufferCanvas = new SceneCanvas({
|
|
2074
|
-
// width and height already multiplied by pixelRatio
|
|
2075
|
-
// so we need to revert that
|
|
2076
|
-
// also increase size by x nd y offset to make sure content fits canvas
|
|
2077
|
-
width: canvas.width / canvas.pixelRatio + Math.abs(x),
|
|
2078
|
-
height: canvas.height / canvas.pixelRatio + Math.abs(y),
|
|
2079
|
-
pixelRatio: canvas.pixelRatio,
|
|
2080
|
-
});
|
|
2081
|
-
|
|
2082
|
-
if (config.imageSmoothingEnabled === false) {
|
|
2083
|
-
context._context.imageSmoothingEnabled = false;
|
|
2084
|
-
}
|
|
2085
|
-
context.save();
|
|
2086
|
-
|
|
2087
|
-
if (x || y) {
|
|
2088
|
-
context.translate(-1 * x, -1 * y);
|
|
2089
|
-
}
|
|
2090
|
-
|
|
2091
|
-
this.drawScene(canvas, undefined, bufferCanvas);
|
|
2092
|
-
context.restore();
|
|
2093
|
-
|
|
2094
|
-
return canvas;
|
|
2095
|
-
}
|
|
2096
|
-
/**
|
|
2097
|
-
* converts node into an canvas element.
|
|
2098
|
-
* @method
|
|
2099
|
-
* @name Konva.Node#toCanvas
|
|
2100
|
-
* @param {Object} config
|
|
2101
|
-
* @param {Function} config.callback function executed when the composite has completed
|
|
2102
|
-
* @param {Number} [config.x] x position of canvas section
|
|
2103
|
-
* @param {Number} [config.y] y position of canvas section
|
|
2104
|
-
* @param {Number} [config.width] width of canvas section
|
|
2105
|
-
* @param {Number} [config.height] height of canvas section
|
|
2106
|
-
* @param {Number} [config.pixelRatio] pixelRatio of output canvas. Default is 1.
|
|
2107
|
-
* You can use that property to increase quality of the image, for example for super hight quality exports
|
|
2108
|
-
* or usage on retina (or similar) displays. pixelRatio will be used to multiply the size of exported image.
|
|
2109
|
-
* If you export to 500x500 size with pixelRatio = 2, then produced image will have size 1000x1000.
|
|
2110
|
-
* @param {Boolean} [config.imageSmoothingEnabled] set this to false if you want to disable imageSmoothing
|
|
2111
|
-
* @example
|
|
2112
|
-
* var canvas = node.toCanvas();
|
|
2113
|
-
*/
|
|
2114
|
-
toCanvas(config?) {
|
|
2115
|
-
return this._toKonvaCanvas(config)._canvas;
|
|
2116
|
-
}
|
|
2117
|
-
/**
|
|
2118
|
-
* Creates a composite data URL (base64 string). If MIME type is not
|
|
2119
|
-
* specified, then "image/png" will result. For "image/jpeg", specify a quality
|
|
2120
|
-
* level as quality (range 0.0 - 1.0)
|
|
2121
|
-
* @method
|
|
2122
|
-
* @name Konva.Node#toDataURL
|
|
2123
|
-
* @param {Object} config
|
|
2124
|
-
* @param {String} [config.mimeType] can be "image/png" or "image/jpeg".
|
|
2125
|
-
* "image/png" is the default
|
|
2126
|
-
* @param {Number} [config.x] x position of canvas section
|
|
2127
|
-
* @param {Number} [config.y] y position of canvas section
|
|
2128
|
-
* @param {Number} [config.width] width of canvas section
|
|
2129
|
-
* @param {Number} [config.height] height of canvas section
|
|
2130
|
-
* @param {Number} [config.quality] jpeg quality. If using an "image/jpeg" mimeType,
|
|
2131
|
-
* you can specify the quality from 0 to 1, where 0 is very poor quality and 1
|
|
2132
|
-
* is very high quality
|
|
2133
|
-
* @param {Number} [config.pixelRatio] pixelRatio of output image url. Default is 1.
|
|
2134
|
-
* You can use that property to increase quality of the image, for example for super hight quality exports
|
|
2135
|
-
* or usage on retina (or similar) displays. pixelRatio will be used to multiply the size of exported image.
|
|
2136
|
-
* If you export to 500x500 size with pixelRatio = 2, then produced image will have size 1000x1000.
|
|
2137
|
-
* @param {Boolean} [config.imageSmoothingEnabled] set this to false if you want to disable imageSmoothing
|
|
2138
|
-
* @returns {String}
|
|
2139
|
-
*/
|
|
2140
|
-
toDataURL(config?: {
|
|
2141
|
-
x?: number;
|
|
2142
|
-
y?: number;
|
|
2143
|
-
width?: number;
|
|
2144
|
-
height?: number;
|
|
2145
|
-
pixelRatio?: number;
|
|
2146
|
-
mimeType?: string;
|
|
2147
|
-
quality?: number;
|
|
2148
|
-
callback?: (str: string) => void;
|
|
2149
|
-
}) {
|
|
2150
|
-
config = config || {};
|
|
2151
|
-
const mimeType = config.mimeType || null,
|
|
2152
|
-
quality = config.quality || null;
|
|
2153
|
-
const url = this._toKonvaCanvas(config).toDataURL(mimeType, quality);
|
|
2154
|
-
if (config.callback) {
|
|
2155
|
-
config.callback(url);
|
|
2156
|
-
}
|
|
2157
|
-
return url;
|
|
2158
|
-
}
|
|
2159
|
-
/**
|
|
2160
|
-
* converts node into an image. Since the toImage
|
|
2161
|
-
* method is asynchronous, the resulting image can only be retrieved from the config callback
|
|
2162
|
-
* or the returned Promise. toImage is most commonly used
|
|
2163
|
-
* to cache complex drawings as an image so that they don't have to constantly be redrawn
|
|
2164
|
-
* @method
|
|
2165
|
-
* @name Konva.Node#toImage
|
|
2166
|
-
* @param {Object} config
|
|
2167
|
-
* @param {Function} [config.callback] function executed when the composite has completed
|
|
2168
|
-
* @param {String} [config.mimeType] can be "image/png" or "image/jpeg".
|
|
2169
|
-
* "image/png" is the default
|
|
2170
|
-
* @param {Number} [config.x] x position of canvas section
|
|
2171
|
-
* @param {Number} [config.y] y position of canvas section
|
|
2172
|
-
* @param {Number} [config.width] width of canvas section
|
|
2173
|
-
* @param {Number} [config.height] height of canvas section
|
|
2174
|
-
* @param {Number} [config.quality] jpeg quality. If using an "image/jpeg" mimeType,
|
|
2175
|
-
* you can specify the quality from 0 to 1, where 0 is very poor quality and 1
|
|
2176
|
-
* is very high quality
|
|
2177
|
-
* @param {Number} [config.pixelRatio] pixelRatio of output image. Default is 1.
|
|
2178
|
-
* You can use that property to increase quality of the image, for example for super hight quality exports
|
|
2179
|
-
* or usage on retina (or similar) displays. pixelRatio will be used to multiply the size of exported image.
|
|
2180
|
-
* If you export to 500x500 size with pixelRatio = 2, then produced image will have size 1000x1000.
|
|
2181
|
-
* @param {Boolean} [config.imageSmoothingEnabled] set this to false if you want to disable imageSmoothing
|
|
2182
|
-
* @return {Promise<Image>}
|
|
2183
|
-
* @example
|
|
2184
|
-
* var image = node.toImage({
|
|
2185
|
-
* callback(img) {
|
|
2186
|
-
* // do stuff with img
|
|
2187
|
-
* }
|
|
2188
|
-
* });
|
|
2189
|
-
*/
|
|
2190
|
-
toImage(config?: {
|
|
2191
|
-
x?: number;
|
|
2192
|
-
y?: number;
|
|
2193
|
-
width?: number;
|
|
2194
|
-
height?: number;
|
|
2195
|
-
pixelRatio?: number;
|
|
2196
|
-
mimeType?: string;
|
|
2197
|
-
quality?: number;
|
|
2198
|
-
callback?: (img: HTMLImageElement) => void;
|
|
2199
|
-
}) {
|
|
2200
|
-
return new Promise<HTMLImageElement>((resolve, reject) => {
|
|
2201
|
-
try {
|
|
2202
|
-
const callback = config?.callback;
|
|
2203
|
-
if (callback) delete config.callback;
|
|
2204
|
-
Util._urlToImage(this.toDataURL(config as any), function (img) {
|
|
2205
|
-
resolve(img);
|
|
2206
|
-
callback?.(img);
|
|
2207
|
-
});
|
|
2208
|
-
} catch (err) {
|
|
2209
|
-
reject(err);
|
|
2210
|
-
}
|
|
2211
|
-
});
|
|
2212
|
-
}
|
|
2213
|
-
/**
|
|
2214
|
-
* Converts node into a blob. Since the toBlob method is asynchronous,
|
|
2215
|
-
* the resulting blob can only be retrieved from the config callback
|
|
2216
|
-
* or the returned Promise.
|
|
2217
|
-
* @method
|
|
2218
|
-
* @name Konva.Node#toBlob
|
|
2219
|
-
* @param {Object} config
|
|
2220
|
-
* @param {Function} [config.callback] function executed when the composite has completed
|
|
2221
|
-
* @param {Number} [config.x] x position of canvas section
|
|
2222
|
-
* @param {Number} [config.y] y position of canvas section
|
|
2223
|
-
* @param {Number} [config.width] width of canvas section
|
|
2224
|
-
* @param {Number} [config.height] height of canvas section
|
|
2225
|
-
* @param {Number} [config.pixelRatio] pixelRatio of output canvas. Default is 1.
|
|
2226
|
-
* You can use that property to increase quality of the image, for example for super hight quality exports
|
|
2227
|
-
* or usage on retina (or similar) displays. pixelRatio will be used to multiply the size of exported image.
|
|
2228
|
-
* If you export to 500x500 size with pixelRatio = 2, then produced image will have size 1000x1000.
|
|
2229
|
-
* @param {Boolean} [config.imageSmoothingEnabled] set this to false if you want to disable imageSmoothing
|
|
2230
|
-
* @example
|
|
2231
|
-
* var blob = await node.toBlob({});
|
|
2232
|
-
* @returns {Promise<Blob>}
|
|
2233
|
-
*/
|
|
2234
|
-
toBlob(config?: {
|
|
2235
|
-
x?: number;
|
|
2236
|
-
y?: number;
|
|
2237
|
-
width?: number;
|
|
2238
|
-
height?: number;
|
|
2239
|
-
pixelRatio?: number;
|
|
2240
|
-
mimeType?: string;
|
|
2241
|
-
quality?: number;
|
|
2242
|
-
callback?: (blob: Blob | null) => void;
|
|
2243
|
-
}) {
|
|
2244
|
-
return new Promise((resolve, reject) => {
|
|
2245
|
-
try {
|
|
2246
|
-
const callback = config?.callback;
|
|
2247
|
-
if (callback) delete config.callback;
|
|
2248
|
-
this.toCanvas(config).toBlob(
|
|
2249
|
-
(blob) => {
|
|
2250
|
-
resolve(blob);
|
|
2251
|
-
callback?.(blob);
|
|
2252
|
-
},
|
|
2253
|
-
config?.mimeType,
|
|
2254
|
-
config?.quality
|
|
2255
|
-
);
|
|
2256
|
-
} catch (err) {
|
|
2257
|
-
reject(err);
|
|
2258
|
-
}
|
|
2259
|
-
});
|
|
2260
|
-
}
|
|
2261
|
-
setSize(size) {
|
|
2262
|
-
this.width(size.width);
|
|
2263
|
-
this.height(size.height);
|
|
2264
|
-
return this;
|
|
2265
|
-
}
|
|
2266
|
-
getSize() {
|
|
2267
|
-
return {
|
|
2268
|
-
width: this.width(),
|
|
2269
|
-
height: this.height(),
|
|
2270
|
-
};
|
|
2271
|
-
}
|
|
2272
|
-
/**
|
|
2273
|
-
* get class name, which may return Stage, Layer, Group, or shape class names like Rect, Circle, Text, etc.
|
|
2274
|
-
* @method
|
|
2275
|
-
* @name Konva.Node#getClassName
|
|
2276
|
-
* @returns {String}
|
|
2277
|
-
*/
|
|
2278
|
-
getClassName() {
|
|
2279
|
-
return this.className || this.nodeType;
|
|
2280
|
-
}
|
|
2281
|
-
/**
|
|
2282
|
-
* get the node type, which may return Stage, Layer, Group, or Shape
|
|
2283
|
-
* @method
|
|
2284
|
-
* @name Konva.Node#getType
|
|
2285
|
-
* @returns {String}
|
|
2286
|
-
*/
|
|
2287
|
-
getType() {
|
|
2288
|
-
return this.nodeType;
|
|
2289
|
-
}
|
|
2290
|
-
getDragDistance(): number {
|
|
2291
|
-
// compare with undefined because we need to track 0 value
|
|
2292
|
-
if (this.attrs.dragDistance !== undefined) {
|
|
2293
|
-
return this.attrs.dragDistance;
|
|
2294
|
-
} else if (this.parent) {
|
|
2295
|
-
return this.parent.getDragDistance();
|
|
2296
|
-
} else {
|
|
2297
|
-
return Konva.dragDistance;
|
|
2298
|
-
}
|
|
2299
|
-
}
|
|
2300
|
-
_off(type, name?, callback?) {
|
|
2301
|
-
let evtListeners = this.eventListeners[type],
|
|
2302
|
-
i,
|
|
2303
|
-
evtName,
|
|
2304
|
-
handler;
|
|
2305
|
-
|
|
2306
|
-
for (i = 0; i < evtListeners.length; i++) {
|
|
2307
|
-
evtName = evtListeners[i].name;
|
|
2308
|
-
handler = evtListeners[i].handler;
|
|
2309
|
-
|
|
2310
|
-
// the following two conditions must be true in order to remove a handler:
|
|
2311
|
-
// 1) the current event name cannot be konva unless the event name is konva
|
|
2312
|
-
// this enables developers to force remove a konva specific listener for whatever reason
|
|
2313
|
-
// 2) an event name is not specified, or if one is specified, it matches the current event name
|
|
2314
|
-
if (
|
|
2315
|
-
(evtName !== 'konva' || name === 'konva') &&
|
|
2316
|
-
(!name || evtName === name) &&
|
|
2317
|
-
(!callback || callback === handler)
|
|
2318
|
-
) {
|
|
2319
|
-
evtListeners.splice(i, 1);
|
|
2320
|
-
if (evtListeners.length === 0) {
|
|
2321
|
-
delete this.eventListeners[type];
|
|
2322
|
-
break;
|
|
2323
|
-
}
|
|
2324
|
-
i--;
|
|
2325
|
-
}
|
|
2326
|
-
}
|
|
2327
|
-
}
|
|
2328
|
-
_fireChangeEvent(attr, oldVal, newVal) {
|
|
2329
|
-
this._fire(attr + CHANGE, {
|
|
2330
|
-
oldVal: oldVal,
|
|
2331
|
-
newVal: newVal,
|
|
2332
|
-
});
|
|
2333
|
-
}
|
|
2334
|
-
/**
|
|
2335
|
-
* add name to node
|
|
2336
|
-
* @method
|
|
2337
|
-
* @name Konva.Node#addName
|
|
2338
|
-
* @param {String} name
|
|
2339
|
-
* @returns {Konva.Node}
|
|
2340
|
-
* @example
|
|
2341
|
-
* node.name('red');
|
|
2342
|
-
* node.addName('selected');
|
|
2343
|
-
* node.name(); // return 'red selected'
|
|
2344
|
-
*/
|
|
2345
|
-
addName(name: string) {
|
|
2346
|
-
if (!this.hasName(name)) {
|
|
2347
|
-
const oldName = this.name();
|
|
2348
|
-
const newName = oldName ? oldName + ' ' + name : name;
|
|
2349
|
-
this.name(newName);
|
|
2350
|
-
}
|
|
2351
|
-
return this;
|
|
2352
|
-
}
|
|
2353
|
-
/**
|
|
2354
|
-
* check is node has name
|
|
2355
|
-
* @method
|
|
2356
|
-
* @name Konva.Node#hasName
|
|
2357
|
-
* @param {String} name
|
|
2358
|
-
* @returns {Boolean}
|
|
2359
|
-
* @example
|
|
2360
|
-
* node.name('red');
|
|
2361
|
-
* node.hasName('red'); // return true
|
|
2362
|
-
* node.hasName('selected'); // return false
|
|
2363
|
-
* node.hasName(''); // return false
|
|
2364
|
-
*/
|
|
2365
|
-
hasName(name) {
|
|
2366
|
-
if (!name) {
|
|
2367
|
-
return false;
|
|
2368
|
-
}
|
|
2369
|
-
const fullName = this.name();
|
|
2370
|
-
if (!fullName) {
|
|
2371
|
-
return false;
|
|
2372
|
-
}
|
|
2373
|
-
// if name is '' the "names" will be [''], so I added extra check above
|
|
2374
|
-
const names = (fullName || '').split(/\s/g);
|
|
2375
|
-
return names.indexOf(name) !== -1;
|
|
2376
|
-
}
|
|
2377
|
-
/**
|
|
2378
|
-
* remove name from node
|
|
2379
|
-
* @method
|
|
2380
|
-
* @name Konva.Node#removeName
|
|
2381
|
-
* @param {String} name
|
|
2382
|
-
* @returns {Konva.Node}
|
|
2383
|
-
* @example
|
|
2384
|
-
* node.name('red selected');
|
|
2385
|
-
* node.removeName('selected');
|
|
2386
|
-
* node.hasName('selected'); // return false
|
|
2387
|
-
* node.name(); // return 'red'
|
|
2388
|
-
*/
|
|
2389
|
-
removeName(name) {
|
|
2390
|
-
const names = (this.name() || '').split(/\s/g);
|
|
2391
|
-
const index = names.indexOf(name);
|
|
2392
|
-
if (index !== -1) {
|
|
2393
|
-
names.splice(index, 1);
|
|
2394
|
-
this.name(names.join(' '));
|
|
2395
|
-
}
|
|
2396
|
-
return this;
|
|
2397
|
-
}
|
|
2398
|
-
/**
|
|
2399
|
-
* set attr
|
|
2400
|
-
* @method
|
|
2401
|
-
* @name Konva.Node#setAttr
|
|
2402
|
-
* @param {String} attr
|
|
2403
|
-
* @param {*} val
|
|
2404
|
-
* @returns {Konva.Node}
|
|
2405
|
-
* @example
|
|
2406
|
-
* node.setAttr('x', 5);
|
|
2407
|
-
*/
|
|
2408
|
-
setAttr(attr: string, val) {
|
|
2409
|
-
const func = this[SET + Util._capitalize(attr)];
|
|
2410
|
-
|
|
2411
|
-
if (Util._isFunction(func)) {
|
|
2412
|
-
func.call(this, val);
|
|
2413
|
-
} else {
|
|
2414
|
-
// otherwise set directly
|
|
2415
|
-
this._setAttr(attr, val);
|
|
2416
|
-
}
|
|
2417
|
-
return this;
|
|
2418
|
-
}
|
|
2419
|
-
_requestDraw() {
|
|
2420
|
-
if (Konva.autoDrawEnabled) {
|
|
2421
|
-
const drawNode = this.getLayer() || this.getStage();
|
|
2422
|
-
drawNode?.batchDraw();
|
|
2423
|
-
}
|
|
2424
|
-
}
|
|
2425
|
-
_setAttr(key: string, val) {
|
|
2426
|
-
const oldVal = this.attrs[key];
|
|
2427
|
-
if (oldVal === val && !Util.isObject(val)) {
|
|
2428
|
-
return;
|
|
2429
|
-
}
|
|
2430
|
-
if (val === undefined || val === null) {
|
|
2431
|
-
delete this.attrs[key];
|
|
2432
|
-
} else {
|
|
2433
|
-
this.attrs[key] = val;
|
|
2434
|
-
}
|
|
2435
|
-
if (this._shouldFireChangeEvents) {
|
|
2436
|
-
this._fireChangeEvent(key, oldVal, val);
|
|
2437
|
-
}
|
|
2438
|
-
this._requestDraw();
|
|
2439
|
-
}
|
|
2440
|
-
_setComponentAttr(key, component, val) {
|
|
2441
|
-
let oldVal;
|
|
2442
|
-
if (val !== undefined) {
|
|
2443
|
-
oldVal = this.attrs[key];
|
|
2444
|
-
|
|
2445
|
-
if (!oldVal) {
|
|
2446
|
-
// set value to default value using getAttr
|
|
2447
|
-
this.attrs[key] = this.getAttr(key);
|
|
2448
|
-
}
|
|
2449
|
-
|
|
2450
|
-
this.attrs[key][component] = val;
|
|
2451
|
-
this._fireChangeEvent(key, oldVal, val);
|
|
2452
|
-
}
|
|
2453
|
-
}
|
|
2454
|
-
_fireAndBubble(eventType, evt, compareShape?) {
|
|
2455
|
-
if (evt && this.nodeType === SHAPE) {
|
|
2456
|
-
evt.target = this;
|
|
2457
|
-
}
|
|
2458
|
-
|
|
2459
|
-
const nonBubbling = [
|
|
2460
|
-
MOUSEENTER,
|
|
2461
|
-
MOUSELEAVE,
|
|
2462
|
-
POINTERENTER,
|
|
2463
|
-
POINTERLEAVE,
|
|
2464
|
-
TOUCHENTER,
|
|
2465
|
-
TOUCHLEAVE,
|
|
2466
|
-
];
|
|
2467
|
-
|
|
2468
|
-
const shouldStop =
|
|
2469
|
-
nonBubbling.indexOf(eventType) !== -1 &&
|
|
2470
|
-
((compareShape &&
|
|
2471
|
-
(this === compareShape ||
|
|
2472
|
-
(this.isAncestorOf && this.isAncestorOf(compareShape)))) ||
|
|
2473
|
-
(this.nodeType === 'Stage' && !compareShape));
|
|
2474
|
-
|
|
2475
|
-
if (!shouldStop) {
|
|
2476
|
-
this._fire(eventType, evt);
|
|
2477
|
-
|
|
2478
|
-
// simulate event bubbling
|
|
2479
|
-
const stopBubble =
|
|
2480
|
-
nonBubbling.indexOf(eventType) !== -1 &&
|
|
2481
|
-
compareShape &&
|
|
2482
|
-
compareShape.isAncestorOf &&
|
|
2483
|
-
compareShape.isAncestorOf(this) &&
|
|
2484
|
-
!compareShape.isAncestorOf(this.parent);
|
|
2485
|
-
if (
|
|
2486
|
-
((evt && !evt.cancelBubble) || !evt) &&
|
|
2487
|
-
this.parent &&
|
|
2488
|
-
this.parent.isListening() &&
|
|
2489
|
-
!stopBubble
|
|
2490
|
-
) {
|
|
2491
|
-
if (compareShape && compareShape.parent) {
|
|
2492
|
-
this._fireAndBubble.call(this.parent, eventType, evt, compareShape);
|
|
2493
|
-
} else {
|
|
2494
|
-
this._fireAndBubble.call(this.parent, eventType, evt);
|
|
2495
|
-
}
|
|
2496
|
-
}
|
|
2497
|
-
}
|
|
2498
|
-
}
|
|
2499
|
-
|
|
2500
|
-
_getProtoListeners(eventType) {
|
|
2501
|
-
const allListeners = this._cache.get(ALL_LISTENERS) ?? {};
|
|
2502
|
-
let events = allListeners?.[eventType];
|
|
2503
|
-
if (events === undefined) {
|
|
2504
|
-
//recalculate cache
|
|
2505
|
-
events = [];
|
|
2506
|
-
let obj = Object.getPrototypeOf(this);
|
|
2507
|
-
while (obj) {
|
|
2508
|
-
const hierarchyEvents = obj.eventListeners?.[eventType] ?? [];
|
|
2509
|
-
events.push(...hierarchyEvents);
|
|
2510
|
-
obj = Object.getPrototypeOf(obj);
|
|
2511
|
-
}
|
|
2512
|
-
// update cache
|
|
2513
|
-
allListeners[eventType] = events;
|
|
2514
|
-
this._cache.set(ALL_LISTENERS, allListeners);
|
|
2515
|
-
}
|
|
2516
|
-
|
|
2517
|
-
return events;
|
|
2518
|
-
}
|
|
2519
|
-
_fire(eventType, evt) {
|
|
2520
|
-
evt = evt || {};
|
|
2521
|
-
evt.currentTarget = this;
|
|
2522
|
-
evt.type = eventType;
|
|
2523
|
-
|
|
2524
|
-
const topListeners = this._getProtoListeners(eventType);
|
|
2525
|
-
if (topListeners) {
|
|
2526
|
-
for (let i = 0; i < topListeners.length; i++) {
|
|
2527
|
-
topListeners[i].handler.call(this, evt);
|
|
2528
|
-
}
|
|
2529
|
-
}
|
|
2530
|
-
|
|
2531
|
-
// it is important to iterate over self listeners without cache
|
|
2532
|
-
// because events can be added/removed while firing
|
|
2533
|
-
const selfListeners = this.eventListeners[eventType];
|
|
2534
|
-
if (selfListeners) {
|
|
2535
|
-
for (let i = 0; i < selfListeners.length; i++) {
|
|
2536
|
-
selfListeners[i].handler.call(this, evt);
|
|
2537
|
-
}
|
|
2538
|
-
}
|
|
2539
|
-
}
|
|
2540
|
-
/**
|
|
2541
|
-
* draw both scene and hit graphs. If the node being drawn is the stage, all of the layers will be cleared and redrawn
|
|
2542
|
-
* @method
|
|
2543
|
-
* @name Konva.Node#draw
|
|
2544
|
-
* @returns {Konva.Node}
|
|
2545
|
-
*/
|
|
2546
|
-
draw() {
|
|
2547
|
-
this.drawScene();
|
|
2548
|
-
this.drawHit();
|
|
2549
|
-
return this;
|
|
2550
|
-
}
|
|
2551
|
-
|
|
2552
|
-
// drag & drop
|
|
2553
|
-
_createDragElement(evt) {
|
|
2554
|
-
const pointerId = evt ? evt.pointerId : undefined;
|
|
2555
|
-
const stage = this.getStage();
|
|
2556
|
-
const ap = this.getAbsolutePosition();
|
|
2557
|
-
if (!stage) {
|
|
2558
|
-
return;
|
|
2559
|
-
}
|
|
2560
|
-
const pos =
|
|
2561
|
-
stage._getPointerById(pointerId) ||
|
|
2562
|
-
stage._changedPointerPositions[0] ||
|
|
2563
|
-
ap;
|
|
2564
|
-
DD._dragElements.set(this._id, {
|
|
2565
|
-
node: this,
|
|
2566
|
-
startPointerPos: pos,
|
|
2567
|
-
offset: {
|
|
2568
|
-
x: pos.x - ap.x,
|
|
2569
|
-
y: pos.y - ap.y,
|
|
2570
|
-
},
|
|
2571
|
-
dragStatus: 'ready',
|
|
2572
|
-
pointerId,
|
|
2573
|
-
});
|
|
2574
|
-
}
|
|
2575
|
-
|
|
2576
|
-
/**
|
|
2577
|
-
* initiate drag and drop.
|
|
2578
|
-
* @method
|
|
2579
|
-
* @name Konva.Node#startDrag
|
|
2580
|
-
*/
|
|
2581
|
-
startDrag(evt?: any, bubbleEvent = true) {
|
|
2582
|
-
if (!DD._dragElements.has(this._id)) {
|
|
2583
|
-
this._createDragElement(evt);
|
|
2584
|
-
}
|
|
2585
|
-
|
|
2586
|
-
const elem = DD._dragElements.get(this._id)!;
|
|
2587
|
-
elem.dragStatus = 'dragging';
|
|
2588
|
-
this.fire(
|
|
2589
|
-
'dragstart',
|
|
2590
|
-
{
|
|
2591
|
-
type: 'dragstart',
|
|
2592
|
-
target: this,
|
|
2593
|
-
evt: evt && evt.evt,
|
|
2594
|
-
},
|
|
2595
|
-
bubbleEvent
|
|
2596
|
-
);
|
|
2597
|
-
}
|
|
2598
|
-
|
|
2599
|
-
_setDragPosition(evt, elem) {
|
|
2600
|
-
// const pointers = this.getStage().getPointersPositions();
|
|
2601
|
-
// const pos = pointers.find(p => p.id === this._dragEventId);
|
|
2602
|
-
const pos = this.getStage()!._getPointerById(elem.pointerId);
|
|
2603
|
-
|
|
2604
|
-
if (!pos) {
|
|
2605
|
-
return;
|
|
2606
|
-
}
|
|
2607
|
-
let newNodePos = {
|
|
2608
|
-
x: pos.x - elem.offset.x,
|
|
2609
|
-
y: pos.y - elem.offset.y,
|
|
2610
|
-
};
|
|
2611
|
-
|
|
2612
|
-
const dbf = this.dragBoundFunc();
|
|
2613
|
-
if (dbf !== undefined) {
|
|
2614
|
-
const bounded = dbf.call(this, newNodePos, evt);
|
|
2615
|
-
if (!bounded) {
|
|
2616
|
-
Util.warn(
|
|
2617
|
-
'dragBoundFunc did not return any value. That is unexpected behavior. You must return new absolute position from dragBoundFunc.'
|
|
2618
|
-
);
|
|
2619
|
-
} else {
|
|
2620
|
-
newNodePos = bounded;
|
|
2621
|
-
}
|
|
2622
|
-
}
|
|
2623
|
-
|
|
2624
|
-
if (
|
|
2625
|
-
!this._lastPos ||
|
|
2626
|
-
this._lastPos.x !== newNodePos.x ||
|
|
2627
|
-
this._lastPos.y !== newNodePos.y
|
|
2628
|
-
) {
|
|
2629
|
-
this.setAbsolutePosition(newNodePos);
|
|
2630
|
-
this._requestDraw();
|
|
2631
|
-
}
|
|
2632
|
-
|
|
2633
|
-
this._lastPos = newNodePos;
|
|
2634
|
-
}
|
|
2635
|
-
|
|
2636
|
-
/**
|
|
2637
|
-
* stop drag and drop
|
|
2638
|
-
* @method
|
|
2639
|
-
* @name Konva.Node#stopDrag
|
|
2640
|
-
*/
|
|
2641
|
-
stopDrag(evt?) {
|
|
2642
|
-
const elem = DD._dragElements.get(this._id);
|
|
2643
|
-
if (elem) {
|
|
2644
|
-
elem.dragStatus = 'stopped';
|
|
2645
|
-
}
|
|
2646
|
-
DD._endDragBefore(evt);
|
|
2647
|
-
DD._endDragAfter(evt);
|
|
2648
|
-
}
|
|
2649
|
-
|
|
2650
|
-
setDraggable(draggable) {
|
|
2651
|
-
this._setAttr('draggable', draggable);
|
|
2652
|
-
this._dragChange();
|
|
2653
|
-
}
|
|
2654
|
-
|
|
2655
|
-
/**
|
|
2656
|
-
* determine if node is currently in drag and drop mode
|
|
2657
|
-
* @method
|
|
2658
|
-
* @name Konva.Node#isDragging
|
|
2659
|
-
*/
|
|
2660
|
-
isDragging() {
|
|
2661
|
-
const elem = DD._dragElements.get(this._id);
|
|
2662
|
-
return elem ? elem.dragStatus === 'dragging' : false;
|
|
2663
|
-
}
|
|
2664
|
-
|
|
2665
|
-
_listenDrag() {
|
|
2666
|
-
this._dragCleanup();
|
|
2667
|
-
|
|
2668
|
-
this.on('mousedown.konva touchstart.konva', function (evt) {
|
|
2669
|
-
const shouldCheckButton = evt.evt['button'] !== undefined;
|
|
2670
|
-
const canDrag =
|
|
2671
|
-
!shouldCheckButton || Konva.dragButtons.indexOf(evt.evt['button']) >= 0;
|
|
2672
|
-
if (!canDrag) {
|
|
2673
|
-
return;
|
|
2674
|
-
}
|
|
2675
|
-
if (this.isDragging()) {
|
|
2676
|
-
return;
|
|
2677
|
-
}
|
|
2678
|
-
|
|
2679
|
-
let hasDraggingChild = false;
|
|
2680
|
-
DD._dragElements.forEach((elem) => {
|
|
2681
|
-
if (this.isAncestorOf(elem.node)) {
|
|
2682
|
-
hasDraggingChild = true;
|
|
2683
|
-
}
|
|
2684
|
-
});
|
|
2685
|
-
// nested drag can be started
|
|
2686
|
-
// in that case we don't need to start new drag
|
|
2687
|
-
if (!hasDraggingChild) {
|
|
2688
|
-
this._createDragElement(evt);
|
|
2689
|
-
}
|
|
2690
|
-
});
|
|
2691
|
-
}
|
|
2692
|
-
|
|
2693
|
-
_dragChange() {
|
|
2694
|
-
if (this.attrs.draggable) {
|
|
2695
|
-
this._listenDrag();
|
|
2696
|
-
} else {
|
|
2697
|
-
// remove event listeners
|
|
2698
|
-
this._dragCleanup();
|
|
2699
|
-
|
|
2700
|
-
/*
|
|
2701
|
-
* force drag and drop to end
|
|
2702
|
-
* if this node is currently in
|
|
2703
|
-
* drag and drop mode
|
|
2704
|
-
*/
|
|
2705
|
-
const stage = this.getStage();
|
|
2706
|
-
if (!stage) {
|
|
2707
|
-
return;
|
|
2708
|
-
}
|
|
2709
|
-
const dragElement = DD._dragElements.get(this._id);
|
|
2710
|
-
const isDragging = dragElement && dragElement.dragStatus === 'dragging';
|
|
2711
|
-
const isReady = dragElement && dragElement.dragStatus === 'ready';
|
|
2712
|
-
|
|
2713
|
-
if (isDragging) {
|
|
2714
|
-
this.stopDrag();
|
|
2715
|
-
} else if (isReady) {
|
|
2716
|
-
DD._dragElements.delete(this._id);
|
|
2717
|
-
}
|
|
2718
|
-
}
|
|
2719
|
-
}
|
|
2720
|
-
|
|
2721
|
-
_dragCleanup() {
|
|
2722
|
-
this.off('mousedown.konva');
|
|
2723
|
-
this.off('touchstart.konva');
|
|
2724
|
-
}
|
|
2725
|
-
|
|
2726
|
-
/**
|
|
2727
|
-
* determine if node (at least partially) is currently in user-visible area
|
|
2728
|
-
* @method
|
|
2729
|
-
* @param {(Number | Object)} margin optional margin in pixels
|
|
2730
|
-
* @param {Number} margin.x
|
|
2731
|
-
* @param {Number} margin.y
|
|
2732
|
-
* @returns {Boolean}
|
|
2733
|
-
* @name Konva.Node#isClientRectOnScreen
|
|
2734
|
-
* @example
|
|
2735
|
-
* // get index
|
|
2736
|
-
* // default calculations
|
|
2737
|
-
* var isOnScreen = node.isClientRectOnScreen()
|
|
2738
|
-
* // increase object size (or screen size) for cases when objects close to the screen still need to be marked as "visible"
|
|
2739
|
-
* var isOnScreen = node.isClientRectOnScreen({ x: stage.width(), y: stage.height() })
|
|
2740
|
-
*/
|
|
2741
|
-
isClientRectOnScreen(
|
|
2742
|
-
margin: { x: number; y: number } = { x: 0, y: 0 }
|
|
2743
|
-
): boolean {
|
|
2744
|
-
const stage = this.getStage();
|
|
2745
|
-
if (!stage) {
|
|
2746
|
-
return false;
|
|
2747
|
-
}
|
|
2748
|
-
const screenRect = {
|
|
2749
|
-
x: -margin.x,
|
|
2750
|
-
y: -margin.y,
|
|
2751
|
-
width: stage.width() + 2 * margin.x,
|
|
2752
|
-
height: stage.height() + 2 * margin.y,
|
|
2753
|
-
};
|
|
2754
|
-
return Util.haveIntersection(screenRect, this.getClientRect());
|
|
2755
|
-
}
|
|
2756
|
-
|
|
2757
|
-
// @ts-ignore:
|
|
2758
|
-
preventDefault: GetSet<boolean, this>;
|
|
2759
|
-
|
|
2760
|
-
// from filters
|
|
2761
|
-
blue: GetSet<number, this>;
|
|
2762
|
-
brightness: GetSet<number, this>;
|
|
2763
|
-
contrast: GetSet<number, this>;
|
|
2764
|
-
blurRadius: GetSet<number, this>;
|
|
2765
|
-
luminance: GetSet<number, this>;
|
|
2766
|
-
green: GetSet<number, this>;
|
|
2767
|
-
alpha: GetSet<number, this>;
|
|
2768
|
-
hue: GetSet<number, this>;
|
|
2769
|
-
kaleidoscopeAngle: GetSet<number, this>;
|
|
2770
|
-
kaleidoscopePower: GetSet<number, this>;
|
|
2771
|
-
levels: GetSet<number, this>;
|
|
2772
|
-
noise: GetSet<number, this>;
|
|
2773
|
-
pixelSize: GetSet<number, this>;
|
|
2774
|
-
red: GetSet<number, this>;
|
|
2775
|
-
saturation: GetSet<number, this>;
|
|
2776
|
-
threshold: GetSet<number, this>;
|
|
2777
|
-
value: GetSet<number, this>;
|
|
2778
|
-
|
|
2779
|
-
dragBoundFunc: GetSet<
|
|
2780
|
-
(this: Node, pos: Vector2d, event: any) => Vector2d,
|
|
2781
|
-
this
|
|
2782
|
-
>;
|
|
2783
|
-
draggable: GetSet<boolean, this>;
|
|
2784
|
-
dragDistance: GetSet<number, this>;
|
|
2785
|
-
embossBlend: GetSet<boolean, this>;
|
|
2786
|
-
embossDirection: GetSet<string, this>;
|
|
2787
|
-
embossStrength: GetSet<number, this>;
|
|
2788
|
-
embossWhiteLevel: GetSet<number, this>;
|
|
2789
|
-
enhance: GetSet<number, this>;
|
|
2790
|
-
filters: GetSet<Filters, this>;
|
|
2791
|
-
position: GetSet<Vector2d, this>;
|
|
2792
|
-
absolutePosition: GetSet<Vector2d, this>;
|
|
2793
|
-
size: GetSet<{ width: number; height: number }, this>;
|
|
2794
|
-
|
|
2795
|
-
id: GetSet<string, this>;
|
|
2796
|
-
|
|
2797
|
-
listening: GetSet<boolean, this>;
|
|
2798
|
-
name: GetSet<string, this>;
|
|
2799
|
-
offset: GetSet<Vector2d, this>;
|
|
2800
|
-
offsetX: GetSet<number, this>;
|
|
2801
|
-
offsetY: GetSet<number, this>;
|
|
2802
|
-
opacity: GetSet<number, this>;
|
|
2803
|
-
|
|
2804
|
-
rotation: GetSet<number, this>;
|
|
2805
|
-
zIndex: GetSet<number, this>;
|
|
2806
|
-
|
|
2807
|
-
scale: GetSet<Vector2d, this>;
|
|
2808
|
-
scaleX: GetSet<number, this>;
|
|
2809
|
-
scaleY: GetSet<number, this>;
|
|
2810
|
-
skew: GetSet<Vector2d, this>;
|
|
2811
|
-
skewX: GetSet<number, this>;
|
|
2812
|
-
skewY: GetSet<number, this>;
|
|
2813
|
-
|
|
2814
|
-
to: (params: AnimTo) => void;
|
|
2815
|
-
|
|
2816
|
-
transformsEnabled: GetSet<string, this>;
|
|
2817
|
-
|
|
2818
|
-
visible: GetSet<boolean, this>;
|
|
2819
|
-
width: GetSet<number, this>;
|
|
2820
|
-
height: GetSet<number, this>;
|
|
2821
|
-
|
|
2822
|
-
x: GetSet<number, this>;
|
|
2823
|
-
y: GetSet<number, this>;
|
|
2824
|
-
globalCompositeOperation: GetSet<globalCompositeOperationType, this>;
|
|
2825
|
-
|
|
2826
|
-
/**
|
|
2827
|
-
* create node with JSON string or an Object. De-serializtion does not generate custom
|
|
2828
|
-
* shape drawing functions, images, or event handlers (this would make the
|
|
2829
|
-
* serialized object huge). If your app uses custom shapes, images, and
|
|
2830
|
-
* event handlers (it probably does), then you need to select the appropriate
|
|
2831
|
-
* shapes after loading the stage and set these properties via on(), setSceneFunc(),
|
|
2832
|
-
* and setImage() methods
|
|
2833
|
-
* @method
|
|
2834
|
-
* @memberof Konva.Node
|
|
2835
|
-
* @param {String|Object} json string or object
|
|
2836
|
-
* @param {Element} [container] optional container dom element used only if you're
|
|
2837
|
-
* creating a stage node
|
|
2838
|
-
*/
|
|
2839
|
-
static create(data, container?) {
|
|
2840
|
-
if (Util._isString(data)) {
|
|
2841
|
-
data = JSON.parse(data);
|
|
2842
|
-
}
|
|
2843
|
-
return this._createNode(data, container);
|
|
2844
|
-
}
|
|
2845
|
-
|
|
2846
|
-
static _createNode(obj, container?) {
|
|
2847
|
-
let className = Node.prototype.getClassName.call(obj),
|
|
2848
|
-
children = obj.children,
|
|
2849
|
-
no,
|
|
2850
|
-
len,
|
|
2851
|
-
n;
|
|
2852
|
-
|
|
2853
|
-
// if container was passed in, add it to attrs
|
|
2854
|
-
if (container) {
|
|
2855
|
-
obj.attrs.container = container;
|
|
2856
|
-
}
|
|
2857
|
-
|
|
2858
|
-
if (!Konva[className]) {
|
|
2859
|
-
Util.warn(
|
|
2860
|
-
'Can not find a node with class name "' +
|
|
2861
|
-
className +
|
|
2862
|
-
'". Fallback to "Shape".'
|
|
2863
|
-
);
|
|
2864
|
-
className = 'Shape';
|
|
2865
|
-
}
|
|
2866
|
-
|
|
2867
|
-
const Class = Konva[className];
|
|
2868
|
-
|
|
2869
|
-
no = new Class(obj.attrs);
|
|
2870
|
-
if (children) {
|
|
2871
|
-
len = children.length;
|
|
2872
|
-
for (n = 0; n < len; n++) {
|
|
2873
|
-
no.add(Node._createNode(children[n]));
|
|
2874
|
-
}
|
|
2875
|
-
}
|
|
2876
|
-
|
|
2877
|
-
return no;
|
|
2878
|
-
}
|
|
2879
|
-
}
|
|
2880
|
-
|
|
2881
|
-
interface AnimTo extends NodeConfig {
|
|
2882
|
-
onFinish?: Function;
|
|
2883
|
-
onUpdate?: Function;
|
|
2884
|
-
duration?: number;
|
|
2885
|
-
}
|
|
2886
|
-
|
|
2887
|
-
Node.prototype.nodeType = 'Node';
|
|
2888
|
-
Node.prototype._attrsAffectingSize = [];
|
|
2889
|
-
|
|
2890
|
-
// attache events listeners once into prototype
|
|
2891
|
-
// that way we don't spend too much time on making an new instance
|
|
2892
|
-
Node.prototype.eventListeners = {};
|
|
2893
|
-
Node.prototype.on.call(Node.prototype, TRANSFORM_CHANGE_STR, function () {
|
|
2894
|
-
if (this._batchingTransformChange) {
|
|
2895
|
-
this._needClearTransformCache = true;
|
|
2896
|
-
return;
|
|
2897
|
-
}
|
|
2898
|
-
this._clearCache(TRANSFORM);
|
|
2899
|
-
this._clearSelfAndDescendantCache(ABSOLUTE_TRANSFORM);
|
|
2900
|
-
});
|
|
2901
|
-
|
|
2902
|
-
Node.prototype.on.call(Node.prototype, 'visibleChange.konva', function () {
|
|
2903
|
-
this._clearSelfAndDescendantCache(VISIBLE);
|
|
2904
|
-
});
|
|
2905
|
-
Node.prototype.on.call(Node.prototype, 'listeningChange.konva', function () {
|
|
2906
|
-
this._clearSelfAndDescendantCache(LISTENING);
|
|
2907
|
-
});
|
|
2908
|
-
Node.prototype.on.call(Node.prototype, 'opacityChange.konva', function () {
|
|
2909
|
-
this._clearSelfAndDescendantCache(ABSOLUTE_OPACITY);
|
|
2910
|
-
});
|
|
2911
|
-
|
|
2912
|
-
const addGetterSetter = Factory.addGetterSetter;
|
|
2913
|
-
|
|
2914
|
-
/**
|
|
2915
|
-
* get/set zIndex relative to the node's siblings who share the same parent.
|
|
2916
|
-
* Please remember that zIndex is not absolute (like in CSS). It is relative to parent element only.
|
|
2917
|
-
* @name Konva.Node#zIndex
|
|
2918
|
-
* @method
|
|
2919
|
-
* @param {Number} index
|
|
2920
|
-
* @returns {Number}
|
|
2921
|
-
* @example
|
|
2922
|
-
* // get index
|
|
2923
|
-
* var index = node.zIndex();
|
|
2924
|
-
*
|
|
2925
|
-
* // set index
|
|
2926
|
-
* node.zIndex(2);
|
|
2927
|
-
*/
|
|
2928
|
-
addGetterSetter(Node, 'zIndex');
|
|
2929
|
-
|
|
2930
|
-
/**
|
|
2931
|
-
* get/set node absolute position
|
|
2932
|
-
* @name Konva.Node#absolutePosition
|
|
2933
|
-
* @method
|
|
2934
|
-
* @param {Object} pos
|
|
2935
|
-
* @param {Number} pos.x
|
|
2936
|
-
* @param {Number} pos.y
|
|
2937
|
-
* @returns {Object}
|
|
2938
|
-
* @example
|
|
2939
|
-
* // get position
|
|
2940
|
-
* var position = node.absolutePosition();
|
|
2941
|
-
*
|
|
2942
|
-
* // set position
|
|
2943
|
-
* node.absolutePosition({
|
|
2944
|
-
* x: 5,
|
|
2945
|
-
* y: 10
|
|
2946
|
-
* });
|
|
2947
|
-
*/
|
|
2948
|
-
addGetterSetter(Node, 'absolutePosition');
|
|
2949
|
-
|
|
2950
|
-
addGetterSetter(Node, 'position');
|
|
2951
|
-
/**
|
|
2952
|
-
* get/set node position relative to parent
|
|
2953
|
-
* @name Konva.Node#position
|
|
2954
|
-
* @method
|
|
2955
|
-
* @param {Object} pos
|
|
2956
|
-
* @param {Number} pos.x
|
|
2957
|
-
* @param {Number} pos.y
|
|
2958
|
-
* @returns {Object}
|
|
2959
|
-
* @example
|
|
2960
|
-
* // get position
|
|
2961
|
-
* var position = node.position();
|
|
2962
|
-
*
|
|
2963
|
-
* // set position
|
|
2964
|
-
* node.position({
|
|
2965
|
-
* x: 5,
|
|
2966
|
-
* y: 10
|
|
2967
|
-
* });
|
|
2968
|
-
*/
|
|
2969
|
-
|
|
2970
|
-
addGetterSetter(Node, 'x', 0, getNumberValidator());
|
|
2971
|
-
|
|
2972
|
-
/**
|
|
2973
|
-
* get/set x position
|
|
2974
|
-
* @name Konva.Node#x
|
|
2975
|
-
* @method
|
|
2976
|
-
* @param {Number} x
|
|
2977
|
-
* @returns {Object}
|
|
2978
|
-
* @example
|
|
2979
|
-
* // get x
|
|
2980
|
-
* var x = node.x();
|
|
2981
|
-
*
|
|
2982
|
-
* // set x
|
|
2983
|
-
* node.x(5);
|
|
2984
|
-
*/
|
|
2985
|
-
|
|
2986
|
-
addGetterSetter(Node, 'y', 0, getNumberValidator());
|
|
2987
|
-
|
|
2988
|
-
/**
|
|
2989
|
-
* get/set y position
|
|
2990
|
-
* @name Konva.Node#y
|
|
2991
|
-
* @method
|
|
2992
|
-
* @param {Number} y
|
|
2993
|
-
* @returns {Integer}
|
|
2994
|
-
* @example
|
|
2995
|
-
* // get y
|
|
2996
|
-
* var y = node.y();
|
|
2997
|
-
*
|
|
2998
|
-
* // set y
|
|
2999
|
-
* node.y(5);
|
|
3000
|
-
*/
|
|
3001
|
-
|
|
3002
|
-
addGetterSetter(
|
|
3003
|
-
Node,
|
|
3004
|
-
'globalCompositeOperation',
|
|
3005
|
-
'source-over',
|
|
3006
|
-
getStringValidator()
|
|
3007
|
-
);
|
|
3008
|
-
|
|
3009
|
-
/**
|
|
3010
|
-
* get/set globalCompositeOperation of a node. globalCompositeOperation DOESN'T affect hit graph of nodes. So they are still trigger to events as they have default "source-over" globalCompositeOperation.
|
|
3011
|
-
* @name Konva.Node#globalCompositeOperation
|
|
3012
|
-
* @method
|
|
3013
|
-
* @param {String} type
|
|
3014
|
-
* @returns {String}
|
|
3015
|
-
* @example
|
|
3016
|
-
* // get globalCompositeOperation
|
|
3017
|
-
* var globalCompositeOperation = shape.globalCompositeOperation();
|
|
3018
|
-
*
|
|
3019
|
-
* // set globalCompositeOperation
|
|
3020
|
-
* shape.globalCompositeOperation('source-in');
|
|
3021
|
-
*/
|
|
3022
|
-
addGetterSetter(Node, 'opacity', 1, getNumberValidator());
|
|
3023
|
-
|
|
3024
|
-
/**
|
|
3025
|
-
* get/set opacity. Opacity values range from 0 to 1.
|
|
3026
|
-
* A node with an opacity of 0 is fully transparent, and a node
|
|
3027
|
-
* with an opacity of 1 is fully opaque
|
|
3028
|
-
* @name Konva.Node#opacity
|
|
3029
|
-
* @method
|
|
3030
|
-
* @param {Object} opacity
|
|
3031
|
-
* @returns {Number}
|
|
3032
|
-
* @example
|
|
3033
|
-
* // get opacity
|
|
3034
|
-
* var opacity = node.opacity();
|
|
3035
|
-
*
|
|
3036
|
-
* // set opacity
|
|
3037
|
-
* node.opacity(0.5);
|
|
3038
|
-
*/
|
|
3039
|
-
|
|
3040
|
-
addGetterSetter(Node, 'name', '', getStringValidator());
|
|
3041
|
-
|
|
3042
|
-
/**
|
|
3043
|
-
* get/set name.
|
|
3044
|
-
* @name Konva.Node#name
|
|
3045
|
-
* @method
|
|
3046
|
-
* @param {String} name
|
|
3047
|
-
* @returns {String}
|
|
3048
|
-
* @example
|
|
3049
|
-
* // get name
|
|
3050
|
-
* var name = node.name();
|
|
3051
|
-
*
|
|
3052
|
-
* // set name
|
|
3053
|
-
* node.name('foo');
|
|
3054
|
-
*
|
|
3055
|
-
* // also node may have multiple names (as css classes)
|
|
3056
|
-
* node.name('foo bar');
|
|
3057
|
-
*/
|
|
3058
|
-
|
|
3059
|
-
addGetterSetter(Node, 'id', '', getStringValidator());
|
|
3060
|
-
|
|
3061
|
-
/**
|
|
3062
|
-
* get/set id. Id is global for whole page.
|
|
3063
|
-
* @name Konva.Node#id
|
|
3064
|
-
* @method
|
|
3065
|
-
* @param {String} id
|
|
3066
|
-
* @returns {String}
|
|
3067
|
-
* @example
|
|
3068
|
-
* // get id
|
|
3069
|
-
* var name = node.id();
|
|
3070
|
-
*
|
|
3071
|
-
* // set id
|
|
3072
|
-
* node.id('foo');
|
|
3073
|
-
*/
|
|
3074
|
-
|
|
3075
|
-
addGetterSetter(Node, 'rotation', 0, getNumberValidator());
|
|
3076
|
-
|
|
3077
|
-
/**
|
|
3078
|
-
* get/set rotation in degrees
|
|
3079
|
-
* @name Konva.Node#rotation
|
|
3080
|
-
* @method
|
|
3081
|
-
* @param {Number} rotation
|
|
3082
|
-
* @returns {Number}
|
|
3083
|
-
* @example
|
|
3084
|
-
* // get rotation in degrees
|
|
3085
|
-
* var rotation = node.rotation();
|
|
3086
|
-
*
|
|
3087
|
-
* // set rotation in degrees
|
|
3088
|
-
* node.rotation(45);
|
|
3089
|
-
*/
|
|
3090
|
-
|
|
3091
|
-
Factory.addComponentsGetterSetter(Node, 'scale', ['x', 'y']);
|
|
3092
|
-
|
|
3093
|
-
/**
|
|
3094
|
-
* get/set scale
|
|
3095
|
-
* @name Konva.Node#scale
|
|
3096
|
-
* @param {Object} scale
|
|
3097
|
-
* @param {Number} scale.x
|
|
3098
|
-
* @param {Number} scale.y
|
|
3099
|
-
* @method
|
|
3100
|
-
* @returns {Object}
|
|
3101
|
-
* @example
|
|
3102
|
-
* // get scale
|
|
3103
|
-
* var scale = node.scale();
|
|
3104
|
-
*
|
|
3105
|
-
* // set scale
|
|
3106
|
-
* shape.scale({
|
|
3107
|
-
* x: 2,
|
|
3108
|
-
* y: 3
|
|
3109
|
-
* });
|
|
3110
|
-
*/
|
|
3111
|
-
|
|
3112
|
-
addGetterSetter(Node, 'scaleX', 1, getNumberValidator());
|
|
3113
|
-
|
|
3114
|
-
/**
|
|
3115
|
-
* get/set scale x
|
|
3116
|
-
* @name Konva.Node#scaleX
|
|
3117
|
-
* @param {Number} x
|
|
3118
|
-
* @method
|
|
3119
|
-
* @returns {Number}
|
|
3120
|
-
* @example
|
|
3121
|
-
* // get scale x
|
|
3122
|
-
* var scaleX = node.scaleX();
|
|
3123
|
-
*
|
|
3124
|
-
* // set scale x
|
|
3125
|
-
* node.scaleX(2);
|
|
3126
|
-
*/
|
|
3127
|
-
|
|
3128
|
-
addGetterSetter(Node, 'scaleY', 1, getNumberValidator());
|
|
3129
|
-
|
|
3130
|
-
/**
|
|
3131
|
-
* get/set scale y
|
|
3132
|
-
* @name Konva.Node#scaleY
|
|
3133
|
-
* @param {Number} y
|
|
3134
|
-
* @method
|
|
3135
|
-
* @returns {Number}
|
|
3136
|
-
* @example
|
|
3137
|
-
* // get scale y
|
|
3138
|
-
* var scaleY = node.scaleY();
|
|
3139
|
-
*
|
|
3140
|
-
* // set scale y
|
|
3141
|
-
* node.scaleY(2);
|
|
3142
|
-
*/
|
|
3143
|
-
|
|
3144
|
-
Factory.addComponentsGetterSetter(Node, 'skew', ['x', 'y']);
|
|
3145
|
-
|
|
3146
|
-
/**
|
|
3147
|
-
* get/set skew
|
|
3148
|
-
* @name Konva.Node#skew
|
|
3149
|
-
* @param {Object} skew
|
|
3150
|
-
* @param {Number} skew.x
|
|
3151
|
-
* @param {Number} skew.y
|
|
3152
|
-
* @method
|
|
3153
|
-
* @returns {Object}
|
|
3154
|
-
* @example
|
|
3155
|
-
* // get skew
|
|
3156
|
-
* var skew = node.skew();
|
|
3157
|
-
*
|
|
3158
|
-
* // set skew
|
|
3159
|
-
* node.skew({
|
|
3160
|
-
* x: 20,
|
|
3161
|
-
* y: 10
|
|
3162
|
-
* });
|
|
3163
|
-
*/
|
|
3164
|
-
|
|
3165
|
-
addGetterSetter(Node, 'skewX', 0, getNumberValidator());
|
|
3166
|
-
|
|
3167
|
-
/**
|
|
3168
|
-
* get/set skew x
|
|
3169
|
-
* @name Konva.Node#skewX
|
|
3170
|
-
* @param {Number} x
|
|
3171
|
-
* @method
|
|
3172
|
-
* @returns {Number}
|
|
3173
|
-
* @example
|
|
3174
|
-
* // get skew x
|
|
3175
|
-
* var skewX = node.skewX();
|
|
3176
|
-
*
|
|
3177
|
-
* // set skew x
|
|
3178
|
-
* node.skewX(3);
|
|
3179
|
-
*/
|
|
3180
|
-
|
|
3181
|
-
addGetterSetter(Node, 'skewY', 0, getNumberValidator());
|
|
3182
|
-
|
|
3183
|
-
/**
|
|
3184
|
-
* get/set skew y
|
|
3185
|
-
* @name Konva.Node#skewY
|
|
3186
|
-
* @param {Number} y
|
|
3187
|
-
* @method
|
|
3188
|
-
* @returns {Number}
|
|
3189
|
-
* @example
|
|
3190
|
-
* // get skew y
|
|
3191
|
-
* var skewY = node.skewY();
|
|
3192
|
-
*
|
|
3193
|
-
* // set skew y
|
|
3194
|
-
* node.skewY(3);
|
|
3195
|
-
*/
|
|
3196
|
-
|
|
3197
|
-
Factory.addComponentsGetterSetter(Node, 'offset', ['x', 'y']);
|
|
3198
|
-
|
|
3199
|
-
/**
|
|
3200
|
-
* get/set offset. Offsets the default position and rotation point
|
|
3201
|
-
* @method
|
|
3202
|
-
* @param {Object} offset
|
|
3203
|
-
* @param {Number} offset.x
|
|
3204
|
-
* @param {Number} offset.y
|
|
3205
|
-
* @returns {Object}
|
|
3206
|
-
* @example
|
|
3207
|
-
* // get offset
|
|
3208
|
-
* var offset = node.offset();
|
|
3209
|
-
*
|
|
3210
|
-
* // set offset
|
|
3211
|
-
* node.offset({
|
|
3212
|
-
* x: 20,
|
|
3213
|
-
* y: 10
|
|
3214
|
-
* });
|
|
3215
|
-
*/
|
|
3216
|
-
|
|
3217
|
-
addGetterSetter(Node, 'offsetX', 0, getNumberValidator());
|
|
3218
|
-
|
|
3219
|
-
/**
|
|
3220
|
-
* get/set offset x
|
|
3221
|
-
* @name Konva.Node#offsetX
|
|
3222
|
-
* @method
|
|
3223
|
-
* @param {Number} x
|
|
3224
|
-
* @returns {Number}
|
|
3225
|
-
* @example
|
|
3226
|
-
* // get offset x
|
|
3227
|
-
* var offsetX = node.offsetX();
|
|
3228
|
-
*
|
|
3229
|
-
* // set offset x
|
|
3230
|
-
* node.offsetX(3);
|
|
3231
|
-
*/
|
|
3232
|
-
|
|
3233
|
-
addGetterSetter(Node, 'offsetY', 0, getNumberValidator());
|
|
3234
|
-
|
|
3235
|
-
/**
|
|
3236
|
-
* get/set offset y
|
|
3237
|
-
* @name Konva.Node#offsetY
|
|
3238
|
-
* @method
|
|
3239
|
-
* @param {Number} y
|
|
3240
|
-
* @returns {Number}
|
|
3241
|
-
* @example
|
|
3242
|
-
* // get offset y
|
|
3243
|
-
* var offsetY = node.offsetY();
|
|
3244
|
-
*
|
|
3245
|
-
* // set offset y
|
|
3246
|
-
* node.offsetY(3);
|
|
3247
|
-
*/
|
|
3248
|
-
|
|
3249
|
-
addGetterSetter(Node, 'dragDistance', undefined, getNumberValidator());
|
|
3250
|
-
|
|
3251
|
-
/**
|
|
3252
|
-
* get/set drag distance
|
|
3253
|
-
* @name Konva.Node#dragDistance
|
|
3254
|
-
* @method
|
|
3255
|
-
* @param {Number} distance
|
|
3256
|
-
* @returns {Number}
|
|
3257
|
-
* @example
|
|
3258
|
-
* // get drag distance
|
|
3259
|
-
* var dragDistance = node.dragDistance();
|
|
3260
|
-
*
|
|
3261
|
-
* // set distance
|
|
3262
|
-
* // node starts dragging only if pointer moved more then 3 pixels
|
|
3263
|
-
* node.dragDistance(3);
|
|
3264
|
-
* // or set globally
|
|
3265
|
-
* Konva.dragDistance = 3;
|
|
3266
|
-
*/
|
|
3267
|
-
|
|
3268
|
-
addGetterSetter(Node, 'width', 0, getNumberValidator());
|
|
3269
|
-
/**
|
|
3270
|
-
* get/set width
|
|
3271
|
-
* @name Konva.Node#width
|
|
3272
|
-
* @method
|
|
3273
|
-
* @param {Number} width
|
|
3274
|
-
* @returns {Number}
|
|
3275
|
-
* @example
|
|
3276
|
-
* // get width
|
|
3277
|
-
* var width = node.width();
|
|
3278
|
-
*
|
|
3279
|
-
* // set width
|
|
3280
|
-
* node.width(100);
|
|
3281
|
-
*/
|
|
3282
|
-
|
|
3283
|
-
addGetterSetter(Node, 'height', 0, getNumberValidator());
|
|
3284
|
-
/**
|
|
3285
|
-
* get/set height
|
|
3286
|
-
* @name Konva.Node#height
|
|
3287
|
-
* @method
|
|
3288
|
-
* @param {Number} height
|
|
3289
|
-
* @returns {Number}
|
|
3290
|
-
* @example
|
|
3291
|
-
* // get height
|
|
3292
|
-
* var height = node.height();
|
|
3293
|
-
*
|
|
3294
|
-
* // set height
|
|
3295
|
-
* node.height(100);
|
|
3296
|
-
*/
|
|
3297
|
-
|
|
3298
|
-
addGetterSetter(Node, 'listening', true, getBooleanValidator());
|
|
3299
|
-
/**
|
|
3300
|
-
* get/set listening attr. If you need to determine if a node is listening or not
|
|
3301
|
-
* by taking into account its parents, use the isListening() method
|
|
3302
|
-
* nodes with listening set to false will not be detected in hit graph
|
|
3303
|
-
* so they will be ignored in container.getIntersection() method
|
|
3304
|
-
* @name Konva.Node#listening
|
|
3305
|
-
* @method
|
|
3306
|
-
* @param {Boolean} listening Can be true, or false. The default is true.
|
|
3307
|
-
* @returns {Boolean}
|
|
3308
|
-
* @example
|
|
3309
|
-
* // get listening attr
|
|
3310
|
-
* var listening = node.listening();
|
|
3311
|
-
*
|
|
3312
|
-
* // stop listening for events, remove node and all its children from hit graph
|
|
3313
|
-
* node.listening(false);
|
|
3314
|
-
*
|
|
3315
|
-
* // listen to events according to the parent
|
|
3316
|
-
* node.listening(true);
|
|
3317
|
-
*/
|
|
3318
|
-
|
|
3319
|
-
/**
|
|
3320
|
-
* get/set preventDefault
|
|
3321
|
-
* By default all shapes will prevent default behavior
|
|
3322
|
-
* of a browser on a pointer move or tap.
|
|
3323
|
-
* that will prevent native scrolling when you are trying to drag&drop a node
|
|
3324
|
-
* but sometimes you may need to enable default actions
|
|
3325
|
-
* in that case you can set the property to false
|
|
3326
|
-
* @name Konva.Node#preventDefault
|
|
3327
|
-
* @method
|
|
3328
|
-
* @param {Boolean} preventDefault
|
|
3329
|
-
* @returns {Boolean}
|
|
3330
|
-
* @example
|
|
3331
|
-
* // get preventDefault
|
|
3332
|
-
* var shouldPrevent = shape.preventDefault();
|
|
3333
|
-
*
|
|
3334
|
-
* // set preventDefault
|
|
3335
|
-
* shape.preventDefault(false);
|
|
3336
|
-
*/
|
|
3337
|
-
|
|
3338
|
-
addGetterSetter(Node, 'preventDefault', true, getBooleanValidator());
|
|
3339
|
-
|
|
3340
|
-
addGetterSetter(Node, 'filters', undefined, function (this: Node, val) {
|
|
3341
|
-
this._filterUpToDate = false;
|
|
3342
|
-
return val;
|
|
3343
|
-
});
|
|
3344
|
-
/**
|
|
3345
|
-
* get/set filters. Supports function filters, CSS filter strings, or mixed arrays.
|
|
3346
|
-
* CSS filters are applied using native browser performance when possible, function filters use ImageData manipulation.
|
|
3347
|
-
* CSS filters automatically fall back to function filters in unsupported browsers.
|
|
3348
|
-
* @name Konva.Node#filters
|
|
3349
|
-
* @method
|
|
3350
|
-
* @param {Array} filters array of filter functions and/or CSS filter strings
|
|
3351
|
-
* @returns {Array}
|
|
3352
|
-
* @example
|
|
3353
|
-
* // get filters
|
|
3354
|
-
* var filters = node.filters();
|
|
3355
|
-
*
|
|
3356
|
-
* // set CSS filters only (no caching required, uses native performance)
|
|
3357
|
-
* node.filters(['blur(5px)', 'brightness(1.2)', 'contrast(1.5)']);
|
|
3358
|
-
*
|
|
3359
|
-
* // set function filters only (caching required)
|
|
3360
|
-
* node.cache();
|
|
3361
|
-
* node.filters([Konva.Filters.Blur, Konva.Filters.Sepia, Konva.Filters.Invert]);
|
|
3362
|
-
*
|
|
3363
|
-
* // mix CSS and function filters (caching required, uses fallback strategy)
|
|
3364
|
-
* node.cache();
|
|
3365
|
-
* node.filters([
|
|
3366
|
-
* 'blur(3px)', // CSS filter
|
|
3367
|
-
* Konva.Filters.Invert, // function filter
|
|
3368
|
-
* 'brightness(1.5)' // CSS filter
|
|
3369
|
-
* ]);
|
|
3370
|
-
*/
|
|
3371
|
-
|
|
3372
|
-
addGetterSetter(Node, 'visible', true, getBooleanValidator());
|
|
3373
|
-
/**
|
|
3374
|
-
* get/set visible attr. Can be true, or false. The default is true.
|
|
3375
|
-
* If you need to determine if a node is visible or not
|
|
3376
|
-
* by taking into account its parents, use the isVisible() method
|
|
3377
|
-
* @name Konva.Node#visible
|
|
3378
|
-
* @method
|
|
3379
|
-
* @param {Boolean} visible
|
|
3380
|
-
* @returns {Boolean}
|
|
3381
|
-
* @example
|
|
3382
|
-
* // get visible attr
|
|
3383
|
-
* var visible = node.visible();
|
|
3384
|
-
*
|
|
3385
|
-
* // make invisible
|
|
3386
|
-
* node.visible(false);
|
|
3387
|
-
*
|
|
3388
|
-
* // make visible (according to the parent)
|
|
3389
|
-
* node.visible(true);
|
|
3390
|
-
*
|
|
3391
|
-
*/
|
|
3392
|
-
|
|
3393
|
-
addGetterSetter(Node, 'transformsEnabled', 'all', getStringValidator());
|
|
3394
|
-
|
|
3395
|
-
/**
|
|
3396
|
-
* get/set transforms that are enabled. Can be "all", "none", or "position". The default
|
|
3397
|
-
* is "all"
|
|
3398
|
-
* @name Konva.Node#transformsEnabled
|
|
3399
|
-
* @method
|
|
3400
|
-
* @param {String} enabled
|
|
3401
|
-
* @returns {String}
|
|
3402
|
-
* @example
|
|
3403
|
-
* // enable position transform only to improve draw performance
|
|
3404
|
-
* node.transformsEnabled('position');
|
|
3405
|
-
*
|
|
3406
|
-
* // enable all transforms
|
|
3407
|
-
* node.transformsEnabled('all');
|
|
3408
|
-
*/
|
|
3409
|
-
|
|
3410
|
-
/**
|
|
3411
|
-
* get/set node size
|
|
3412
|
-
* @name Konva.Node#size
|
|
3413
|
-
* @method
|
|
3414
|
-
* @param {Object} size
|
|
3415
|
-
* @param {Number} size.width
|
|
3416
|
-
* @param {Number} size.height
|
|
3417
|
-
* @returns {Object}
|
|
3418
|
-
* @example
|
|
3419
|
-
* // get node size
|
|
3420
|
-
* var size = node.size();
|
|
3421
|
-
* var width = size.width;
|
|
3422
|
-
* var height = size.height;
|
|
3423
|
-
*
|
|
3424
|
-
* // set size
|
|
3425
|
-
* node.size({
|
|
3426
|
-
* width: 100,
|
|
3427
|
-
* height: 200
|
|
3428
|
-
* });
|
|
3429
|
-
*/
|
|
3430
|
-
addGetterSetter(Node, 'size');
|
|
3431
|
-
|
|
3432
|
-
/**
|
|
3433
|
-
* get/set drag bound function. This is used to override the default
|
|
3434
|
-
* drag and drop position.
|
|
3435
|
-
* @name Konva.Node#dragBoundFunc
|
|
3436
|
-
* @method
|
|
3437
|
-
* @param {Function} dragBoundFunc
|
|
3438
|
-
* @returns {Function}
|
|
3439
|
-
* @example
|
|
3440
|
-
* // get drag bound function
|
|
3441
|
-
* var dragBoundFunc = node.dragBoundFunc();
|
|
3442
|
-
*
|
|
3443
|
-
* // create vertical drag and drop
|
|
3444
|
-
* node.dragBoundFunc(function(pos){
|
|
3445
|
-
* // important pos - is absolute position of the node
|
|
3446
|
-
* // you should return absolute position too
|
|
3447
|
-
* return {
|
|
3448
|
-
* x: this.absolutePosition().x,
|
|
3449
|
-
* y: pos.y
|
|
3450
|
-
* };
|
|
3451
|
-
* });
|
|
3452
|
-
*/
|
|
3453
|
-
addGetterSetter(Node, 'dragBoundFunc');
|
|
3454
|
-
|
|
3455
|
-
/**
|
|
3456
|
-
* get/set draggable flag
|
|
3457
|
-
* @name Konva.Node#draggable
|
|
3458
|
-
* @method
|
|
3459
|
-
* @param {Boolean} draggable
|
|
3460
|
-
* @returns {Boolean}
|
|
3461
|
-
* @example
|
|
3462
|
-
* // get draggable flag
|
|
3463
|
-
* var draggable = node.draggable();
|
|
3464
|
-
*
|
|
3465
|
-
* // enable drag and drop
|
|
3466
|
-
* node.draggable(true);
|
|
3467
|
-
*
|
|
3468
|
-
* // disable drag and drop
|
|
3469
|
-
* node.draggable(false);
|
|
3470
|
-
*/
|
|
3471
|
-
addGetterSetter(Node, 'draggable', false, getBooleanValidator());
|
|
3472
|
-
|
|
3473
|
-
Factory.backCompat(Node, {
|
|
3474
|
-
rotateDeg: 'rotate',
|
|
3475
|
-
setRotationDeg: 'setRotation',
|
|
3476
|
-
getRotationDeg: 'getRotation',
|
|
3477
|
-
});
|