@nasser-sw/fabric 7.0.1-beta8 → 7.0.1-beta9

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.
Files changed (138) hide show
  1. package/debug/konva-master/CHANGELOG.md +1475 -0
  2. package/debug/konva-master/LICENSE +22 -0
  3. package/debug/konva-master/README.md +209 -0
  4. package/debug/konva-master/gulpfile.mjs +110 -0
  5. package/debug/konva-master/package.json +139 -0
  6. package/debug/konva-master/release.sh +62 -0
  7. package/debug/konva-master/resources/doc-includes/ContainerParams.txt +6 -0
  8. package/debug/konva-master/resources/doc-includes/NodeParams.txt +20 -0
  9. package/debug/konva-master/resources/doc-includes/ShapeParams.txt +53 -0
  10. package/debug/konva-master/resources/jsdoc.conf.json +28 -0
  11. package/debug/konva-master/rollup.config.mjs +32 -0
  12. package/debug/konva-master/src/Animation.ts +237 -0
  13. package/debug/konva-master/src/BezierFunctions.ts +826 -0
  14. package/debug/konva-master/src/Canvas.ts +230 -0
  15. package/debug/konva-master/src/Container.ts +649 -0
  16. package/debug/konva-master/src/Context.ts +1017 -0
  17. package/debug/konva-master/src/Core.ts +5 -0
  18. package/debug/konva-master/src/DragAndDrop.ts +173 -0
  19. package/debug/konva-master/src/Factory.ts +246 -0
  20. package/debug/konva-master/src/FastLayer.ts +29 -0
  21. package/debug/konva-master/src/Global.ts +210 -0
  22. package/debug/konva-master/src/Group.ts +31 -0
  23. package/debug/konva-master/src/Layer.ts +546 -0
  24. package/debug/konva-master/src/Node.ts +3477 -0
  25. package/debug/konva-master/src/PointerEvents.ts +67 -0
  26. package/debug/konva-master/src/Shape.ts +2081 -0
  27. package/debug/konva-master/src/Stage.ts +1000 -0
  28. package/debug/konva-master/src/Tween.ts +811 -0
  29. package/debug/konva-master/src/Util.ts +1123 -0
  30. package/debug/konva-master/src/Validators.ts +210 -0
  31. package/debug/konva-master/src/_CoreInternals.ts +85 -0
  32. package/debug/konva-master/src/_FullInternals.ts +171 -0
  33. package/debug/konva-master/src/canvas-backend.ts +36 -0
  34. package/debug/konva-master/src/filters/Blur.ts +388 -0
  35. package/debug/konva-master/src/filters/Brighten.ts +48 -0
  36. package/debug/konva-master/src/filters/Brightness.ts +30 -0
  37. package/debug/konva-master/src/filters/Contrast.ts +75 -0
  38. package/debug/konva-master/src/filters/Emboss.ts +207 -0
  39. package/debug/konva-master/src/filters/Enhance.ts +154 -0
  40. package/debug/konva-master/src/filters/Grayscale.ts +25 -0
  41. package/debug/konva-master/src/filters/HSL.ts +108 -0
  42. package/debug/konva-master/src/filters/HSV.ts +106 -0
  43. package/debug/konva-master/src/filters/Invert.ts +23 -0
  44. package/debug/konva-master/src/filters/Kaleidoscope.ts +274 -0
  45. package/debug/konva-master/src/filters/Mask.ts +220 -0
  46. package/debug/konva-master/src/filters/Noise.ts +44 -0
  47. package/debug/konva-master/src/filters/Pixelate.ts +107 -0
  48. package/debug/konva-master/src/filters/Posterize.ts +46 -0
  49. package/debug/konva-master/src/filters/RGB.ts +82 -0
  50. package/debug/konva-master/src/filters/RGBA.ts +103 -0
  51. package/debug/konva-master/src/filters/Sepia.ts +27 -0
  52. package/debug/konva-master/src/filters/Solarize.ts +29 -0
  53. package/debug/konva-master/src/filters/Threshold.ts +44 -0
  54. package/debug/konva-master/src/index.ts +3 -0
  55. package/debug/konva-master/src/shapes/Arc.ts +176 -0
  56. package/debug/konva-master/src/shapes/Arrow.ts +231 -0
  57. package/debug/konva-master/src/shapes/Circle.ts +76 -0
  58. package/debug/konva-master/src/shapes/Ellipse.ts +121 -0
  59. package/debug/konva-master/src/shapes/Image.ts +319 -0
  60. package/debug/konva-master/src/shapes/Label.ts +386 -0
  61. package/debug/konva-master/src/shapes/Line.ts +364 -0
  62. package/debug/konva-master/src/shapes/Path.ts +1013 -0
  63. package/debug/konva-master/src/shapes/Rect.ts +79 -0
  64. package/debug/konva-master/src/shapes/RegularPolygon.ts +167 -0
  65. package/debug/konva-master/src/shapes/Ring.ts +94 -0
  66. package/debug/konva-master/src/shapes/Sprite.ts +370 -0
  67. package/debug/konva-master/src/shapes/Star.ts +125 -0
  68. package/debug/konva-master/src/shapes/Text.ts +1065 -0
  69. package/debug/konva-master/src/shapes/TextPath.ts +583 -0
  70. package/debug/konva-master/src/shapes/Transformer.ts +1889 -0
  71. package/debug/konva-master/src/shapes/Wedge.ts +129 -0
  72. package/debug/konva-master/src/skia-backend.ts +35 -0
  73. package/debug/konva-master/src/types.ts +84 -0
  74. package/debug/konva-master/tsconfig.json +31 -0
  75. package/debug/konva-master/tsconfig.test.json +7 -0
  76. package/dist/index.js +915 -23
  77. package/dist/index.js.map +1 -1
  78. package/dist/index.min.js +1 -1
  79. package/dist/index.min.js.map +1 -1
  80. package/dist/index.min.mjs +1 -1
  81. package/dist/index.min.mjs.map +1 -1
  82. package/dist/index.mjs +915 -23
  83. package/dist/index.mjs.map +1 -1
  84. package/dist/index.node.cjs +915 -23
  85. package/dist/index.node.cjs.map +1 -1
  86. package/dist/index.node.mjs +915 -23
  87. package/dist/index.node.mjs.map +1 -1
  88. package/dist/package.json.min.mjs +1 -1
  89. package/dist/package.json.mjs +1 -1
  90. package/dist/src/shapes/Text/Text.d.ts +19 -0
  91. package/dist/src/shapes/Text/Text.d.ts.map +1 -1
  92. package/dist/src/shapes/Text/Text.min.mjs +1 -1
  93. package/dist/src/shapes/Text/Text.min.mjs.map +1 -1
  94. package/dist/src/shapes/Text/Text.mjs +238 -4
  95. package/dist/src/shapes/Text/Text.mjs.map +1 -1
  96. package/dist/src/shapes/Textbox.d.ts +38 -1
  97. package/dist/src/shapes/Textbox.d.ts.map +1 -1
  98. package/dist/src/shapes/Textbox.min.mjs +1 -1
  99. package/dist/src/shapes/Textbox.min.mjs.map +1 -1
  100. package/dist/src/shapes/Textbox.mjs +497 -15
  101. package/dist/src/shapes/Textbox.mjs.map +1 -1
  102. package/dist/src/text/examples/arabicTextExample.d.ts +60 -0
  103. package/dist/src/text/examples/arabicTextExample.d.ts.map +1 -0
  104. package/dist/src/text/measure.d.ts +9 -0
  105. package/dist/src/text/measure.d.ts.map +1 -1
  106. package/dist/src/text/measure.min.mjs +1 -1
  107. package/dist/src/text/measure.min.mjs.map +1 -1
  108. package/dist/src/text/measure.mjs +175 -4
  109. package/dist/src/text/measure.mjs.map +1 -1
  110. package/dist/src/text/overlayEditor.d.ts.map +1 -1
  111. package/dist/src/text/overlayEditor.min.mjs +1 -1
  112. package/dist/src/text/overlayEditor.min.mjs.map +1 -1
  113. package/dist/src/text/overlayEditor.mjs +7 -0
  114. package/dist/src/text/overlayEditor.mjs.map +1 -1
  115. package/dist/src/text/scriptUtils.d.ts +142 -0
  116. package/dist/src/text/scriptUtils.d.ts.map +1 -0
  117. package/dist/src/text/scriptUtils.min.mjs +2 -0
  118. package/dist/src/text/scriptUtils.min.mjs.map +1 -0
  119. package/dist/src/text/scriptUtils.mjs +212 -0
  120. package/dist/src/text/scriptUtils.mjs.map +1 -0
  121. package/dist-extensions/src/shapes/Text/Text.d.ts +19 -0
  122. package/dist-extensions/src/shapes/Text/Text.d.ts.map +1 -1
  123. package/dist-extensions/src/shapes/Textbox.d.ts +38 -1
  124. package/dist-extensions/src/shapes/Textbox.d.ts.map +1 -1
  125. package/dist-extensions/src/text/measure.d.ts +9 -0
  126. package/dist-extensions/src/text/measure.d.ts.map +1 -1
  127. package/dist-extensions/src/text/overlayEditor.d.ts.map +1 -1
  128. package/dist-extensions/src/text/scriptUtils.d.ts +142 -0
  129. package/dist-extensions/src/text/scriptUtils.d.ts.map +1 -0
  130. package/fabric-test-editor.html +2401 -46
  131. package/fonts/STV Bold.ttf +0 -0
  132. package/fonts/STV Light.ttf +0 -0
  133. package/fonts/STV Regular.ttf +0 -0
  134. package/package.json +1 -1
  135. package/src/shapes/Text/Text.ts +238 -5
  136. package/src/shapes/Textbox.ts +521 -11
  137. package/src/text/measure.ts +200 -50
  138. package/src/text/overlayEditor.ts +7 -0
@@ -0,0 +1,1000 @@
1
+ import { Util } from './Util.ts';
2
+ import { Factory } from './Factory.ts';
3
+ import type { ContainerConfig } from './Container.ts';
4
+ import { Container } from './Container.ts';
5
+ import { Konva } from './Global.ts';
6
+ import { SceneCanvas, HitCanvas } from './Canvas.ts';
7
+ import type { GetSet, Vector2d } from './types.ts';
8
+ import type { Shape } from './Shape.ts';
9
+ import type { Layer } from './Layer.ts';
10
+ import { DD } from './DragAndDrop.ts';
11
+ import { _registerNode } from './Global.ts';
12
+ import * as PointerEvents from './PointerEvents.ts';
13
+
14
+ export interface StageConfig extends ContainerConfig {
15
+ container?: HTMLDivElement | string;
16
+ }
17
+
18
+ // CONSTANTS
19
+ const STAGE = 'Stage',
20
+ STRING = 'string',
21
+ PX = 'px',
22
+ MOUSEOUT = 'mouseout',
23
+ MOUSELEAVE = 'mouseleave',
24
+ MOUSEOVER = 'mouseover',
25
+ MOUSEENTER = 'mouseenter',
26
+ MOUSEMOVE = 'mousemove',
27
+ MOUSEDOWN = 'mousedown',
28
+ MOUSEUP = 'mouseup',
29
+ POINTERMOVE = 'pointermove',
30
+ POINTERDOWN = 'pointerdown',
31
+ POINTERUP = 'pointerup',
32
+ POINTERCANCEL = 'pointercancel',
33
+ LOSTPOINTERCAPTURE = 'lostpointercapture',
34
+ POINTEROUT = 'pointerout',
35
+ POINTERLEAVE = 'pointerleave',
36
+ POINTEROVER = 'pointerover',
37
+ POINTERENTER = 'pointerenter',
38
+ CONTEXTMENU = 'contextmenu',
39
+ TOUCHSTART = 'touchstart',
40
+ TOUCHEND = 'touchend',
41
+ TOUCHMOVE = 'touchmove',
42
+ TOUCHCANCEL = 'touchcancel',
43
+ WHEEL = 'wheel',
44
+ MAX_LAYERS_NUMBER = 5,
45
+ EVENTS = [
46
+ [MOUSEENTER, '_pointerenter'],
47
+ [MOUSEDOWN, '_pointerdown'],
48
+ [MOUSEMOVE, '_pointermove'],
49
+ [MOUSEUP, '_pointerup'],
50
+ [MOUSELEAVE, '_pointerleave'],
51
+ [TOUCHSTART, '_pointerdown'],
52
+ [TOUCHMOVE, '_pointermove'],
53
+ [TOUCHEND, '_pointerup'],
54
+ [TOUCHCANCEL, '_pointercancel'],
55
+ [MOUSEOVER, '_pointerover'],
56
+ [WHEEL, '_wheel'],
57
+ [CONTEXTMENU, '_contextmenu'],
58
+ [POINTERDOWN, '_pointerdown'],
59
+ [POINTERMOVE, '_pointermove'],
60
+ [POINTERUP, '_pointerup'],
61
+ [POINTERCANCEL, '_pointercancel'],
62
+ [POINTERLEAVE, '_pointerleave'],
63
+ [LOSTPOINTERCAPTURE, '_lostpointercapture'],
64
+ ];
65
+
66
+ const EVENTS_MAP = {
67
+ mouse: {
68
+ [POINTEROUT]: MOUSEOUT,
69
+ [POINTERLEAVE]: MOUSELEAVE,
70
+ [POINTEROVER]: MOUSEOVER,
71
+ [POINTERENTER]: MOUSEENTER,
72
+ [POINTERMOVE]: MOUSEMOVE,
73
+ [POINTERDOWN]: MOUSEDOWN,
74
+ [POINTERUP]: MOUSEUP,
75
+ [POINTERCANCEL]: 'mousecancel',
76
+ pointerclick: 'click',
77
+ pointerdblclick: 'dblclick',
78
+ },
79
+ touch: {
80
+ [POINTEROUT]: 'touchout',
81
+ [POINTERLEAVE]: 'touchleave',
82
+ [POINTEROVER]: 'touchover',
83
+ [POINTERENTER]: 'touchenter',
84
+ [POINTERMOVE]: TOUCHMOVE,
85
+ [POINTERDOWN]: TOUCHSTART,
86
+ [POINTERUP]: TOUCHEND,
87
+ [POINTERCANCEL]: TOUCHCANCEL,
88
+ pointerclick: 'tap',
89
+ pointerdblclick: 'dbltap',
90
+ },
91
+ pointer: {
92
+ [POINTEROUT]: POINTEROUT,
93
+ [POINTERLEAVE]: POINTERLEAVE,
94
+ [POINTEROVER]: POINTEROVER,
95
+ [POINTERENTER]: POINTERENTER,
96
+ [POINTERMOVE]: POINTERMOVE,
97
+ [POINTERDOWN]: POINTERDOWN,
98
+ [POINTERUP]: POINTERUP,
99
+ [POINTERCANCEL]: POINTERCANCEL,
100
+ pointerclick: 'pointerclick',
101
+ pointerdblclick: 'pointerdblclick',
102
+ },
103
+ };
104
+
105
+ const getEventType = (type) => {
106
+ if (type.indexOf('pointer') >= 0) {
107
+ return 'pointer';
108
+ }
109
+ if (type.indexOf('touch') >= 0) {
110
+ return 'touch';
111
+ }
112
+ return 'mouse';
113
+ };
114
+
115
+ const getEventsMap = (eventType: string) => {
116
+ const type = getEventType(eventType);
117
+ if (type === 'pointer') {
118
+ return Konva.pointerEventsEnabled && EVENTS_MAP.pointer;
119
+ }
120
+ if (type === 'touch') {
121
+ return EVENTS_MAP.touch;
122
+ }
123
+ if (type === 'mouse') {
124
+ return EVENTS_MAP.mouse;
125
+ }
126
+ };
127
+
128
+ function checkNoClip(attrs: any = {}) {
129
+ if (attrs.clipFunc || attrs.clipWidth || attrs.clipHeight) {
130
+ Util.warn(
131
+ 'Stage does not support clipping. Please use clip for Layers or Groups.'
132
+ );
133
+ }
134
+ return attrs;
135
+ }
136
+
137
+ const NO_POINTERS_MESSAGE = `Pointer position is missing and not registered by the stage. Looks like it is outside of the stage container. You can set it manually from event: stage.setPointersPositions(event);`;
138
+
139
+ export const stages: Stage[] = [];
140
+
141
+ /**
142
+ * Stage constructor. A stage is used to contain multiple layers
143
+ * @constructor
144
+ * @memberof Konva
145
+ * @augments Konva.Container
146
+ * @param {Object} config
147
+ * @param {String|Element} config.container Container selector or DOM element
148
+ * @@nodeParams
149
+ * @example
150
+ * var stage = new Konva.Stage({
151
+ * width: 500,
152
+ * height: 800,
153
+ * container: 'containerId' // or "#containerId" or ".containerClass"
154
+ * });
155
+ */
156
+
157
+ export class Stage extends Container<Layer> {
158
+ content: HTMLDivElement;
159
+ pointerPos: Vector2d | null;
160
+ _pointerPositions: (Vector2d & { id?: number })[] = [];
161
+ _changedPointerPositions: (Vector2d & { id: number })[] = [];
162
+
163
+ bufferCanvas: SceneCanvas;
164
+ bufferHitCanvas: HitCanvas;
165
+ _mouseTargetShape: Shape;
166
+ _touchTargetShape: Shape;
167
+ _pointerTargetShape: Shape;
168
+ _mouseClickStartShape: Shape;
169
+ _touchClickStartShape: Shape;
170
+ _pointerClickStartShape: Shape;
171
+ _mouseClickEndShape: Shape;
172
+ _touchClickEndShape: Shape;
173
+ _pointerClickEndShape: Shape;
174
+
175
+ _mouseDblTimeout: any;
176
+ _touchDblTimeout: any;
177
+ _pointerDblTimeout: any;
178
+
179
+ constructor(config: StageConfig) {
180
+ super(checkNoClip(config));
181
+ this._buildDOM();
182
+ this._bindContentEvents();
183
+ stages.push(this);
184
+ this.on('widthChange.konva heightChange.konva', this._resizeDOM);
185
+ this.on('visibleChange.konva', this._checkVisibility);
186
+ this.on(
187
+ 'clipWidthChange.konva clipHeightChange.konva clipFuncChange.konva',
188
+ () => {
189
+ checkNoClip(this.attrs);
190
+ }
191
+ );
192
+ this._checkVisibility();
193
+ }
194
+
195
+ _validateAdd(child) {
196
+ const isLayer = child.getType() === 'Layer';
197
+ const isFastLayer = child.getType() === 'FastLayer';
198
+ const valid = isLayer || isFastLayer;
199
+ if (!valid) {
200
+ Util.throw('You may only add layers to the stage.');
201
+ }
202
+ }
203
+
204
+ _checkVisibility() {
205
+ if (!this.content) {
206
+ return;
207
+ }
208
+ const style = this.visible() ? '' : 'none';
209
+ this.content.style.display = style;
210
+ }
211
+ /**
212
+ * set container dom element which contains the stage wrapper div element
213
+ * @method
214
+ * @name Konva.Stage#setContainer
215
+ * @param {DomElement} container can pass in a dom element or id string
216
+ */
217
+ setContainer(container) {
218
+ if (typeof container === STRING) {
219
+ let id;
220
+ if (container.charAt(0) === '.') {
221
+ const className = container.slice(1);
222
+ container = document.getElementsByClassName(className)[0];
223
+ } else {
224
+ if (container.charAt(0) !== '#') {
225
+ id = container;
226
+ } else {
227
+ id = container.slice(1);
228
+ }
229
+ container = document.getElementById(id);
230
+ }
231
+ if (!container) {
232
+ throw 'Can not find container in document with id ' + id;
233
+ }
234
+ }
235
+ this._setAttr('container', container);
236
+ if (this.content) {
237
+ if (this.content.parentElement) {
238
+ this.content.parentElement.removeChild(this.content);
239
+ }
240
+ container.appendChild(this.content);
241
+ }
242
+ return this;
243
+ }
244
+ shouldDrawHit() {
245
+ return true;
246
+ }
247
+
248
+ /**
249
+ * clear all layers
250
+ * @method
251
+ * @name Konva.Stage#clear
252
+ */
253
+ clear() {
254
+ const layers = this.children,
255
+ len = layers.length;
256
+
257
+ for (let n = 0; n < len; n++) {
258
+ layers[n].clear();
259
+ }
260
+ return this;
261
+ }
262
+ clone(obj?) {
263
+ if (!obj) {
264
+ obj = {};
265
+ }
266
+ obj.container =
267
+ typeof document !== 'undefined' && document.createElement('div');
268
+ return Container.prototype.clone.call(this, obj) as this;
269
+ }
270
+
271
+ destroy() {
272
+ super.destroy();
273
+
274
+ const content = this.content;
275
+ if (content && Util._isInDocument(content)) {
276
+ this.container().removeChild(content);
277
+ }
278
+ const index = stages.indexOf(this);
279
+ if (index > -1) {
280
+ stages.splice(index, 1);
281
+ }
282
+
283
+ Util.releaseCanvas(this.bufferCanvas._canvas, this.bufferHitCanvas._canvas);
284
+
285
+ return this;
286
+ }
287
+ /**
288
+ * returns ABSOLUTE pointer position which can be a touch position or mouse position
289
+ * pointer position doesn't include any transforms (such as scale) of the stage
290
+ * it is just a plain position of pointer relative to top-left corner of the canvas
291
+ * @method
292
+ * @name Konva.Stage#getPointerPosition
293
+ * @returns {Vector2d|null}
294
+ */
295
+ getPointerPosition(): Vector2d | null {
296
+ const pos = this._pointerPositions[0] || this._changedPointerPositions[0];
297
+ if (!pos) {
298
+ Util.warn(NO_POINTERS_MESSAGE);
299
+ return null;
300
+ }
301
+ return {
302
+ x: pos.x,
303
+ y: pos.y,
304
+ };
305
+ }
306
+ _getPointerById(id?: number) {
307
+ return this._pointerPositions.find((p) => p.id === id);
308
+ }
309
+ getPointersPositions() {
310
+ return this._pointerPositions;
311
+ }
312
+ getStage() {
313
+ return this;
314
+ }
315
+ getContent() {
316
+ return this.content;
317
+ }
318
+ _toKonvaCanvas(config) {
319
+ config = config || {};
320
+
321
+ config.x = config.x || 0;
322
+ config.y = config.y || 0;
323
+ config.width = config.width || this.width();
324
+ config.height = config.height || this.height();
325
+
326
+ const canvas = new SceneCanvas({
327
+ width: config.width,
328
+ height: config.height,
329
+ pixelRatio: config.pixelRatio || 1,
330
+ });
331
+ const _context = canvas.getContext()._context;
332
+ const layers = this.children;
333
+
334
+ if (config.x || config.y) {
335
+ _context.translate(-1 * config.x, -1 * config.y);
336
+ }
337
+
338
+ layers.forEach(function (layer) {
339
+ if (!layer.isVisible()) {
340
+ return;
341
+ }
342
+ const layerCanvas = layer._toKonvaCanvas(config);
343
+ _context.drawImage(
344
+ layerCanvas._canvas,
345
+ config.x,
346
+ config.y,
347
+ layerCanvas.getWidth() / layerCanvas.getPixelRatio(),
348
+ layerCanvas.getHeight() / layerCanvas.getPixelRatio()
349
+ );
350
+ });
351
+ return canvas;
352
+ }
353
+
354
+ /**
355
+ * get visible intersection shape. This is the preferred
356
+ * method for determining if a point intersects a shape or not
357
+ * nodes with listening set to false will not be detected
358
+ * @method
359
+ * @name Konva.Stage#getIntersection
360
+ * @param {Object} pos
361
+ * @param {Number} pos.x
362
+ * @param {Number} pos.y
363
+ * @returns {Konva.Node}
364
+ * @example
365
+ * var shape = stage.getIntersection({x: 50, y: 50});
366
+ */
367
+ getIntersection(pos: Vector2d) {
368
+ if (!pos) {
369
+ return null;
370
+ }
371
+ const layers = this.children,
372
+ len = layers.length,
373
+ end = len - 1;
374
+
375
+ for (let n = end; n >= 0; n--) {
376
+ const shape = layers[n].getIntersection(pos);
377
+ if (shape) {
378
+ return shape;
379
+ }
380
+ }
381
+
382
+ return null;
383
+ }
384
+ _resizeDOM() {
385
+ const width = this.width();
386
+ const height = this.height();
387
+ if (this.content) {
388
+ // set content dimensions
389
+ this.content.style.width = width + PX;
390
+ this.content.style.height = height + PX;
391
+ }
392
+
393
+ this.bufferCanvas.setSize(width, height);
394
+ this.bufferHitCanvas.setSize(width, height);
395
+
396
+ // set layer dimensions
397
+ this.children.forEach((layer) => {
398
+ layer.setSize({ width, height });
399
+ layer.draw();
400
+ });
401
+ }
402
+ add(layer: Layer, ...rest) {
403
+ if (arguments.length > 1) {
404
+ for (let i = 0; i < arguments.length; i++) {
405
+ this.add(arguments[i]);
406
+ }
407
+ return this;
408
+ }
409
+ super.add(layer);
410
+
411
+ const length = this.children.length;
412
+ if (length > MAX_LAYERS_NUMBER) {
413
+ Util.warn(
414
+ 'The stage has ' +
415
+ length +
416
+ ' layers. Recommended maximum number of layers is 3-5. Adding more layers into the stage may drop the performance. Rethink your tree structure, you can use Konva.Group.'
417
+ );
418
+ }
419
+ layer.setSize({ width: this.width(), height: this.height() });
420
+
421
+ // draw layer and append canvas to container
422
+ layer.draw();
423
+
424
+ if (Konva.isBrowser) {
425
+ this.content.appendChild(layer.canvas._canvas);
426
+ }
427
+
428
+ // chainable
429
+ return this;
430
+ }
431
+ getParent() {
432
+ return null;
433
+ }
434
+ getLayer() {
435
+ return null;
436
+ }
437
+
438
+ hasPointerCapture(pointerId: number): boolean {
439
+ return PointerEvents.hasPointerCapture(pointerId, this);
440
+ }
441
+
442
+ setPointerCapture(pointerId: number) {
443
+ PointerEvents.setPointerCapture(pointerId, this);
444
+ }
445
+
446
+ releaseCapture(pointerId: number) {
447
+ PointerEvents.releaseCapture(pointerId, this);
448
+ }
449
+
450
+ /**
451
+ * returns an array of layers
452
+ * @method
453
+ * @name Konva.Stage#getLayers
454
+ */
455
+ getLayers() {
456
+ return this.children;
457
+ }
458
+ _bindContentEvents() {
459
+ if (!Konva.isBrowser) {
460
+ return;
461
+ }
462
+ EVENTS.forEach(([event, methodName]) => {
463
+ this.content.addEventListener(
464
+ event,
465
+ (evt) => {
466
+ this[methodName](evt);
467
+ },
468
+ { passive: false }
469
+ );
470
+ });
471
+ }
472
+ _pointerenter(evt: PointerEvent) {
473
+ this.setPointersPositions(evt);
474
+ const events = getEventsMap(evt.type);
475
+ if (events) {
476
+ this._fire(events.pointerenter, {
477
+ evt: evt,
478
+ target: this,
479
+ currentTarget: this,
480
+ });
481
+ }
482
+ }
483
+ _pointerover(evt) {
484
+ this.setPointersPositions(evt);
485
+ const events = getEventsMap(evt.type);
486
+ if (events) {
487
+ this._fire(events.pointerover, {
488
+ evt: evt,
489
+ target: this,
490
+ currentTarget: this,
491
+ });
492
+ }
493
+ }
494
+ _getTargetShape(evenType) {
495
+ let shape: Shape | null = this[evenType + 'targetShape'];
496
+ if (shape && !shape.getStage()) {
497
+ shape = null;
498
+ }
499
+ return shape;
500
+ }
501
+ _pointerleave(evt) {
502
+ const events = getEventsMap(evt.type);
503
+ const eventType = getEventType(evt.type);
504
+
505
+ if (!events) {
506
+ return;
507
+ }
508
+ this.setPointersPositions(evt);
509
+
510
+ const targetShape = this._getTargetShape(eventType);
511
+ const eventsEnabled =
512
+ !(Konva.isDragging() || Konva.isTransforming()) || Konva.hitOnDragEnabled;
513
+ if (targetShape && eventsEnabled) {
514
+ targetShape._fireAndBubble(events.pointerout, { evt: evt });
515
+ targetShape._fireAndBubble(events.pointerleave, { evt: evt });
516
+ this._fire(events.pointerleave, {
517
+ evt: evt,
518
+ target: this,
519
+ currentTarget: this,
520
+ });
521
+ this[eventType + 'targetShape'] = null;
522
+ } else if (eventsEnabled) {
523
+ this._fire(events.pointerleave, {
524
+ evt: evt,
525
+ target: this,
526
+ currentTarget: this,
527
+ });
528
+ this._fire(events.pointerout, {
529
+ evt: evt,
530
+ target: this,
531
+ currentTarget: this,
532
+ });
533
+ }
534
+ this.pointerPos = null;
535
+ this._pointerPositions = [];
536
+ }
537
+ _pointerdown(evt: TouchEvent | MouseEvent | PointerEvent) {
538
+ const events = getEventsMap(evt.type);
539
+ const eventType = getEventType(evt.type);
540
+
541
+ if (!events) {
542
+ return;
543
+ }
544
+ this.setPointersPositions(evt);
545
+
546
+ let triggeredOnShape = false;
547
+ this._changedPointerPositions.forEach((pos) => {
548
+ const shape = this.getIntersection(pos);
549
+ DD.justDragged = false;
550
+ // probably we are staring a click
551
+ Konva['_' + eventType + 'ListenClick'] = true;
552
+
553
+ // no shape detected? do nothing
554
+ if (!shape || !shape.isListening()) {
555
+ this[eventType + 'ClickStartShape'] = undefined;
556
+ return;
557
+ }
558
+
559
+ if (Konva.capturePointerEventsEnabled) {
560
+ shape.setPointerCapture(pos.id);
561
+ }
562
+
563
+ // save where we started the click
564
+ this[eventType + 'ClickStartShape'] = shape;
565
+
566
+ shape._fireAndBubble(events.pointerdown, {
567
+ evt: evt,
568
+ pointerId: pos.id,
569
+ });
570
+ triggeredOnShape = true;
571
+
572
+ // TODO: test in iframe
573
+ // only call preventDefault if the shape is listening for events
574
+ const isTouch = evt.type.indexOf('touch') >= 0;
575
+ if (shape.preventDefault() && evt.cancelable && isTouch) {
576
+ evt.preventDefault();
577
+ }
578
+ });
579
+
580
+ // trigger down on stage if not already
581
+ if (!triggeredOnShape) {
582
+ this._fire(events.pointerdown, {
583
+ evt: evt,
584
+ target: this,
585
+ currentTarget: this,
586
+ pointerId: this._pointerPositions[0].id,
587
+ });
588
+ }
589
+ }
590
+ _pointermove(evt: TouchEvent | MouseEvent | PointerEvent) {
591
+ const events = getEventsMap(evt.type);
592
+ const eventType = getEventType(evt.type);
593
+ if (!events) {
594
+ return;
595
+ }
596
+ // prevent default only for touch-based interactions to avoid blocking
597
+ // native mouse wheel scrolling during drag on desktop
598
+ const isTouchPointer =
599
+ (evt as any).type.indexOf('touch') >= 0 ||
600
+ (evt as any).pointerType === 'touch';
601
+ if (
602
+ Konva.isDragging() &&
603
+ DD.node!.preventDefault() &&
604
+ evt.cancelable &&
605
+ isTouchPointer
606
+ ) {
607
+ evt.preventDefault();
608
+ }
609
+ this.setPointersPositions(evt);
610
+
611
+ const eventsEnabled =
612
+ !(Konva.isDragging() || Konva.isTransforming()) || Konva.hitOnDragEnabled;
613
+ if (!eventsEnabled) {
614
+ return;
615
+ }
616
+
617
+ const processedShapesIds = {};
618
+ let triggeredOnShape = false;
619
+ const targetShape = this._getTargetShape(eventType);
620
+ this._changedPointerPositions.forEach((pos) => {
621
+ const shape = (PointerEvents.getCapturedShape(pos.id) ||
622
+ this.getIntersection(pos)) as Shape;
623
+ const pointerId = pos.id;
624
+ const event = { evt: evt, pointerId };
625
+
626
+ const differentTarget = targetShape !== shape;
627
+
628
+ if (differentTarget && targetShape) {
629
+ targetShape._fireAndBubble(events.pointerout, { ...event }, shape);
630
+ targetShape._fireAndBubble(events.pointerleave, { ...event }, shape);
631
+ }
632
+
633
+ if (shape) {
634
+ if (processedShapesIds[shape._id]) {
635
+ return;
636
+ }
637
+ processedShapesIds[shape._id] = true;
638
+ }
639
+
640
+ if (shape && shape.isListening()) {
641
+ triggeredOnShape = true;
642
+ if (differentTarget) {
643
+ shape._fireAndBubble(events.pointerover, { ...event }, targetShape);
644
+ shape._fireAndBubble(events.pointerenter, { ...event }, targetShape);
645
+ this[eventType + 'targetShape'] = shape;
646
+ }
647
+ shape._fireAndBubble(events.pointermove, { ...event });
648
+ } else {
649
+ if (targetShape) {
650
+ this._fire(events.pointerover, {
651
+ evt: evt,
652
+ target: this,
653
+ currentTarget: this,
654
+ pointerId,
655
+ });
656
+ this[eventType + 'targetShape'] = null;
657
+ }
658
+ }
659
+ });
660
+
661
+ if (!triggeredOnShape) {
662
+ this._fire(events.pointermove, {
663
+ evt: evt,
664
+ target: this,
665
+ currentTarget: this,
666
+ pointerId: this._changedPointerPositions[0].id,
667
+ });
668
+ }
669
+ }
670
+ _pointerup(evt) {
671
+ const events = getEventsMap(evt.type);
672
+ const eventType = getEventType(evt.type);
673
+
674
+ if (!events) {
675
+ return;
676
+ }
677
+ this.setPointersPositions(evt);
678
+ const clickStartShape = this[eventType + 'ClickStartShape'];
679
+ const clickEndShape = this[eventType + 'ClickEndShape'];
680
+ const processedShapesIds = {};
681
+ let skipPointerUpTrigger = false;
682
+ this._changedPointerPositions.forEach((pos) => {
683
+ const shape = (PointerEvents.getCapturedShape(pos.id) ||
684
+ this.getIntersection(pos)) as Shape;
685
+
686
+ if (shape) {
687
+ shape.releaseCapture(pos.id);
688
+ if (processedShapesIds[shape._id]) {
689
+ return;
690
+ }
691
+ processedShapesIds[shape._id] = true;
692
+ }
693
+
694
+ const pointerId = pos.id;
695
+ const event = { evt: evt, pointerId };
696
+
697
+ let fireDblClick = false;
698
+ if (Konva['_' + eventType + 'InDblClickWindow']) {
699
+ fireDblClick = true;
700
+ clearTimeout(this[eventType + 'DblTimeout']);
701
+ } else if (!DD.justDragged) {
702
+ // don't set inDblClickWindow after dragging
703
+ Konva['_' + eventType + 'InDblClickWindow'] = true;
704
+ clearTimeout(this[eventType + 'DblTimeout']);
705
+ }
706
+
707
+ this[eventType + 'DblTimeout'] = setTimeout(function () {
708
+ Konva['_' + eventType + 'InDblClickWindow'] = false;
709
+ }, Konva.dblClickWindow);
710
+
711
+ if (shape && shape.isListening()) {
712
+ skipPointerUpTrigger = true;
713
+ this[eventType + 'ClickEndShape'] = shape;
714
+ shape._fireAndBubble(events.pointerup, { ...event });
715
+
716
+ // detect if click or double click occurred
717
+ if (
718
+ Konva['_' + eventType + 'ListenClick'] &&
719
+ clickStartShape &&
720
+ clickStartShape === shape
721
+ ) {
722
+ shape._fireAndBubble(events.pointerclick, { ...event });
723
+
724
+ if (fireDblClick && clickEndShape && clickEndShape === shape) {
725
+ shape._fireAndBubble(events.pointerdblclick, { ...event });
726
+ }
727
+ }
728
+ } else {
729
+ this[eventType + 'ClickEndShape'] = null;
730
+
731
+ if (!skipPointerUpTrigger) {
732
+ this._fire(events.pointerup, {
733
+ evt: evt,
734
+ target: this,
735
+ currentTarget: this,
736
+ pointerId: this._changedPointerPositions[0].id,
737
+ });
738
+ skipPointerUpTrigger = true;
739
+ }
740
+
741
+ if (Konva['_' + eventType + 'ListenClick']) {
742
+ this._fire(events.pointerclick, {
743
+ evt: evt,
744
+ target: this,
745
+ currentTarget: this,
746
+ pointerId,
747
+ });
748
+ }
749
+
750
+ if (fireDblClick) {
751
+ this._fire(events.pointerdblclick, {
752
+ evt: evt,
753
+ target: this,
754
+ currentTarget: this,
755
+ pointerId,
756
+ });
757
+ }
758
+ }
759
+ });
760
+
761
+ if (!skipPointerUpTrigger) {
762
+ this._fire(events.pointerup, {
763
+ evt: evt,
764
+ target: this,
765
+ currentTarget: this,
766
+ pointerId: this._changedPointerPositions[0].id,
767
+ });
768
+ }
769
+
770
+ Konva['_' + eventType + 'ListenClick'] = false;
771
+
772
+ // always call preventDefault for desktop events because some browsers
773
+ // try to drag and drop the canvas element
774
+ // TODO: are we sure we need to prevent default at all?
775
+ // do not call this function on mobile because it prevent "click" event on all parent containers
776
+ // but apps may listen to it.
777
+ if (evt.cancelable && eventType !== 'touch' && eventType !== 'pointer') {
778
+ evt.preventDefault();
779
+ }
780
+ }
781
+ _contextmenu(evt) {
782
+ this.setPointersPositions(evt);
783
+ const shape = this.getIntersection(this.getPointerPosition()!);
784
+
785
+ if (shape && shape.isListening()) {
786
+ shape._fireAndBubble(CONTEXTMENU, { evt: evt });
787
+ } else {
788
+ this._fire(CONTEXTMENU, {
789
+ evt: evt,
790
+ target: this,
791
+ currentTarget: this,
792
+ });
793
+ }
794
+ }
795
+
796
+ _wheel(evt) {
797
+ this.setPointersPositions(evt);
798
+ const shape = this.getIntersection(this.getPointerPosition()!);
799
+
800
+ if (shape && shape.isListening()) {
801
+ shape._fireAndBubble(WHEEL, { evt: evt });
802
+ } else {
803
+ this._fire(WHEEL, {
804
+ evt: evt,
805
+ target: this,
806
+ currentTarget: this,
807
+ });
808
+ }
809
+ }
810
+
811
+ _pointercancel(evt: PointerEvent) {
812
+ this.setPointersPositions(evt);
813
+ const shape =
814
+ PointerEvents.getCapturedShape(evt.pointerId) ||
815
+ this.getIntersection(this.getPointerPosition()!);
816
+
817
+ if (shape) {
818
+ shape._fireAndBubble(POINTERUP, PointerEvents.createEvent(evt));
819
+ }
820
+
821
+ PointerEvents.releaseCapture(evt.pointerId);
822
+ }
823
+
824
+ _lostpointercapture(evt: PointerEvent) {
825
+ PointerEvents.releaseCapture(evt.pointerId);
826
+ }
827
+
828
+ /**
829
+ * manually register pointers positions (mouse/touch) in the stage.
830
+ * So you can use stage.getPointerPosition(). Usually you don't need to use that method
831
+ * because all internal events are automatically registered. It may be useful if event
832
+ * is triggered outside of the stage, but you still want to use Konva methods to get pointers position.
833
+ * @method
834
+ * @name Konva.Stage#setPointersPositions
835
+ * @param {Object} event Event object
836
+ * @example
837
+ *
838
+ * window.addEventListener('mousemove', (e) => {
839
+ * stage.setPointersPositions(e);
840
+ * });
841
+ */
842
+ setPointersPositions(evt) {
843
+ const contentPosition = this._getContentPosition();
844
+ let x: number | null = null,
845
+ y: number | null = null;
846
+ evt = evt ? evt : window.event;
847
+
848
+ // touch events
849
+ if (evt.touches !== undefined) {
850
+ // touchlist has not support for map method
851
+ // so we have to iterate
852
+ this._pointerPositions = [];
853
+ this._changedPointerPositions = [];
854
+ Array.prototype.forEach.call(evt.touches, (touch: any) => {
855
+ this._pointerPositions.push({
856
+ id: touch.identifier,
857
+ x: (touch.clientX - contentPosition.left) / contentPosition.scaleX,
858
+ y: (touch.clientY - contentPosition.top) / contentPosition.scaleY,
859
+ });
860
+ });
861
+
862
+ Array.prototype.forEach.call(
863
+ evt.changedTouches || evt.touches,
864
+ (touch: any) => {
865
+ this._changedPointerPositions.push({
866
+ id: touch.identifier,
867
+ x: (touch.clientX - contentPosition.left) / contentPosition.scaleX,
868
+ y: (touch.clientY - contentPosition.top) / contentPosition.scaleY,
869
+ });
870
+ }
871
+ );
872
+ } else {
873
+ // mouse events
874
+ x = (evt.clientX - contentPosition.left) / contentPosition.scaleX;
875
+ y = (evt.clientY - contentPosition.top) / contentPosition.scaleY;
876
+ this.pointerPos = {
877
+ x: x,
878
+ y: y,
879
+ };
880
+ this._pointerPositions = [{ x, y, id: Util._getFirstPointerId(evt) }];
881
+ this._changedPointerPositions = [
882
+ { x, y, id: Util._getFirstPointerId(evt) },
883
+ ];
884
+ }
885
+ }
886
+ _setPointerPosition(evt) {
887
+ Util.warn(
888
+ 'Method _setPointerPosition is deprecated. Use "stage.setPointersPositions(event)" instead.'
889
+ );
890
+ this.setPointersPositions(evt);
891
+ }
892
+ _getContentPosition() {
893
+ if (!this.content || !this.content.getBoundingClientRect) {
894
+ return {
895
+ top: 0,
896
+ left: 0,
897
+ scaleX: 1,
898
+ scaleY: 1,
899
+ };
900
+ }
901
+
902
+ const rect = this.content.getBoundingClientRect();
903
+
904
+ return {
905
+ top: rect.top,
906
+ left: rect.left,
907
+ // sometimes clientWidth can be equals to 0
908
+ // i saw it in react-konva test, looks like it is because of hidden testing element
909
+ scaleX: rect.width / this.content.clientWidth || 1,
910
+ scaleY: rect.height / this.content.clientHeight || 1,
911
+ };
912
+ }
913
+ _buildDOM() {
914
+ this.bufferCanvas = new SceneCanvas({
915
+ width: this.width(),
916
+ height: this.height(),
917
+ });
918
+ this.bufferHitCanvas = new HitCanvas({
919
+ pixelRatio: 1,
920
+ width: this.width(),
921
+ height: this.height(),
922
+ });
923
+
924
+ if (!Konva.isBrowser) {
925
+ return;
926
+ }
927
+ const container = this.container();
928
+ if (!container) {
929
+ throw 'Stage has no container. A container is required.';
930
+ }
931
+ // clear content inside container
932
+ container.innerHTML = '';
933
+
934
+ // content
935
+ this.content = document.createElement('div');
936
+ this.content.style.position = 'relative';
937
+ this.content.style.userSelect = 'none';
938
+ this.content.className = 'konvajs-content';
939
+
940
+ this.content.setAttribute('role', 'presentation');
941
+
942
+ container.appendChild(this.content);
943
+
944
+ this._resizeDOM();
945
+ }
946
+ // currently cache function is now working for stage, because stage has no its own canvas element
947
+ cache() {
948
+ Util.warn(
949
+ 'Cache function is not allowed for stage. You may use cache only for layers, groups and shapes.'
950
+ );
951
+ return this;
952
+ }
953
+ clearCache() {
954
+ return this;
955
+ }
956
+ /**
957
+ * batch draw
958
+ * @method
959
+ * @name Konva.Stage#batchDraw
960
+ * @return {Konva.Stage} this
961
+ */
962
+ batchDraw() {
963
+ this.getChildren().forEach(function (layer) {
964
+ layer.batchDraw();
965
+ });
966
+ return this;
967
+ }
968
+
969
+ container: GetSet<HTMLDivElement, this>;
970
+ }
971
+
972
+ Stage.prototype.nodeType = STAGE;
973
+ _registerNode(Stage);
974
+
975
+ /**
976
+ * get/set container DOM element
977
+ * @method
978
+ * @name Konva.Stage#container
979
+ * @returns {DomElement} container
980
+ * @example
981
+ * // get container
982
+ * var container = stage.container();
983
+ * // set container
984
+ * var container = document.createElement('div');
985
+ * body.appendChild(container);
986
+ * stage.container(container);
987
+ */
988
+ Factory.addGetterSetter(Stage, 'container');
989
+
990
+ // chrome is clearing canvas in inactive browser window, causing layer content to be erased
991
+ // so let's redraw layers as soon as window becomes active
992
+ // TODO: any other way to solve this issue?
993
+ // TODO: should we remove it if chrome fixes the issue?
994
+ if (Konva.isBrowser) {
995
+ document.addEventListener('visibilitychange', () => {
996
+ stages.forEach((stage) => {
997
+ stage.batchDraw();
998
+ });
999
+ });
1000
+ }