leafer-ui 1.0.0-rc.3 → 1.0.0-rc.30

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/dist/web.esm.js CHANGED
@@ -1,8 +1,255 @@
1
- import { LeafList, DataHelper, RenderEvent, ChildEvent, WatchEvent, PropertyEvent, LeafHelper, BranchHelper, Bounds, LeafBoundsHelper, BoundsHelper, Debug, LeafLevelList, LayoutEvent, Run, ImageManager, Platform, AnimateEvent, ResizeEvent, Creator, LeaferCanvasBase, Cursor, canvasSizeAttrs, canvasPatch, InteractionHelper, MathHelper, InteractionBase, LeaferImage, FileHelper, MatrixHelper, ImageEvent, PointHelper, TaskProcessor } from '@leafer/core';
1
+ import { Debug, LeaferCanvasBase, Platform, DataHelper, canvasSizeAttrs, ResizeEvent, canvasPatch, Creator, LeaferImage, FileHelper, LeafList, RenderEvent, ChildEvent, WatchEvent, PropertyEvent, LeafHelper, BranchHelper, Bounds, LeafBoundsHelper, LeafLevelList, LayoutEvent, Run, ImageManager, AnimateEvent, BoundsHelper, Answer, MathHelper, MatrixHelper, AlignHelper, ImageEvent, AroundHelper, PointHelper, Direction4, TwoPointBoundsHelper, TaskProcessor, Matrix } from '@leafer/core';
2
2
  export * from '@leafer/core';
3
3
  export { LeaferImage } from '@leafer/core';
4
- import { ColorConvert as ColorConvert$1, Paint, Effect, TextConvert as TextConvert$1, Export as Export$1 } from '@leafer-ui/core';
4
+ import { InteractionHelper, InteractionBase, Cursor, HitCanvasManager } from '@leafer-ui/core';
5
5
  export * from '@leafer-ui/core';
6
+ import { PaintImage, ColorConvert, PaintGradient, Export, Group, TextConvert, Paint, Effect } from '@leafer-ui/draw';
7
+
8
+ const debug$3 = Debug.get('LeaferCanvas');
9
+ class LeaferCanvas extends LeaferCanvasBase {
10
+ set zIndex(zIndex) {
11
+ const { style } = this.view;
12
+ style.zIndex = zIndex;
13
+ this.setAbsolute(this.view);
14
+ }
15
+ set childIndex(index) {
16
+ const { view, parentView } = this;
17
+ if (view && parentView) {
18
+ const beforeNode = parentView.children[index];
19
+ if (beforeNode) {
20
+ this.setAbsolute(beforeNode);
21
+ parentView.insertBefore(view, beforeNode);
22
+ }
23
+ else {
24
+ parentView.appendChild(beforeNode);
25
+ }
26
+ }
27
+ }
28
+ init() {
29
+ const { view } = this.config;
30
+ view ? this.__createViewFrom(view) : this.__createView();
31
+ const { style } = this.view;
32
+ style.display || (style.display = 'block');
33
+ this.parentView = this.view.parentElement;
34
+ if (this.parentView) {
35
+ const pStyle = this.parentView.style;
36
+ pStyle.webkitUserSelect = pStyle.userSelect = 'none';
37
+ }
38
+ if (Platform.syncDomFont && !this.parentView) {
39
+ style.display = 'none';
40
+ document.body.appendChild(this.view);
41
+ }
42
+ this.__createContext();
43
+ if (!this.autoLayout)
44
+ this.resize(this.config);
45
+ }
46
+ set backgroundColor(color) { this.view.style.backgroundColor = color; }
47
+ get backgroundColor() { return this.view.style.backgroundColor; }
48
+ set hittable(hittable) { this.view.style.pointerEvents = hittable ? 'auto' : 'none'; }
49
+ get hittable() { return this.view.style.pointerEvents !== 'none'; }
50
+ __createView() {
51
+ this.view = document.createElement('canvas');
52
+ }
53
+ __createViewFrom(inputView) {
54
+ let find = (typeof inputView === 'string') ? document.getElementById(inputView) : inputView;
55
+ if (find) {
56
+ if (find instanceof HTMLCanvasElement) {
57
+ this.view = find;
58
+ }
59
+ else {
60
+ let parent = find;
61
+ if (find === window || find === document) {
62
+ const div = document.createElement('div');
63
+ const { style } = div;
64
+ style.position = 'absolute';
65
+ style.top = style.bottom = style.left = style.right = '0px';
66
+ document.body.appendChild(div);
67
+ parent = div;
68
+ }
69
+ this.__createView();
70
+ const view = this.view;
71
+ if (parent.hasChildNodes()) {
72
+ this.setAbsolute(view);
73
+ parent.style.position || (parent.style.position = 'relative');
74
+ }
75
+ parent.appendChild(view);
76
+ }
77
+ }
78
+ else {
79
+ debug$3.error(`no id: ${inputView}`);
80
+ this.__createView();
81
+ }
82
+ }
83
+ setAbsolute(view) {
84
+ const { style } = view;
85
+ style.position = 'absolute';
86
+ style.top = style.left = '0px';
87
+ }
88
+ updateViewSize() {
89
+ const { width, height, pixelRatio } = this;
90
+ const { style } = this.view;
91
+ style.width = width + 'px';
92
+ style.height = height + 'px';
93
+ this.view.width = Math.ceil(width * pixelRatio);
94
+ this.view.height = Math.ceil(height * pixelRatio);
95
+ }
96
+ updateClientBounds() {
97
+ this.clientBounds = this.view.getBoundingClientRect();
98
+ }
99
+ startAutoLayout(autoBounds, listener) {
100
+ this.autoBounds = autoBounds;
101
+ this.resizeListener = listener;
102
+ try {
103
+ this.resizeObserver = new ResizeObserver((entries) => {
104
+ this.updateClientBounds();
105
+ for (const entry of entries)
106
+ this.checkAutoBounds(entry.contentRect);
107
+ });
108
+ const parent = this.parentView;
109
+ if (parent) {
110
+ this.resizeObserver.observe(parent);
111
+ this.checkAutoBounds(parent.getBoundingClientRect());
112
+ }
113
+ else {
114
+ this.checkAutoBounds(this.view);
115
+ debug$3.warn('no parent');
116
+ }
117
+ }
118
+ catch (_a) {
119
+ this.imitateResizeObserver();
120
+ }
121
+ }
122
+ imitateResizeObserver() {
123
+ if (this.autoLayout) {
124
+ if (this.parentView)
125
+ this.checkAutoBounds(this.parentView.getBoundingClientRect());
126
+ Platform.requestRender(this.imitateResizeObserver.bind(this));
127
+ }
128
+ }
129
+ checkAutoBounds(parentSize) {
130
+ const view = this.view;
131
+ const { x, y, width, height } = this.autoBounds.getBoundsFrom(parentSize);
132
+ if (width !== this.width || height !== this.height) {
133
+ const { style } = view;
134
+ const { pixelRatio } = this;
135
+ style.marginLeft = x + 'px';
136
+ style.marginTop = y + 'px';
137
+ const size = { width, height, pixelRatio };
138
+ const oldSize = {};
139
+ DataHelper.copyAttrs(oldSize, this, canvasSizeAttrs);
140
+ this.resize(size);
141
+ if (this.width !== undefined)
142
+ this.resizeListener(new ResizeEvent(size, oldSize));
143
+ }
144
+ }
145
+ stopAutoLayout() {
146
+ this.autoLayout = false;
147
+ this.resizeListener = null;
148
+ if (this.resizeObserver) {
149
+ this.resizeObserver.disconnect();
150
+ this.resizeObserver = null;
151
+ }
152
+ }
153
+ unrealCanvas() {
154
+ if (!this.unreal && this.parentView) {
155
+ const view = this.view;
156
+ if (view)
157
+ view.remove();
158
+ this.view = this.parentView;
159
+ this.unreal = true;
160
+ }
161
+ }
162
+ destroy() {
163
+ if (this.view) {
164
+ this.stopAutoLayout();
165
+ if (!this.unreal) {
166
+ const view = this.view;
167
+ if (view.parentElement)
168
+ view.remove();
169
+ }
170
+ super.destroy();
171
+ }
172
+ }
173
+ }
174
+
175
+ canvasPatch(CanvasRenderingContext2D.prototype);
176
+ canvasPatch(Path2D.prototype);
177
+
178
+ const { mineType, fileType } = FileHelper;
179
+ Object.assign(Creator, {
180
+ canvas: (options, manager) => new LeaferCanvas(options, manager),
181
+ image: (options) => new LeaferImage(options)
182
+ });
183
+ function useCanvas(_canvasType, _power) {
184
+ Platform.origin = {
185
+ createCanvas(width, height) {
186
+ const canvas = document.createElement('canvas');
187
+ canvas.width = width;
188
+ canvas.height = height;
189
+ return canvas;
190
+ },
191
+ canvasToDataURL: (canvas, type, quality) => canvas.toDataURL(mineType(type), quality),
192
+ canvasToBolb: (canvas, type, quality) => new Promise((resolve) => canvas.toBlob(resolve, mineType(type), quality)),
193
+ canvasSaveAs: (canvas, filename, quality) => {
194
+ const url = canvas.toDataURL(mineType(fileType(filename)), quality);
195
+ return Platform.origin.download(url, filename);
196
+ },
197
+ download(url, filename) {
198
+ return new Promise((resolve) => {
199
+ let el = document.createElement('a');
200
+ el.href = url;
201
+ el.download = filename;
202
+ document.body.appendChild(el);
203
+ el.click();
204
+ document.body.removeChild(el);
205
+ resolve();
206
+ });
207
+ },
208
+ loadImage(src) {
209
+ return new Promise((resolve, reject) => {
210
+ const img = new Image();
211
+ const { crossOrigin } = Platform.image;
212
+ if (crossOrigin) {
213
+ img.setAttribute('crossOrigin', crossOrigin);
214
+ img.crossOrigin = crossOrigin;
215
+ }
216
+ img.onload = () => { resolve(img); };
217
+ img.onerror = (e) => { reject(e); };
218
+ img.src = Platform.image.getRealURL(src);
219
+ });
220
+ }
221
+ };
222
+ Platform.event = {
223
+ stopDefault(origin) { origin.preventDefault(); },
224
+ stopNow(origin) { origin.stopImmediatePropagation(); },
225
+ stop(origin) { origin.stopPropagation(); }
226
+ };
227
+ Platform.canvas = Creator.canvas();
228
+ Platform.conicGradientSupport = !!Platform.canvas.context.createConicGradient;
229
+ }
230
+ Platform.name = 'web';
231
+ Platform.isMobile = 'ontouchstart' in window;
232
+ Platform.requestRender = function (render) { window.requestAnimationFrame(render); };
233
+ Platform.devicePixelRatio = Math.max(1, devicePixelRatio);
234
+ const { userAgent } = navigator;
235
+ if (userAgent.indexOf("Firefox") > -1) {
236
+ Platform.conicGradientRotate90 = true;
237
+ Platform.intWheelDeltaY = true;
238
+ Platform.syncDomFont = true;
239
+ }
240
+ else if (userAgent.indexOf("Safari") > -1 && userAgent.indexOf("Chrome") === -1) {
241
+ Platform.fullImageShadow = true;
242
+ }
243
+ if (userAgent.indexOf('Windows') > -1) {
244
+ Platform.os = 'Windows';
245
+ Platform.intWheelDeltaY = true;
246
+ }
247
+ else if (userAgent.indexOf('Mac') > -1) {
248
+ Platform.os = 'Mac';
249
+ }
250
+ else if (userAgent.indexOf('Linux') > -1) {
251
+ Platform.os = 'Linux';
252
+ }
6
253
 
7
254
  class Watcher {
8
255
  get childrenChanged() { return this.hasAdd || this.hasRemove || this.hasVisible; }
@@ -10,7 +257,7 @@ class Watcher {
10
257
  if (this.hasRemove) {
11
258
  const updatedList = new LeafList();
12
259
  this.__updatedList.list.forEach(item => { if (item.leafer)
13
- updatedList.push(item); });
260
+ updatedList.add(item); });
14
261
  return updatedList;
15
262
  }
16
263
  else {
@@ -45,7 +292,7 @@ class Watcher {
45
292
  this.target.emit(RenderEvent.REQUEST);
46
293
  }
47
294
  __onAttrChange(event) {
48
- this.__updatedList.push(event.target);
295
+ this.__updatedList.add(event.target);
49
296
  this.update();
50
297
  }
51
298
  __onChildEvent(event) {
@@ -55,12 +302,12 @@ class Watcher {
55
302
  }
56
303
  else {
57
304
  this.hasRemove = true;
58
- this.__updatedList.push(event.parent);
305
+ this.__updatedList.add(event.parent);
59
306
  }
60
307
  this.update();
61
308
  }
62
309
  __pushChild(child) {
63
- this.__updatedList.push(child);
310
+ this.__updatedList.add(child);
64
311
  if (child.isBranch)
65
312
  this.__loopChildren(child);
66
313
  }
@@ -99,22 +346,22 @@ class Watcher {
99
346
  }
100
347
  }
101
348
 
102
- const { updateAllWorldMatrix: updateAllWorldMatrix$1, updateAllWorldOpacity } = LeafHelper;
349
+ const { updateAllMatrix: updateAllMatrix$1, updateBounds: updateOneBounds, updateAllWorldOpacity } = LeafHelper;
103
350
  const { pushAllChildBranch, pushAllParent } = BranchHelper;
104
351
  function updateMatrix(updateList, levelList) {
105
352
  let layout;
106
353
  updateList.list.forEach(leaf => {
107
354
  layout = leaf.__layout;
108
- if (levelList.without(leaf) && !layout.useZoomProxy) {
355
+ if (levelList.without(leaf) && !layout.proxyZoom) {
109
356
  if (layout.matrixChanged) {
110
- updateAllWorldMatrix$1(leaf);
111
- levelList.push(leaf);
357
+ updateAllMatrix$1(leaf, true);
358
+ levelList.add(leaf);
112
359
  if (leaf.isBranch)
113
360
  pushAllChildBranch(leaf, levelList);
114
361
  pushAllParent(leaf, levelList);
115
362
  }
116
363
  else if (layout.boundsChanged) {
117
- levelList.push(leaf);
364
+ levelList.add(leaf);
118
365
  if (leaf.isBranch)
119
366
  leaf.__tempNumber = 0;
120
367
  pushAllParent(leaf, levelList);
@@ -123,20 +370,21 @@ function updateMatrix(updateList, levelList) {
123
370
  });
124
371
  }
125
372
  function updateBounds(boundsList) {
126
- let itemList, branch;
373
+ let list, branch, children;
127
374
  boundsList.sort(true);
128
375
  boundsList.levels.forEach(level => {
129
- itemList = boundsList.levelMap[level];
130
- for (let i = 0, len = itemList.length; i < len; i++) {
131
- branch = itemList[i];
376
+ list = boundsList.levelMap[level];
377
+ for (let i = 0, len = list.length; i < len; i++) {
378
+ branch = list[i];
132
379
  if (branch.isBranch && branch.__tempNumber) {
133
- for (let j = 0, jLen = branch.children.length; j < jLen; j++) {
134
- if (!branch.children[j].isBranch) {
135
- branch.children[j].__updateWorldBounds();
380
+ children = branch.children;
381
+ for (let j = 0, jLen = children.length; j < jLen; j++) {
382
+ if (!children[j].isBranch) {
383
+ updateOneBounds(children[j]);
136
384
  }
137
385
  }
138
386
  }
139
- branch.__updateWorldBounds();
387
+ updateOneBounds(branch);
140
388
  }
141
389
  });
142
390
  }
@@ -149,7 +397,7 @@ function updateChange(updateList) {
149
397
  }
150
398
 
151
399
  const { worldBounds } = LeafBoundsHelper;
152
- const { setByListWithHandle } = BoundsHelper;
400
+ const bigBounds = { x: 0, y: 0, width: 100000, height: 100000 };
153
401
  class LayoutBlockData {
154
402
  constructor(list) {
155
403
  this.updatedBounds = new Bounds();
@@ -160,14 +408,20 @@ class LayoutBlockData {
160
408
  this.updatedList = list;
161
409
  }
162
410
  setBefore() {
163
- setByListWithHandle(this.beforeBounds, this.updatedList.list, worldBounds);
411
+ this.beforeBounds.setListWithFn(this.updatedList.list, worldBounds);
164
412
  }
165
413
  setAfter() {
166
- setByListWithHandle(this.afterBounds, this.updatedList.list, worldBounds);
167
- this.updatedBounds.setByList([this.beforeBounds, this.afterBounds]);
414
+ const { list } = this.updatedList;
415
+ if (list.some(leaf => leaf.noBounds)) {
416
+ this.afterBounds.set(bigBounds);
417
+ }
418
+ else {
419
+ this.afterBounds.setListWithFn(list, worldBounds);
420
+ }
421
+ this.updatedBounds.setList([this.beforeBounds, this.afterBounds]);
168
422
  }
169
423
  merge(data) {
170
- this.updatedList.pushList(data.updatedList.list);
424
+ this.updatedList.addList(data.updatedList.list);
171
425
  this.beforeBounds.add(data.beforeBounds);
172
426
  this.afterBounds.add(data.afterBounds);
173
427
  this.updatedBounds.add(data.updatedBounds);
@@ -177,8 +431,7 @@ class LayoutBlockData {
177
431
  }
178
432
  }
179
433
 
180
- const { updateAllWorldMatrix, updateAllChange } = LeafHelper;
181
- const { pushAllBranchStack, updateWorldBoundsByBranchStack } = BranchHelper;
434
+ const { updateAllMatrix, updateAllChange } = LeafHelper;
182
435
  const debug$2 = Debug.get('Layouter');
183
436
  class Layouter {
184
437
  constructor(target, userConfig) {
@@ -255,12 +508,15 @@ class Layouter {
255
508
  const { target, __updatedList: updateList } = this;
256
509
  const { BEFORE, LAYOUT, AFTER } = LayoutEvent;
257
510
  const blocks = this.getBlocks(updateList);
258
- blocks.forEach(item => { item.setBefore(); });
511
+ blocks.forEach(item => item.setBefore());
259
512
  target.emitEvent(new LayoutEvent(BEFORE, blocks, this.times));
513
+ this.extraBlock = null;
260
514
  updateList.sort();
261
515
  updateMatrix(updateList, this.__levelList);
262
516
  updateBounds(this.__levelList);
263
517
  updateChange(updateList);
518
+ if (this.extraBlock)
519
+ blocks.push(this.extraBlock);
264
520
  blocks.forEach(item => item.setAfter());
265
521
  target.emitEvent(new LayoutEvent(LAYOUT, blocks, this.times));
266
522
  target.emitEvent(new LayoutEvent(AFTER, blocks, this.times));
@@ -283,17 +539,22 @@ class Layouter {
283
539
  Run.end(t);
284
540
  }
285
541
  static fullLayout(target) {
286
- updateAllWorldMatrix(target);
542
+ updateAllMatrix(target, true);
287
543
  if (target.isBranch) {
288
- const branchStack = [target];
289
- pushAllBranchStack(target, branchStack);
290
- updateWorldBoundsByBranchStack(branchStack);
544
+ BranchHelper.updateBounds(target);
291
545
  }
292
546
  else {
293
- target.__updateWorldBounds();
547
+ LeafHelper.updateBounds(target);
294
548
  }
295
549
  updateAllChange(target);
296
550
  }
551
+ addExtra(leaf) {
552
+ if (!this.__updatedList.has(leaf)) {
553
+ const { updatedList, beforeBounds } = this.extraBlock || (this.extraBlock = new LayoutBlockData([]));
554
+ updatedList.length ? beforeBounds.add(leaf.__world) : beforeBounds.set(leaf.__world);
555
+ updatedList.add(leaf);
556
+ }
557
+ }
297
558
  createBlock(data) {
298
559
  return new LayoutBlockData(data);
299
560
  }
@@ -321,8 +582,7 @@ class Layouter {
321
582
  if (this.target) {
322
583
  this.stop();
323
584
  this.__removeListenEvents();
324
- this.target = null;
325
- this.config = null;
585
+ this.target = this.config = null;
326
586
  }
327
587
  }
328
588
  }
@@ -403,6 +663,10 @@ class Renderer {
403
663
  }
404
664
  else {
405
665
  this.requestLayout();
666
+ if (this.ignore) {
667
+ this.ignore = this.rendering = false;
668
+ return;
669
+ }
406
670
  this.emitRender(RenderEvent.BEFORE);
407
671
  if (this.config.usePartRender && this.totalTimes > 1) {
408
672
  this.partRender();
@@ -424,8 +688,7 @@ class Renderer {
424
688
  const { canvas, updateBlocks: list } = this;
425
689
  if (!list)
426
690
  return debug$1.warn('PartRender: need update attr');
427
- if (list.some(block => block.includes(this.target.__world)))
428
- this.mergeBlocks();
691
+ this.mergeBlocks();
429
692
  list.forEach(block => { if (canvas.bounds.hit(block) && !block.isEmpty())
430
693
  this.clipRender(block); });
431
694
  }
@@ -434,17 +697,17 @@ class Renderer {
434
697
  const { canvas } = this;
435
698
  const bounds = block.getIntersect(canvas.bounds);
436
699
  const includes = block.includes(this.target.__world);
437
- const realBounds = new Bounds().copy(bounds);
700
+ const realBounds = new Bounds(bounds);
438
701
  canvas.save();
439
702
  if (includes && !Debug.showRepaint) {
440
703
  canvas.clear();
441
704
  }
442
705
  else {
443
- bounds.spread(1 + 1 / this.canvas.pixelRatio).ceil();
706
+ bounds.spread(10 + 1 / this.canvas.pixelRatio).ceil();
444
707
  canvas.clearWorld(bounds, true);
445
708
  canvas.clipWorld(bounds, true);
446
709
  }
447
- this.__render(bounds, realBounds);
710
+ this.__render(bounds, includes, realBounds);
448
711
  canvas.restore();
449
712
  Run.end(t);
450
713
  }
@@ -453,12 +716,12 @@ class Renderer {
453
716
  const { canvas } = this;
454
717
  canvas.save();
455
718
  canvas.clear();
456
- this.__render(canvas.bounds);
719
+ this.__render(canvas.bounds, true);
457
720
  canvas.restore();
458
721
  Run.end(t);
459
722
  }
460
- __render(bounds, realBounds) {
461
- const options = (bounds === null || bounds === void 0 ? void 0 : bounds.includes(this.target.__world)) ? {} : { bounds };
723
+ __render(bounds, includes, realBounds) {
724
+ const options = bounds.includes(this.target.__world) ? { includes } : { bounds, includes };
462
725
  if (this.needFill)
463
726
  this.canvas.fillWorld(bounds, this.config.fill);
464
727
  if (Debug.showRepaint)
@@ -484,7 +747,7 @@ class Renderer {
484
747
  const { updateBlocks: list } = this;
485
748
  if (list) {
486
749
  const bounds = new Bounds();
487
- bounds.setByList(list);
750
+ bounds.setList(list);
488
751
  list.length = 0;
489
752
  list.push(bounds);
490
753
  }
@@ -493,12 +756,12 @@ class Renderer {
493
756
  const startTime = Date.now();
494
757
  Platform.requestRender(() => {
495
758
  this.FPS = Math.min(60, Math.ceil(1000 / (Date.now() - startTime)));
496
- if (this.changed) {
497
- if (this.running && this.canvas.view)
759
+ if (this.running) {
760
+ this.target.emit(AnimateEvent.FRAME);
761
+ if (this.changed && this.canvas.view)
498
762
  this.render();
763
+ this.target.emit(RenderEvent.NEXT);
499
764
  }
500
- if (this.running)
501
- this.target.emit(AnimateEvent.FRAME);
502
765
  if (this.target)
503
766
  this.__requestRender();
504
767
  });
@@ -511,9 +774,12 @@ class Renderer {
511
774
  const bounds = new Bounds(0, 0, width, height);
512
775
  if (!bounds.includes(this.target.__world) || this.needFill || !e.samePixelRatio) {
513
776
  this.addBlock(this.canvas.bounds);
514
- this.target.forceUpdate('blendMode');
777
+ this.target.forceUpdate('surface');
778
+ return;
515
779
  }
516
780
  }
781
+ this.addBlock(new Bounds(0, 0, 1, 1));
782
+ this.changed = true;
517
783
  }
518
784
  __onLayoutEnd(event) {
519
785
  if (event.data)
@@ -524,7 +790,7 @@ class Renderer {
524
790
  empty = (!leaf.__world.width || !leaf.__world.height);
525
791
  if (empty) {
526
792
  if (!leaf.isLeafer)
527
- debug$1.warn(leaf.innerName, ': empty');
793
+ debug$1.tip(leaf.innerName, ': empty');
528
794
  empty = (!leaf.isBranch || leaf.isBranchLeaf);
529
795
  }
530
796
  return empty;
@@ -551,15 +817,13 @@ class Renderer {
551
817
  if (this.target) {
552
818
  this.stop();
553
819
  this.__removeListenEvents();
554
- this.target = null;
555
- this.canvas = null;
556
- this.config = null;
820
+ this.target = this.canvas = this.config = null;
557
821
  }
558
822
  }
559
823
  }
560
824
 
561
825
  const { hitRadiusPoint } = BoundsHelper;
562
- class FindPath {
826
+ class Picker {
563
827
  constructor(target, selector) {
564
828
  this.target = target;
565
829
  this.selector = selector;
@@ -571,51 +835,59 @@ class FindPath {
571
835
  options = {};
572
836
  const through = options.through || false;
573
837
  const ignoreHittable = options.ignoreHittable || false;
838
+ const target = options.target || this.target;
574
839
  this.exclude = options.exclude || null;
575
840
  this.point = { x: hitPoint.x, y: hitPoint.y, radiusX: hitRadius, radiusY: hitRadius };
576
- this.findList = [];
577
- this.eachFind(this.target.children, this.target.__onlyHitMask);
578
- const list = this.findList;
579
- const leaf = this.getBestMatchLeaf();
841
+ this.findList = new LeafList(options.findList);
842
+ if (!options.findList)
843
+ this.hitBranch(target);
844
+ const { list } = this.findList;
845
+ const leaf = this.getBestMatchLeaf(list, options.bottomList, ignoreHittable);
580
846
  const path = ignoreHittable ? this.getPath(leaf) : this.getHitablePath(leaf);
581
847
  this.clear();
582
- return through ? { path, leaf, throughPath: list.length ? this.getThroughPath(list) : path } : { path, leaf };
848
+ return through ? { path, target: leaf, throughPath: list.length ? this.getThroughPath(list) : path } : { path, target: leaf };
583
849
  }
584
- getBestMatchLeaf() {
585
- const { findList: targets } = this;
586
- if (targets.length > 1) {
850
+ getBestMatchLeaf(list, bottomList, ignoreHittable) {
851
+ if (list.length) {
587
852
  let find;
588
- this.findList = [];
853
+ this.findList = new LeafList();
589
854
  const { x, y } = this.point;
590
855
  const point = { x, y, radiusX: 0, radiusY: 0 };
591
- for (let i = 0, len = targets.length; i < len; i++) {
592
- find = targets[i];
593
- if (LeafHelper.worldHittable(find)) {
856
+ for (let i = 0, len = list.length; i < len; i++) {
857
+ find = list[i];
858
+ if (ignoreHittable || LeafHelper.worldHittable(find)) {
594
859
  this.hitChild(find, point);
595
860
  if (this.findList.length)
596
- return this.findList[0];
861
+ return this.findList.list[0];
597
862
  }
598
863
  }
599
864
  }
600
- return targets[0];
865
+ if (bottomList) {
866
+ for (let i = 0, len = bottomList.length; i < len; i++) {
867
+ this.hitChild(bottomList[i].target, this.point, bottomList[i].proxy);
868
+ if (this.findList.length)
869
+ return this.findList.list[0];
870
+ }
871
+ }
872
+ return list[0];
601
873
  }
602
874
  getPath(leaf) {
603
875
  const path = new LeafList();
604
876
  while (leaf) {
605
- path.push(leaf);
877
+ path.add(leaf);
606
878
  leaf = leaf.parent;
607
879
  }
608
- path.push(this.target);
880
+ path.add(this.target);
609
881
  return path;
610
882
  }
611
883
  getHitablePath(leaf) {
612
- const path = this.getPath(leaf);
884
+ const path = this.getPath(leaf && leaf.hittable ? leaf : null);
613
885
  let item, hittablePath = new LeafList();
614
886
  for (let i = path.list.length - 1; i > -1; i--) {
615
887
  item = path.list[i];
616
888
  if (!item.__.hittable)
617
889
  break;
618
- hittablePath.unshift(item);
890
+ hittablePath.addAt(item, 0);
619
891
  if (!item.__.hitChildren)
620
892
  break;
621
893
  }
@@ -634,17 +906,20 @@ class FindPath {
634
906
  leaf = path.list[j];
635
907
  if (nextPath && nextPath.has(leaf))
636
908
  break;
637
- throughPath.push(leaf);
909
+ throughPath.add(leaf);
638
910
  }
639
911
  }
640
912
  return throughPath;
641
913
  }
914
+ hitBranch(branch) {
915
+ this.eachFind(branch.children, branch.__onlyHitMask);
916
+ }
642
917
  eachFind(children, hitMask) {
643
918
  let child, hit;
644
919
  const { point } = this, len = children.length;
645
920
  for (let i = len - 1; i > -1; i--) {
646
921
  child = children[i];
647
- if (!child.__.visible || (hitMask && !child.__.isMask))
922
+ if (!child.__.visible || (hitMask && !child.__.mask))
648
923
  continue;
649
924
  hit = child.__.hitRadius ? true : hitRadiusPoint(child.__world, point);
650
925
  if (child.isBranch) {
@@ -660,11 +935,15 @@ class FindPath {
660
935
  }
661
936
  }
662
937
  }
663
- hitChild(child, point) {
938
+ hitChild(child, point, proxy) {
664
939
  if (this.exclude && this.exclude.has(child))
665
940
  return;
666
- if (child.__hitWorld(point))
667
- this.findList.push(child);
941
+ if (child.__hitWorld(point)) {
942
+ const { parent } = child;
943
+ if (parent && parent.__hasMask && !child.__.mask && !parent.children.some(item => item.__.mask && item.__hitWorld(point)))
944
+ return;
945
+ this.findList.add(proxy || child);
946
+ }
668
947
  }
669
948
  clear() {
670
949
  this.point = null;
@@ -676,120 +955,128 @@ class FindPath {
676
955
  }
677
956
  }
678
957
 
958
+ const { Yes, NoAndSkip, YesAndSkip } = Answer;
959
+ const idCondition = {}, classNameCondition = {}, tagCondition = {};
679
960
  class Selector {
680
961
  constructor(target, userConfig) {
681
962
  this.config = {};
682
- this.innerIdList = {};
683
- this.idList = {};
684
- this.classNameList = {};
685
- this.tagNameList = {};
963
+ this.innerIdMap = {};
964
+ this.idMap = {};
965
+ this.methods = {
966
+ id: (leaf, name) => leaf.id === name ? (this.idMap[name] = leaf, 1) : 0,
967
+ innerId: (leaf, innerId) => leaf.innerId === innerId ? (this.innerIdMap[innerId] = leaf, 1) : 0,
968
+ className: (leaf, name) => leaf.className === name ? 1 : 0,
969
+ tag: (leaf, name) => leaf.__tag === name ? 1 : 0,
970
+ tags: (leaf, nameMap) => nameMap[leaf.__tag] ? 1 : 0
971
+ };
686
972
  this.target = target;
687
973
  if (userConfig)
688
974
  this.config = DataHelper.default(userConfig, this.config);
689
- this.findPath = new FindPath(target, this);
975
+ this.picker = new Picker(target, this);
690
976
  this.__listenEvents();
691
977
  }
978
+ getBy(condition, branch, one, options) {
979
+ switch (typeof condition) {
980
+ case 'number':
981
+ const leaf = this.getByInnerId(condition, branch);
982
+ return one ? leaf : (leaf ? [leaf] : []);
983
+ case 'string':
984
+ switch (condition[0]) {
985
+ case '#':
986
+ idCondition.id = condition.substring(1), condition = idCondition;
987
+ break;
988
+ case '.':
989
+ classNameCondition.className = condition.substring(1), condition = classNameCondition;
990
+ break;
991
+ default:
992
+ tagCondition.tag = condition, condition = tagCondition;
993
+ }
994
+ case 'object':
995
+ if (condition.id !== undefined) {
996
+ const leaf = this.getById(condition.id, branch);
997
+ return one ? leaf : (leaf ? [leaf] : []);
998
+ }
999
+ else if (condition.tag) {
1000
+ const { tag } = condition, isArray = tag instanceof Array;
1001
+ return this.getByMethod(isArray ? this.methods.tags : this.methods.tag, branch, one, isArray ? DataHelper.toMap(tag) : tag);
1002
+ }
1003
+ else {
1004
+ return this.getByMethod(this.methods.className, branch, one, condition.className);
1005
+ }
1006
+ case 'function':
1007
+ return this.getByMethod(condition, branch, one, options);
1008
+ }
1009
+ }
692
1010
  getByPoint(hitPoint, hitRadius, options) {
693
1011
  if (Platform.name === 'node')
694
1012
  this.target.emit(LayoutEvent.CHECK_UPDATE);
695
- return this.findPath.getByPoint(hitPoint, hitRadius, options);
696
- }
697
- find(name, branch) {
698
- if (typeof name === 'number') {
699
- return this.getByInnerId(name, branch);
700
- }
701
- else if (name.startsWith('#')) {
702
- return this.getById(name.substring(1), branch);
703
- }
704
- else if (name.startsWith('.')) {
705
- return this.getByClassName(name.substring(1), branch);
706
- }
707
- else {
708
- return this.getByTagName(name, branch);
709
- }
1013
+ return this.picker.getByPoint(hitPoint, hitRadius, options);
710
1014
  }
711
- getByInnerId(name, branch) {
712
- let cache = this.innerIdList[name];
1015
+ getByInnerId(innerId, branch) {
1016
+ const cache = this.innerIdMap[innerId];
713
1017
  if (cache)
714
1018
  return cache;
715
- if (!branch)
716
- branch = this.target;
717
- let find;
718
- this.loopFind(branch, (leaf) => {
719
- if (leaf.innerId === name) {
720
- find = leaf;
721
- this.innerIdList[name] = find;
722
- return true;
723
- }
724
- else {
725
- return false;
726
- }
727
- });
728
- return find;
1019
+ this.eachFind(this.toChildren(branch), this.methods.innerId, null, innerId);
1020
+ return this.findLeaf;
729
1021
  }
730
- getById(name, branch) {
731
- let cache = this.idList[name];
732
- if (cache)
1022
+ getById(id, branch) {
1023
+ const cache = this.idMap[id];
1024
+ if (cache && LeafHelper.hasParent(cache, branch || this.target))
733
1025
  return cache;
734
- if (!branch)
735
- branch = this.target;
736
- let find;
737
- this.loopFind(branch, (leaf) => {
738
- if (leaf.id === name) {
739
- find = leaf;
740
- this.idList[name] = find;
741
- return true;
742
- }
743
- else {
744
- return false;
745
- }
746
- });
747
- return find;
748
- }
749
- getByClassName(name, branch) {
750
- if (!branch)
751
- branch = this.target;
752
- let find = [];
753
- this.loopFind(branch, (leaf) => {
754
- if (leaf.className === name)
755
- find.push(leaf);
756
- return false;
757
- });
758
- return find;
759
- }
760
- getByTagName(name, branch) {
761
- if (!branch)
762
- branch = this.target;
763
- let find = [];
764
- this.loopFind(branch, (leaf) => {
765
- if (leaf.__tag === name)
766
- find.push(leaf);
767
- return false;
768
- });
769
- return find;
1026
+ this.eachFind(this.toChildren(branch), this.methods.id, null, id);
1027
+ return this.findLeaf;
770
1028
  }
771
- loopFind(branch, find) {
772
- if (find(branch))
773
- return;
774
- const { children } = branch;
775
- for (let i = 0, len = children.length; i < len; i++) {
776
- branch = children[i];
777
- if (find(branch))
778
- return;
779
- if (branch.isBranch)
780
- this.loopFind(branch, find);
781
- }
1029
+ getByClassName(className, branch) {
1030
+ return this.getByMethod(this.methods.className, branch, false, className);
782
1031
  }
783
- __onRemoveChild(event) {
784
- const target = event.target;
785
- if (this.idList[target.id])
786
- this.idList[target.id] = null;
787
- if (this.innerIdList[target.id])
788
- this.innerIdList[target.innerId] = null;
1032
+ getByTag(tag, branch) {
1033
+ return this.getByMethod(this.methods.tag, branch, false, tag);
1034
+ }
1035
+ getByMethod(method, branch, one, options) {
1036
+ const list = one ? null : [];
1037
+ this.eachFind(this.toChildren(branch), method, list, options);
1038
+ return list || this.findLeaf;
1039
+ }
1040
+ eachFind(children, method, list, options) {
1041
+ let child, result;
1042
+ for (let i = 0, len = children.length; i < len; i++) {
1043
+ child = children[i];
1044
+ result = method(child, options);
1045
+ if (result === Yes || result === YesAndSkip) {
1046
+ if (list) {
1047
+ list.push(child);
1048
+ }
1049
+ else {
1050
+ this.findLeaf = child;
1051
+ return;
1052
+ }
1053
+ }
1054
+ if (child.isBranch && result < NoAndSkip)
1055
+ this.eachFind(child.children, method, list, options);
1056
+ }
1057
+ }
1058
+ toChildren(branch) {
1059
+ this.findLeaf = null;
1060
+ return [branch || this.target];
1061
+ }
1062
+ __onRemoveChild(event) {
1063
+ const { id, innerId } = event.child;
1064
+ if (this.idMap[id])
1065
+ delete this.idMap[id];
1066
+ if (this.innerIdMap[innerId])
1067
+ delete this.innerIdMap[innerId];
1068
+ }
1069
+ __checkIdChange(event) {
1070
+ if (event.attrName === 'id') {
1071
+ const id = event.oldValue;
1072
+ if (this.idMap[id])
1073
+ delete this.idMap[id];
1074
+ }
789
1075
  }
790
1076
  __listenEvents() {
791
1077
  this.__eventIds = [
792
- this.target.on_(ChildEvent.REMOVE, this.__onRemoveChild, this)
1078
+ this.target.on_(ChildEvent.REMOVE, this.__onRemoveChild, this),
1079
+ this.target.on_(PropertyEvent.CHANGE, this.__checkIdChange, this)
793
1080
  ];
794
1081
  }
795
1082
  __removeListenEvents() {
@@ -799,11 +1086,10 @@ class Selector {
799
1086
  destroy() {
800
1087
  if (this.__eventIds.length) {
801
1088
  this.__removeListenEvents();
802
- this.findPath.destroy();
803
- this.innerIdList = {};
804
- this.idList = {};
805
- this.classNameList = {};
806
- this.tagNameList = {};
1089
+ this.picker.destroy();
1090
+ this.findLeaf = null;
1091
+ this.innerIdMap = {};
1092
+ this.idMap = {};
807
1093
  }
808
1094
  }
809
1095
  }
@@ -816,169 +1102,6 @@ Object.assign(Creator, {
816
1102
  });
817
1103
  Platform.layout = Layouter.fullLayout;
818
1104
 
819
- const debug = Debug.get('LeaferCanvas');
820
- class LeaferCanvas extends LeaferCanvasBase {
821
- init() {
822
- const { view } = this.config;
823
- view ? this.__createViewFrom(view) : this.__createView();
824
- const { style } = this.view;
825
- style.display || (style.display = 'block');
826
- this.parentView = this.view.parentElement;
827
- if (Platform.syncDomFont && !this.parentView) {
828
- this.view.style.display = 'none';
829
- document.body.appendChild(this.view);
830
- }
831
- this.__createContext();
832
- if (!this.autoLayout)
833
- this.resize(this.config);
834
- }
835
- set backgroundColor(color) { this.view.style.backgroundColor = color; }
836
- get backgroundColor() { return this.view.style.backgroundColor; }
837
- set hittable(hittable) { this.view.style.pointerEvents = hittable ? 'auto' : 'none'; }
838
- get hittable() { return this.view.style.pointerEvents !== 'none'; }
839
- __createView() {
840
- this.view = document.createElement('canvas');
841
- }
842
- setCursor(cursor) {
843
- const list = [];
844
- this.eachCursor(cursor, list);
845
- if (typeof list[list.length - 1] === 'object')
846
- list.push('default');
847
- this.view.style.cursor = list.map(item => (typeof item === 'object') ? `url(${item.url}) ${item.x || 0} ${item.y || 0}` : item).join(',');
848
- }
849
- eachCursor(cursor, list, level = 0) {
850
- level++;
851
- if (cursor instanceof Array) {
852
- cursor.forEach(item => this.eachCursor(item, list, level));
853
- }
854
- else {
855
- const custom = typeof cursor === 'string' && Cursor.get(cursor);
856
- if (custom && level < 2) {
857
- this.eachCursor(custom, list, level);
858
- }
859
- else {
860
- list.push(cursor);
861
- }
862
- }
863
- }
864
- __createViewFrom(inputView) {
865
- let find = (typeof inputView === 'string') ? document.getElementById(inputView) : inputView;
866
- if (find) {
867
- if (find instanceof HTMLCanvasElement) {
868
- this.view = find;
869
- }
870
- else {
871
- let parent = find;
872
- if (find === window || find === document) {
873
- const div = document.createElement('div');
874
- const { style } = div;
875
- style.position = 'absolute';
876
- style.top = style.bottom = style.left = style.right = '0px';
877
- document.body.appendChild(div);
878
- parent = div;
879
- }
880
- this.__createView();
881
- const view = this.view;
882
- if (parent.hasChildNodes()) {
883
- const { style } = view;
884
- style.position = 'absolute';
885
- style.top = style.left = '0px';
886
- parent.style.position || (parent.style.position = 'relative');
887
- }
888
- parent.appendChild(view);
889
- }
890
- }
891
- else {
892
- debug.error(`no id: ${inputView}`);
893
- this.__createView();
894
- }
895
- }
896
- updateViewSize() {
897
- const { width, height, pixelRatio } = this;
898
- const { style } = this.view;
899
- style.width = width + 'px';
900
- style.height = height + 'px';
901
- this.view.width = width * pixelRatio;
902
- this.view.height = height * pixelRatio;
903
- }
904
- updateClientBounds() {
905
- this.clientBounds = this.view.getBoundingClientRect();
906
- }
907
- startAutoLayout(autoBounds, listener) {
908
- this.autoBounds = autoBounds;
909
- this.resizeListener = listener;
910
- try {
911
- this.resizeObserver = new ResizeObserver((entries) => {
912
- this.updateClientBounds();
913
- for (const entry of entries)
914
- this.checkAutoBounds(entry.contentRect);
915
- });
916
- const parent = this.parentView;
917
- if (parent) {
918
- this.resizeObserver.observe(parent);
919
- this.checkAutoBounds(parent.getBoundingClientRect());
920
- }
921
- }
922
- catch (_a) {
923
- this.imitateResizeObserver();
924
- }
925
- }
926
- imitateResizeObserver() {
927
- if (this.autoLayout) {
928
- if (this.parentView)
929
- this.checkAutoBounds(this.parentView.getBoundingClientRect());
930
- Platform.requestRender(this.imitateResizeObserver.bind(this));
931
- }
932
- }
933
- checkAutoBounds(parentSize) {
934
- const view = this.view;
935
- const { x, y, width, height } = this.autoBounds.getBoundsFrom(parentSize);
936
- if (width !== this.width || height !== this.height) {
937
- const { style } = view;
938
- const { pixelRatio } = this;
939
- style.marginLeft = x + 'px';
940
- style.marginTop = y + 'px';
941
- const size = { width, height, pixelRatio };
942
- const oldSize = {};
943
- DataHelper.copyAttrs(oldSize, this, canvasSizeAttrs);
944
- this.resize(size);
945
- if (this.width !== undefined)
946
- this.resizeListener(new ResizeEvent(size, oldSize));
947
- }
948
- }
949
- stopAutoLayout() {
950
- this.autoLayout = false;
951
- this.resizeListener = null;
952
- if (this.resizeObserver) {
953
- this.resizeObserver.disconnect();
954
- this.resizeObserver = null;
955
- }
956
- }
957
- unrealCanvas() {
958
- if (!this.unreal && this.parentView) {
959
- const view = this.view;
960
- if (view)
961
- view.remove();
962
- this.view = this.parentView;
963
- this.unreal = true;
964
- }
965
- }
966
- destroy() {
967
- if (this.view) {
968
- this.stopAutoLayout();
969
- if (!this.unreal) {
970
- const view = this.view;
971
- if (view.parentElement)
972
- view.remove();
973
- }
974
- super.destroy();
975
- }
976
- }
977
- }
978
-
979
- canvasPatch(CanvasRenderingContext2D.prototype);
980
- canvasPatch(Path2D.prototype);
981
-
982
1105
  const PointerEventHelper = {
983
1106
  convert(e, local) {
984
1107
  const base = InteractionHelper.getBase(e);
@@ -1025,7 +1148,7 @@ const WheelEventHelper = {
1025
1148
  let { zoomMode, zoomSpeed } = config;
1026
1149
  const delta = e.deltaY || e.deltaX;
1027
1150
  if (zoomMode) {
1028
- zoom = !e.deltaX && (Platform.intWheelDeltaY ? Math.abs(delta) > 17 : Math.ceil(delta) !== delta);
1151
+ zoom = (zoomMode === 'mouse') ? true : (!e.deltaX && (Platform.intWheelDeltaY ? Math.abs(delta) > 17 : Math.ceil(delta) !== delta));
1029
1152
  if (e.shiftKey || e.metaKey || e.ctrlKey)
1030
1153
  zoom = true;
1031
1154
  }
@@ -1035,7 +1158,7 @@ const WheelEventHelper = {
1035
1158
  if (zoom) {
1036
1159
  zoomSpeed = MathHelper.within(zoomSpeed, 0, 1);
1037
1160
  const min = e.deltaY ? config.delta.y : config.delta.x;
1038
- scale = 1 - delta / (min * 25 * (1 - zoomSpeed) + 10);
1161
+ scale = 1 - delta / (min * 4) * zoomSpeed;
1039
1162
  if (scale < 0.5)
1040
1163
  scale = 0.5;
1041
1164
  if (scale >= 1.5)
@@ -1062,6 +1185,7 @@ class Interaction extends InteractionBase {
1062
1185
  'pointerdown': this.onPointerDown,
1063
1186
  'mousedown': this.onMouseDown,
1064
1187
  'touchstart': this.onTouchStart,
1188
+ 'contextmenu': this.onContextMenu,
1065
1189
  'wheel': this.onWheel,
1066
1190
  'gesturestart': this.onGesturestart,
1067
1191
  'gesturechange': this.onGesturechange,
@@ -1102,12 +1226,6 @@ class Interaction extends InteractionBase {
1102
1226
  this.windowEvents = {};
1103
1227
  }
1104
1228
  }
1105
- getLocal(p, updateClient) {
1106
- if (updateClient)
1107
- this.canvas.updateClientBounds();
1108
- const { clientBounds } = this.canvas;
1109
- return { x: p.clientX - clientBounds.x, y: p.clientY - clientBounds.y };
1110
- }
1111
1229
  getTouches(touches) {
1112
1230
  const list = [];
1113
1231
  for (let i = 0, len = touches.length; i < len; i++) {
@@ -1134,6 +1252,11 @@ class Interaction extends InteractionBase {
1134
1252
  onKeyUp(e) {
1135
1253
  this.keyUp(KeyEventHelper.convert(e));
1136
1254
  }
1255
+ onContextMenu(e) {
1256
+ if (this.config.pointer.preventDefaultMenu)
1257
+ e.preventDefault();
1258
+ this.menu(PointerEventHelper.convert(e, this.getLocal(e)));
1259
+ }
1137
1260
  onScroll() {
1138
1261
  this.canvas.updateClientBounds();
1139
1262
  }
@@ -1261,17 +1384,23 @@ class Interaction extends InteractionBase {
1261
1384
  onWheel(e) {
1262
1385
  this.preventDefaultWheel(e);
1263
1386
  const { wheel } = this.config;
1387
+ if (wheel.disabled)
1388
+ return;
1264
1389
  const scale = wheel.getScale ? wheel.getScale(e, wheel) : WheelEventHelper.getScale(e, wheel);
1265
1390
  const local = this.getLocal(e);
1266
1391
  const eventBase = InteractionHelper.getBase(e);
1267
1392
  scale !== 1 ? this.zoom(getZoomEventData(local, scale, eventBase)) : this.move(getMoveEventData(local, wheel.getMove ? wheel.getMove(e, wheel) : WheelEventHelper.getMove(e, wheel), eventBase));
1268
1393
  }
1269
1394
  onGesturestart(e) {
1395
+ if (this.useMultiTouch)
1396
+ return;
1270
1397
  this.preventDefaultWheel(e);
1271
1398
  this.lastGestureScale = 1;
1272
1399
  this.lastGestureRotation = 0;
1273
1400
  }
1274
1401
  onGesturechange(e) {
1402
+ if (this.useMultiTouch)
1403
+ return;
1275
1404
  this.preventDefaultWheel(e);
1276
1405
  const local = this.getLocal(e);
1277
1406
  const eventBase = InteractionHelper.getBase(e);
@@ -1285,9 +1414,34 @@ class Interaction extends InteractionBase {
1285
1414
  this.lastGestureRotation = e.rotation;
1286
1415
  }
1287
1416
  onGestureend(e) {
1417
+ if (this.useMultiTouch)
1418
+ return;
1288
1419
  this.preventDefaultWheel(e);
1289
1420
  this.transformEnd();
1290
1421
  }
1422
+ setCursor(cursor) {
1423
+ super.setCursor(cursor);
1424
+ const list = [];
1425
+ this.eachCursor(cursor, list);
1426
+ if (typeof list[list.length - 1] === 'object')
1427
+ list.push('default');
1428
+ this.canvas.view.style.cursor = list.map(item => (typeof item === 'object') ? `url(${item.url}) ${item.x || 0} ${item.y || 0}` : item).join(',');
1429
+ }
1430
+ eachCursor(cursor, list, level = 0) {
1431
+ level++;
1432
+ if (cursor instanceof Array) {
1433
+ cursor.forEach(item => this.eachCursor(item, list, level));
1434
+ }
1435
+ else {
1436
+ const custom = typeof cursor === 'string' && Cursor.get(cursor);
1437
+ if (custom && level < 2) {
1438
+ this.eachCursor(custom, list, level);
1439
+ }
1440
+ else {
1441
+ list.push(cursor);
1442
+ }
1443
+ }
1444
+ }
1291
1445
  destroy() {
1292
1446
  if (this.view) {
1293
1447
  super.destroy();
@@ -1297,219 +1451,586 @@ class Interaction extends InteractionBase {
1297
1451
  }
1298
1452
  }
1299
1453
 
1300
- const { mineType, fileType } = FileHelper;
1301
- Object.assign(Creator, {
1302
- canvas: (options, manager) => new LeaferCanvas(options, manager),
1303
- image: (options) => new LeaferImage(options),
1304
- hitCanvas: (options, manager) => new LeaferCanvas(options, manager),
1305
- interaction: (target, canvas, selector, options) => new Interaction(target, canvas, selector, options),
1306
- });
1307
- function useCanvas(_canvasType, _power) {
1308
- Platform.origin = {
1309
- createCanvas(width, height) {
1310
- const canvas = document.createElement('canvas');
1311
- canvas.width = width;
1312
- canvas.height = height;
1313
- return canvas;
1314
- },
1315
- canvasToDataURL: (canvas, type, quality) => canvas.toDataURL(mineType(type), quality),
1316
- canvasToBolb: (canvas, type, quality) => new Promise((resolve) => canvas.toBlob(resolve, mineType(type), quality)),
1317
- canvasSaveAs: (canvas, filename, quality) => {
1318
- return new Promise((resolve) => {
1319
- let el = document.createElement('a');
1320
- el.href = canvas.toDataURL(mineType(fileType(filename)), quality);
1321
- el.download = filename;
1322
- document.body.appendChild(el);
1323
- el.click();
1324
- document.body.removeChild(el);
1325
- resolve();
1326
- });
1327
- },
1328
- loadImage(src) {
1329
- return new Promise((resolve, reject) => {
1330
- const img = new Image();
1331
- img.setAttribute('crossOrigin', 'anonymous');
1332
- img.crossOrigin = 'anonymous';
1333
- img.onload = () => { resolve(img); };
1334
- img.onerror = (e) => { reject(e); };
1335
- if (!src.startsWith('data:') && Platform.imageSuffix)
1336
- src += (src.includes("?") ? "&" : "?") + Platform.imageSuffix;
1337
- img.src = src;
1454
+ function fillText(ui, canvas) {
1455
+ let row;
1456
+ const { rows, decorationY, decorationHeight } = ui.__.__textDrawData;
1457
+ for (let i = 0, len = rows.length; i < len; i++) {
1458
+ row = rows[i];
1459
+ if (row.text) {
1460
+ canvas.fillText(row.text, row.x, row.y);
1461
+ }
1462
+ else if (row.data) {
1463
+ row.data.forEach(charData => {
1464
+ canvas.fillText(charData.char, charData.x, row.y);
1338
1465
  });
1339
1466
  }
1340
- };
1341
- Platform.canvas = Creator.canvas();
1342
- Platform.conicGradientSupport = !!Platform.canvas.context.createConicGradient;
1467
+ if (decorationY)
1468
+ canvas.fillRect(row.x, row.y + decorationY, row.width, decorationHeight);
1469
+ }
1343
1470
  }
1344
- Platform.name = 'web';
1345
- Platform.isMobile = 'ontouchstart' in window;
1346
- Platform.requestRender = function (render) { window.requestAnimationFrame(render); };
1347
- Platform.devicePixelRatio = devicePixelRatio;
1348
- Platform.realtimeLayout = true;
1349
- const { userAgent } = navigator;
1350
- if (userAgent.indexOf("Firefox") > -1) {
1351
- Platform.conicGradientRotate90 = true;
1352
- Platform.intWheelDeltaY = true;
1353
- Platform.syncDomFont = true;
1471
+
1472
+ function fill(fill, ui, canvas) {
1473
+ canvas.fillStyle = fill;
1474
+ ui.__.__font ? fillText(ui, canvas) : (ui.__.windingRule ? canvas.fill(ui.__.windingRule) : canvas.fill());
1354
1475
  }
1355
- else if (userAgent.indexOf("Safari") > -1 && userAgent.indexOf("Chrome") === -1) {
1356
- Platform.fullImageShadow = true;
1476
+ function fills(fills, ui, canvas) {
1477
+ let item;
1478
+ const { windingRule, __font } = ui.__;
1479
+ for (let i = 0, len = fills.length; i < len; i++) {
1480
+ item = fills[i];
1481
+ if (item.image && PaintImage.checkImage(ui, canvas, item, !__font))
1482
+ continue;
1483
+ if (item.style) {
1484
+ canvas.fillStyle = item.style;
1485
+ if (item.transform) {
1486
+ canvas.save();
1487
+ canvas.transform(item.transform);
1488
+ if (item.blendMode)
1489
+ canvas.blendMode = item.blendMode;
1490
+ __font ? fillText(ui, canvas) : (windingRule ? canvas.fill(windingRule) : canvas.fill());
1491
+ canvas.restore();
1492
+ }
1493
+ else {
1494
+ if (item.blendMode) {
1495
+ canvas.saveBlendMode(item.blendMode);
1496
+ __font ? fillText(ui, canvas) : (windingRule ? canvas.fill(windingRule) : canvas.fill());
1497
+ canvas.restoreBlendMode();
1498
+ }
1499
+ else {
1500
+ __font ? fillText(ui, canvas) : (windingRule ? canvas.fill(windingRule) : canvas.fill());
1501
+ }
1502
+ }
1503
+ }
1504
+ }
1357
1505
  }
1358
- if (userAgent.indexOf('Windows') > -1) {
1359
- Platform.os = 'Windows';
1360
- Platform.intWheelDeltaY = true;
1506
+
1507
+ function strokeText(stroke, ui, canvas) {
1508
+ const { strokeAlign } = ui.__;
1509
+ const isStrokes = typeof stroke !== 'string';
1510
+ switch (strokeAlign) {
1511
+ case 'center':
1512
+ canvas.setStroke(isStrokes ? undefined : stroke, ui.__.strokeWidth, ui.__);
1513
+ isStrokes ? drawStrokesStyle(stroke, true, ui, canvas) : drawTextStroke(ui, canvas);
1514
+ break;
1515
+ case 'inside':
1516
+ drawAlignStroke('inside', stroke, isStrokes, ui, canvas);
1517
+ break;
1518
+ case 'outside':
1519
+ drawAlignStroke('outside', stroke, isStrokes, ui, canvas);
1520
+ break;
1521
+ }
1361
1522
  }
1362
- else if (userAgent.indexOf('Mac') > -1) {
1363
- Platform.os = 'Mac';
1523
+ function drawAlignStroke(align, stroke, isStrokes, ui, canvas) {
1524
+ const { __strokeWidth, __font } = ui.__;
1525
+ const out = canvas.getSameCanvas(true, true);
1526
+ out.setStroke(isStrokes ? undefined : stroke, __strokeWidth * 2, ui.__);
1527
+ out.font = __font;
1528
+ isStrokes ? drawStrokesStyle(stroke, true, ui, out) : drawTextStroke(ui, out);
1529
+ out.blendMode = align === 'outside' ? 'destination-out' : 'destination-in';
1530
+ fillText(ui, out);
1531
+ out.blendMode = 'normal';
1532
+ if (ui.__worldFlipped) {
1533
+ canvas.copyWorldByReset(out, ui.__nowWorld);
1534
+ }
1535
+ else {
1536
+ canvas.copyWorldToInner(out, ui.__nowWorld, ui.__layout.renderBounds);
1537
+ }
1538
+ out.recycle(ui.__nowWorld);
1364
1539
  }
1365
- else if (userAgent.indexOf('Linux') > -1) {
1366
- Platform.os = 'Linux';
1540
+ function drawTextStroke(ui, canvas) {
1541
+ let row;
1542
+ const { rows, decorationY, decorationHeight } = ui.__.__textDrawData;
1543
+ for (let i = 0, len = rows.length; i < len; i++) {
1544
+ row = rows[i];
1545
+ if (row.text) {
1546
+ canvas.strokeText(row.text, row.x, row.y);
1547
+ }
1548
+ else if (row.data) {
1549
+ row.data.forEach(charData => {
1550
+ canvas.strokeText(charData.char, charData.x, row.y);
1551
+ });
1552
+ }
1553
+ if (decorationY)
1554
+ canvas.strokeRect(row.x, row.y + decorationY, row.width, decorationHeight);
1555
+ }
1556
+ }
1557
+ function drawStrokesStyle(strokes, isText, ui, canvas) {
1558
+ let item;
1559
+ for (let i = 0, len = strokes.length; i < len; i++) {
1560
+ item = strokes[i];
1561
+ if (item.image && PaintImage.checkImage(ui, canvas, item, false))
1562
+ continue;
1563
+ if (item.style) {
1564
+ canvas.strokeStyle = item.style;
1565
+ if (item.blendMode) {
1566
+ canvas.saveBlendMode(item.blendMode);
1567
+ isText ? drawTextStroke(ui, canvas) : canvas.stroke();
1568
+ canvas.restoreBlendMode();
1569
+ }
1570
+ else {
1571
+ isText ? drawTextStroke(ui, canvas) : canvas.stroke();
1572
+ }
1573
+ }
1574
+ }
1575
+ }
1576
+
1577
+ function stroke(stroke, ui, canvas) {
1578
+ const options = ui.__;
1579
+ const { __strokeWidth, strokeAlign, __font } = options;
1580
+ if (!__strokeWidth)
1581
+ return;
1582
+ if (__font) {
1583
+ strokeText(stroke, ui, canvas);
1584
+ }
1585
+ else {
1586
+ switch (strokeAlign) {
1587
+ case 'center':
1588
+ canvas.setStroke(stroke, __strokeWidth, options);
1589
+ canvas.stroke();
1590
+ break;
1591
+ case 'inside':
1592
+ canvas.save();
1593
+ canvas.setStroke(stroke, __strokeWidth * 2, options);
1594
+ options.windingRule ? canvas.clip(options.windingRule) : canvas.clip();
1595
+ canvas.stroke();
1596
+ canvas.restore();
1597
+ break;
1598
+ case 'outside':
1599
+ const out = canvas.getSameCanvas(true, true);
1600
+ out.setStroke(stroke, __strokeWidth * 2, options);
1601
+ ui.__drawRenderPath(out);
1602
+ out.stroke();
1603
+ options.windingRule ? out.clip(options.windingRule) : out.clip();
1604
+ out.clearWorld(ui.__layout.renderBounds);
1605
+ if (ui.__worldFlipped) {
1606
+ canvas.copyWorldByReset(out, ui.__nowWorld);
1607
+ }
1608
+ else {
1609
+ canvas.copyWorldToInner(out, ui.__nowWorld, ui.__layout.renderBounds);
1610
+ }
1611
+ out.recycle(ui.__nowWorld);
1612
+ break;
1613
+ }
1614
+ }
1615
+ }
1616
+ function strokes(strokes, ui, canvas) {
1617
+ const options = ui.__;
1618
+ const { __strokeWidth, strokeAlign, __font } = options;
1619
+ if (!__strokeWidth)
1620
+ return;
1621
+ if (__font) {
1622
+ strokeText(strokes, ui, canvas);
1623
+ }
1624
+ else {
1625
+ switch (strokeAlign) {
1626
+ case 'center':
1627
+ canvas.setStroke(undefined, __strokeWidth, options);
1628
+ drawStrokesStyle(strokes, false, ui, canvas);
1629
+ break;
1630
+ case 'inside':
1631
+ canvas.save();
1632
+ canvas.setStroke(undefined, __strokeWidth * 2, options);
1633
+ options.windingRule ? canvas.clip(options.windingRule) : canvas.clip();
1634
+ drawStrokesStyle(strokes, false, ui, canvas);
1635
+ canvas.restore();
1636
+ break;
1637
+ case 'outside':
1638
+ const { renderBounds } = ui.__layout;
1639
+ const out = canvas.getSameCanvas(true, true);
1640
+ ui.__drawRenderPath(out);
1641
+ out.setStroke(undefined, __strokeWidth * 2, options);
1642
+ drawStrokesStyle(strokes, false, ui, out);
1643
+ options.windingRule ? out.clip(options.windingRule) : out.clip();
1644
+ out.clearWorld(renderBounds);
1645
+ if (ui.__worldFlipped) {
1646
+ canvas.copyWorldByReset(out, ui.__nowWorld);
1647
+ }
1648
+ else {
1649
+ canvas.copyWorldToInner(out, ui.__nowWorld, renderBounds);
1650
+ }
1651
+ out.recycle(ui.__nowWorld);
1652
+ break;
1653
+ }
1654
+ }
1367
1655
  }
1368
1656
 
1369
- const { get: get$4, rotateOfOuter: rotateOfOuter$2, translate: translate$1, scaleOfOuter: scaleOfOuter$2, scale: scaleHelper$1, rotate } = MatrixHelper;
1370
- function fillOrFitMode(data, mode, box, width, height, rotation) {
1371
- const transform = get$4();
1372
- const swap = rotation && rotation !== 180;
1373
- const sw = box.width / (swap ? height : width);
1374
- const sh = box.height / (swap ? width : height);
1375
- const scale = mode === 'fit' ? Math.min(sw, sh) : Math.max(sw, sh);
1376
- const x = box.x + (box.width - width * scale) / 2;
1377
- const y = box.y + (box.height - height * scale) / 2;
1378
- translate$1(transform, x, y);
1379
- scaleHelper$1(transform, scale);
1657
+ const { getSpread, getOuterOf, getByMove, getIntersectData } = BoundsHelper;
1658
+ function shape(ui, current, options) {
1659
+ const canvas = current.getSameCanvas();
1660
+ const nowWorld = ui.__nowWorld;
1661
+ let bounds, fitMatrix, shapeBounds, worldCanvas;
1662
+ let { scaleX, scaleY } = nowWorld;
1663
+ if (scaleX < 0)
1664
+ scaleX = -scaleX;
1665
+ if (scaleY < 0)
1666
+ scaleY = -scaleY;
1667
+ if (current.bounds.includes(nowWorld)) {
1668
+ worldCanvas = canvas;
1669
+ bounds = shapeBounds = nowWorld;
1670
+ }
1671
+ else {
1672
+ const { renderShapeSpread: spread } = ui.__layout;
1673
+ const worldClipBounds = getIntersectData(spread ? getSpread(current.bounds, scaleX === scaleY ? spread * scaleX : [spread * scaleY, spread * scaleX]) : current.bounds, nowWorld);
1674
+ fitMatrix = current.bounds.getFitMatrix(worldClipBounds);
1675
+ let { a: fitScaleX, d: fitScaleY } = fitMatrix;
1676
+ if (fitMatrix.a < 1) {
1677
+ worldCanvas = current.getSameCanvas();
1678
+ ui.__renderShape(worldCanvas, options);
1679
+ scaleX *= fitScaleX;
1680
+ scaleY *= fitScaleY;
1681
+ }
1682
+ shapeBounds = getOuterOf(nowWorld, fitMatrix);
1683
+ bounds = getByMove(shapeBounds, -fitMatrix.e, -fitMatrix.f);
1684
+ if (options.matrix) {
1685
+ const { matrix } = options;
1686
+ fitMatrix.multiply(matrix);
1687
+ fitScaleX *= matrix.scaleX;
1688
+ fitScaleY *= matrix.scaleY;
1689
+ }
1690
+ options = Object.assign(Object.assign({}, options), { matrix: fitMatrix.withScale(fitScaleX, fitScaleY) });
1691
+ }
1692
+ ui.__renderShape(canvas, options);
1693
+ return {
1694
+ canvas, matrix: fitMatrix, bounds,
1695
+ worldCanvas, shapeBounds, scaleX, scaleY
1696
+ };
1697
+ }
1698
+
1699
+ let recycleMap;
1700
+ function compute(attrName, ui) {
1701
+ const data = ui.__, leafPaints = [];
1702
+ let paints = data.__input[attrName], hasOpacityPixel;
1703
+ if (!(paints instanceof Array))
1704
+ paints = [paints];
1705
+ recycleMap = PaintImage.recycleImage(attrName, data);
1706
+ for (let i = 0, len = paints.length, item; i < len; i++) {
1707
+ item = getLeafPaint(attrName, paints[i], ui);
1708
+ if (item)
1709
+ leafPaints.push(item);
1710
+ }
1711
+ data['_' + attrName] = leafPaints.length ? leafPaints : undefined;
1712
+ if (leafPaints.length && leafPaints[0].image)
1713
+ hasOpacityPixel = leafPaints[0].image.hasOpacityPixel;
1714
+ if (attrName === 'fill') {
1715
+ data.__pixelFill = hasOpacityPixel;
1716
+ }
1717
+ else {
1718
+ data.__pixelStroke = hasOpacityPixel;
1719
+ }
1720
+ }
1721
+ function getLeafPaint(attrName, paint, ui) {
1722
+ if (typeof paint !== 'object' || paint.visible === false || paint.opacity === 0)
1723
+ return undefined;
1724
+ const { boxBounds } = ui.__layout;
1725
+ switch (paint.type) {
1726
+ case 'solid':
1727
+ let { type, blendMode, color, opacity } = paint;
1728
+ return { type, blendMode, style: ColorConvert.string(color, opacity) };
1729
+ case 'image':
1730
+ return PaintImage.image(ui, attrName, paint, boxBounds, !recycleMap || !recycleMap[paint.url]);
1731
+ case 'linear':
1732
+ return PaintGradient.linearGradient(paint, boxBounds);
1733
+ case 'radial':
1734
+ return PaintGradient.radialGradient(paint, boxBounds);
1735
+ case 'angular':
1736
+ return PaintGradient.conicGradient(paint, boxBounds);
1737
+ default:
1738
+ return paint.r !== undefined ? { type: 'solid', style: ColorConvert.string(paint) } : undefined;
1739
+ }
1740
+ }
1741
+
1742
+ const PaintModule = {
1743
+ compute,
1744
+ fill,
1745
+ fills,
1746
+ fillText,
1747
+ stroke,
1748
+ strokes,
1749
+ strokeText,
1750
+ drawTextStroke,
1751
+ shape
1752
+ };
1753
+
1754
+ let origin = {};
1755
+ const { get: get$3, rotateOfOuter: rotateOfOuter$1, translate: translate$1, scaleOfOuter: scaleOfOuter$1, scale: scaleHelper, rotate } = MatrixHelper;
1756
+ function fillOrFitMode(data, box, x, y, scaleX, scaleY, rotation) {
1757
+ const transform = get$3();
1758
+ translate$1(transform, box.x + x, box.y + y);
1759
+ scaleHelper(transform, scaleX, scaleY);
1380
1760
  if (rotation)
1381
- rotateOfOuter$2(transform, { x: box.x + box.width / 2, y: box.y + box.height / 2 }, rotation);
1382
- data.scaleX = data.scaleY = scale;
1761
+ rotateOfOuter$1(transform, { x: box.x + box.width / 2, y: box.y + box.height / 2 }, rotation);
1383
1762
  data.transform = transform;
1384
1763
  }
1385
- function clipMode(data, box, offset, scale, rotation) {
1386
- const transform = get$4();
1387
- translate$1(transform, box.x, box.y);
1388
- if (offset)
1389
- translate$1(transform, offset.x, offset.y);
1390
- if (scale) {
1391
- typeof scale === 'number' ? scaleHelper$1(transform, scale) : scaleHelper$1(transform, scale.x, scale.y);
1392
- data.scaleX = transform.a;
1393
- data.scaleY = transform.d;
1394
- }
1764
+ function clipMode(data, box, x, y, scaleX, scaleY, rotation) {
1765
+ const transform = get$3();
1766
+ translate$1(transform, box.x + x, box.y + y);
1767
+ if (scaleX)
1768
+ scaleHelper(transform, scaleX, scaleY);
1395
1769
  if (rotation)
1396
1770
  rotate(transform, rotation);
1397
1771
  data.transform = transform;
1398
1772
  }
1399
- function repeatMode(data, box, width, height, scale, rotation) {
1400
- const transform = get$4();
1773
+ function repeatMode(data, box, width, height, x, y, scaleX, scaleY, rotation, align) {
1774
+ const transform = get$3();
1401
1775
  if (rotation) {
1402
- rotate(transform, rotation);
1403
- switch (rotation) {
1404
- case 90:
1405
- translate$1(transform, height, 0);
1406
- break;
1407
- case 180:
1408
- translate$1(transform, width, height);
1409
- break;
1410
- case 270:
1411
- translate$1(transform, 0, width);
1412
- break;
1776
+ if (align === 'center') {
1777
+ rotateOfOuter$1(transform, { x: width / 2, y: height / 2 }, rotation);
1778
+ }
1779
+ else {
1780
+ rotate(transform, rotation);
1781
+ switch (rotation) {
1782
+ case 90:
1783
+ translate$1(transform, height, 0);
1784
+ break;
1785
+ case 180:
1786
+ translate$1(transform, width, height);
1787
+ break;
1788
+ case 270:
1789
+ translate$1(transform, 0, width);
1790
+ break;
1791
+ }
1413
1792
  }
1414
1793
  }
1415
- translate$1(transform, box.x, box.y);
1416
- if (scale) {
1417
- scaleOfOuter$2(transform, box, scale);
1418
- data.scaleX = data.scaleY = scale;
1419
- }
1794
+ origin.x = box.x + x;
1795
+ origin.y = box.y + y;
1796
+ translate$1(transform, origin.x, origin.y);
1797
+ if (scaleX)
1798
+ scaleOfOuter$1(transform, origin, scaleX, scaleY);
1420
1799
  data.transform = transform;
1421
1800
  }
1422
1801
 
1423
- const { get: get$3, translate } = MatrixHelper;
1802
+ const { get: get$2, translate } = MatrixHelper;
1803
+ const tempBox = new Bounds();
1804
+ const tempPoint = {};
1424
1805
  function createData(leafPaint, image, paint, box) {
1425
- let { width, height } = image;
1426
- const { opacity, mode, offset, scale, rotation, blendMode } = paint;
1427
- const sameBox = box.width === width && box.height === height;
1806
+ const { blendMode } = paint;
1428
1807
  if (blendMode)
1429
1808
  leafPaint.blendMode = blendMode;
1430
- const data = leafPaint.data = { mode };
1809
+ leafPaint.data = getPatternData(paint, box, image);
1810
+ }
1811
+ function getPatternData(paint, box, image) {
1812
+ let { width, height } = image;
1813
+ if (paint.padding)
1814
+ box = tempBox.set(box).shrink(paint.padding);
1815
+ const { opacity, mode, align, offset, scale, size, rotation, repeat } = paint;
1816
+ const sameBox = box.width === width && box.height === height;
1817
+ const data = { mode };
1818
+ const swapSize = align !== 'center' && (rotation || 0) % 180 === 90;
1819
+ const swapWidth = swapSize ? height : width, swapHeight = swapSize ? width : height;
1820
+ let x = 0, y = 0, scaleX, scaleY;
1821
+ if (!mode || mode === 'cover' || mode === 'fit') {
1822
+ if (!sameBox || rotation) {
1823
+ const sw = box.width / swapWidth, sh = box.height / swapHeight;
1824
+ scaleX = scaleY = mode === 'fit' ? Math.min(sw, sh) : Math.max(sw, sh);
1825
+ x += (box.width - width * scaleX) / 2, y += (box.height - height * scaleY) / 2;
1826
+ }
1827
+ }
1828
+ else if (size) {
1829
+ scaleX = (typeof size === 'number' ? size : size.width) / width;
1830
+ scaleY = (typeof size === 'number' ? size : size.height) / height;
1831
+ }
1832
+ else if (scale) {
1833
+ scaleX = typeof scale === 'number' ? scale : scale.x;
1834
+ scaleY = typeof scale === 'number' ? scale : scale.y;
1835
+ }
1836
+ if (align) {
1837
+ const imageBounds = { x, y, width: swapWidth, height: swapHeight };
1838
+ if (scaleX)
1839
+ imageBounds.width *= scaleX, imageBounds.height *= scaleY;
1840
+ AlignHelper.toPoint(align, imageBounds, box, tempPoint, true);
1841
+ x += tempPoint.x, y += tempPoint.y;
1842
+ }
1843
+ if (offset)
1844
+ x += offset.x, y += offset.y;
1431
1845
  switch (mode) {
1432
1846
  case 'strench':
1433
1847
  if (!sameBox)
1434
1848
  width = box.width, height = box.height;
1435
- if (box.x || box.y) {
1436
- data.transform = get$3();
1437
- translate(data.transform, box.x, box.y);
1438
- }
1439
1849
  break;
1850
+ case 'normal':
1440
1851
  case 'clip':
1441
- if (offset || scale || rotation)
1442
- clipMode(data, box, offset, scale, rotation);
1852
+ if (x || y || scaleX || rotation)
1853
+ clipMode(data, box, x, y, scaleX, scaleY, rotation);
1443
1854
  break;
1444
1855
  case 'repeat':
1445
- if (!sameBox || scale || rotation)
1446
- repeatMode(data, box, width, height, scale, rotation);
1856
+ if (!sameBox || scaleX || rotation)
1857
+ repeatMode(data, box, width, height, x, y, scaleX, scaleY, rotation, align);
1858
+ if (!repeat)
1859
+ data.repeat = 'repeat';
1447
1860
  break;
1448
1861
  case 'fit':
1449
1862
  case 'cover':
1450
1863
  default:
1451
- if (!sameBox || rotation)
1452
- fillOrFitMode(data, mode, box, width, height, rotation);
1864
+ if (scaleX)
1865
+ fillOrFitMode(data, box, x, y, scaleX, scaleY, rotation);
1866
+ }
1867
+ if (!data.transform) {
1868
+ if (box.x || box.y) {
1869
+ data.transform = get$2();
1870
+ translate(data.transform, box.x, box.y);
1871
+ }
1872
+ }
1873
+ if (scaleX && mode !== 'strench') {
1874
+ data.scaleX = scaleX;
1875
+ data.scaleY = scaleY;
1453
1876
  }
1454
1877
  data.width = width;
1455
1878
  data.height = height;
1456
1879
  if (opacity)
1457
1880
  data.opacity = opacity;
1881
+ if (repeat)
1882
+ data.repeat = typeof repeat === 'string' ? (repeat === 'x' ? 'repeat-x' : 'repeat-y') : 'repeat';
1883
+ return data;
1458
1884
  }
1459
1885
 
1460
- function image(ui, attrName, attrValue, box, firstUse) {
1461
- const leafPaint = { type: attrValue.type };
1462
- const image = leafPaint.image = ImageManager.get(attrValue);
1463
- const event = (firstUse || image.loading) && { target: ui, image, attrName, attrValue };
1886
+ let cache, box = new Bounds();
1887
+ const { isSame } = BoundsHelper;
1888
+ function image(ui, attrName, paint, boxBounds, firstUse) {
1889
+ let leafPaint, event;
1890
+ const image = ImageManager.get(paint);
1891
+ if (cache && paint === cache.paint && isSame(boxBounds, cache.boxBounds)) {
1892
+ leafPaint = cache.leafPaint;
1893
+ }
1894
+ else {
1895
+ leafPaint = { type: paint.type, image };
1896
+ cache = image.use > 1 ? { leafPaint, paint, boxBounds: box.set(boxBounds) } : null;
1897
+ }
1898
+ if (firstUse || image.loading)
1899
+ event = { image, attrName, attrValue: paint };
1464
1900
  if (image.ready) {
1465
- if (hasNaturalSize(ui, attrName, image))
1466
- createData(leafPaint, image, attrValue, box);
1901
+ checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds);
1467
1902
  if (firstUse) {
1468
- emit(ImageEvent.LOAD, event);
1469
- emit(ImageEvent.LOADED, event);
1903
+ onLoad(ui, event);
1904
+ onLoadSuccess(ui, event);
1470
1905
  }
1471
1906
  }
1472
1907
  else if (image.error) {
1473
- if (firstUse) {
1474
- ui.forceUpdate('surface');
1475
- event.error = image.error;
1476
- emit(ImageEvent.ERROR, event);
1477
- }
1908
+ if (firstUse)
1909
+ onLoadError(ui, event, image.error);
1478
1910
  }
1479
1911
  else {
1912
+ ignoreRender(ui, true);
1480
1913
  if (firstUse)
1481
- emit(ImageEvent.LOAD, event);
1914
+ onLoad(ui, event);
1482
1915
  leafPaint.loadId = image.load(() => {
1916
+ ignoreRender(ui, false);
1483
1917
  if (!ui.destroyed) {
1484
- if (hasNaturalSize(ui, attrName, image)) {
1485
- createData(leafPaint, image, attrValue, box);
1918
+ if (checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds)) {
1919
+ if (image.hasOpacityPixel)
1920
+ ui.__layout.hitCanvasChanged = true;
1486
1921
  ui.forceUpdate('surface');
1487
1922
  }
1488
- emit(ImageEvent.LOADED, event);
1923
+ onLoadSuccess(ui, event);
1489
1924
  }
1925
+ leafPaint.loadId = null;
1490
1926
  }, (error) => {
1491
- ui.forceUpdate('surface');
1492
- event.error = error;
1493
- emit(ImageEvent.ERROR, event);
1927
+ ignoreRender(ui, false);
1928
+ onLoadError(ui, event, error);
1929
+ leafPaint.loadId = null;
1494
1930
  });
1495
1931
  }
1496
1932
  return leafPaint;
1497
1933
  }
1498
- function hasNaturalSize(ui, attrName, image) {
1934
+ function checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds) {
1499
1935
  if (attrName === 'fill' && !ui.__.__naturalWidth) {
1500
- const { __: d } = ui;
1501
- d.__naturalWidth = image.width;
1502
- d.__naturalHeight = image.height;
1503
- if (!d.__getInput('width') || !d.__getInput('height')) {
1936
+ const data = ui.__;
1937
+ data.__naturalWidth = image.width / data.pixelRatio;
1938
+ data.__naturalHeight = image.height / data.pixelRatio;
1939
+ if (data.__autoSide) {
1504
1940
  ui.forceUpdate('width');
1941
+ if (ui.__proxyData) {
1942
+ ui.setProxyAttr('width', data.width);
1943
+ ui.setProxyAttr('height', data.height);
1944
+ }
1505
1945
  return false;
1506
1946
  }
1507
1947
  }
1948
+ if (!leafPaint.data)
1949
+ createData(leafPaint, image, paint, boxBounds);
1508
1950
  return true;
1509
1951
  }
1510
- function emit(type, data) {
1511
- if (data.target.hasEvent(type))
1512
- data.target.emitEvent(new ImageEvent(type, data));
1952
+ function onLoad(ui, event) {
1953
+ emit(ui, ImageEvent.LOAD, event);
1954
+ }
1955
+ function onLoadSuccess(ui, event) {
1956
+ emit(ui, ImageEvent.LOADED, event);
1957
+ }
1958
+ function onLoadError(ui, event, error) {
1959
+ event.error = error;
1960
+ ui.forceUpdate('surface');
1961
+ emit(ui, ImageEvent.ERROR, event);
1962
+ }
1963
+ function emit(ui, type, data) {
1964
+ if (ui.hasEvent(type))
1965
+ ui.emitEvent(new ImageEvent(type, data));
1966
+ }
1967
+ function ignoreRender(ui, value) {
1968
+ const { leafer } = ui;
1969
+ if (leafer && leafer.viewReady)
1970
+ leafer.renderer.ignore = value;
1971
+ }
1972
+
1973
+ const { get: get$1, scale, copy: copy$1 } = MatrixHelper;
1974
+ const { ceil, abs: abs$1 } = Math;
1975
+ function createPattern(ui, paint, pixelRatio) {
1976
+ let { scaleX, scaleY } = ImageManager.patternLocked ? ui.__world : ui.__nowWorld;
1977
+ const id = scaleX + '-' + scaleY;
1978
+ if (paint.patternId !== id && !ui.destroyed) {
1979
+ scaleX = abs$1(scaleX);
1980
+ scaleY = abs$1(scaleY);
1981
+ const { image, data } = paint;
1982
+ let imageScale, imageMatrix, { width, height, scaleX: sx, scaleY: sy, opacity, transform, repeat } = data;
1983
+ if (sx) {
1984
+ imageMatrix = get$1();
1985
+ copy$1(imageMatrix, transform);
1986
+ scale(imageMatrix, 1 / sx, 1 / sy);
1987
+ scaleX *= sx;
1988
+ scaleY *= sy;
1989
+ }
1990
+ scaleX *= pixelRatio;
1991
+ scaleY *= pixelRatio;
1992
+ width *= scaleX;
1993
+ height *= scaleY;
1994
+ const size = width * height;
1995
+ if (!repeat) {
1996
+ if (size > Platform.image.maxCacheSize)
1997
+ return false;
1998
+ }
1999
+ let maxSize = Platform.image.maxPatternSize;
2000
+ if (!image.isSVG) {
2001
+ const imageSize = image.width * image.height;
2002
+ if (maxSize > imageSize)
2003
+ maxSize = imageSize;
2004
+ }
2005
+ if (size > maxSize)
2006
+ imageScale = Math.sqrt(size / maxSize);
2007
+ if (imageScale) {
2008
+ scaleX /= imageScale;
2009
+ scaleY /= imageScale;
2010
+ width /= imageScale;
2011
+ height /= imageScale;
2012
+ }
2013
+ if (sx) {
2014
+ scaleX /= sx;
2015
+ scaleY /= sy;
2016
+ }
2017
+ if (transform || scaleX !== 1 || scaleY !== 1) {
2018
+ if (!imageMatrix) {
2019
+ imageMatrix = get$1();
2020
+ if (transform)
2021
+ copy$1(imageMatrix, transform);
2022
+ }
2023
+ scale(imageMatrix, 1 / scaleX, 1 / scaleY);
2024
+ }
2025
+ const canvas = image.getCanvas(ceil(width) || 1, ceil(height) || 1, opacity);
2026
+ const pattern = image.getPattern(canvas, repeat || (Platform.origin.noRepeat || 'no-repeat'), imageMatrix, paint);
2027
+ paint.style = pattern;
2028
+ paint.patternId = id;
2029
+ return true;
2030
+ }
2031
+ else {
2032
+ return false;
2033
+ }
1513
2034
  }
1514
2035
 
1515
2036
  /******************************************************************************
@@ -1544,80 +2065,24 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
1544
2065
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
1545
2066
  };
1546
2067
 
1547
- const { get: get$2, scale: scaleHelper, copy: copy$1 } = MatrixHelper;
1548
- function createPattern(ui, paint, pixelRatio) {
1549
- let { scaleX, scaleY } = ui.__world;
1550
- const id = scaleX + '-' + scaleY;
1551
- if (paint.patternId !== id && !ui.destroyed) {
1552
- paint.patternId = id;
1553
- scaleX = Math.abs(scaleX);
1554
- scaleY = Math.abs(scaleY);
1555
- const { image, data } = paint;
1556
- const maxWidth = image.isSVG ? 4096 : Math.min(image.width, 4096);
1557
- const maxHeight = image.isSVG ? 4096 : Math.min(image.height, 4096);
1558
- let scale, matrix, { width, height, scaleX: sx, scaleY: sy, opacity, transform, mode } = data;
1559
- if (sx) {
1560
- matrix = get$2();
1561
- copy$1(matrix, transform);
1562
- scaleHelper(matrix, 1 / sx, 1 / sy);
1563
- scaleX *= sx;
1564
- scaleY *= sy;
1565
- }
1566
- scaleX *= pixelRatio;
1567
- scaleY *= pixelRatio;
1568
- width *= scaleX;
1569
- height *= scaleY;
1570
- if (width > maxWidth || height > maxHeight) {
1571
- scale = Math.max(width / maxWidth, height / maxHeight);
1572
- }
1573
- if (scale) {
1574
- scaleX /= scale;
1575
- scaleY /= scale;
1576
- width /= scale;
1577
- height /= scale;
1578
- }
1579
- if (sx) {
1580
- scaleX /= sx;
1581
- scaleY /= sy;
1582
- }
1583
- if (transform || scaleX !== 1 || scaleY !== 1) {
1584
- if (!matrix) {
1585
- matrix = get$2();
1586
- if (transform)
1587
- copy$1(matrix, transform);
1588
- }
1589
- scaleHelper(matrix, 1 / scaleX, 1 / scaleY);
1590
- }
1591
- const style = Platform.canvas.createPattern(image.getCanvas(width < 1 ? 1 : width, height < 1 ? 1 : height, opacity), mode === 'repeat' ? 'repeat' : (Platform.origin.noRepeat || 'no-repeat'));
1592
- try {
1593
- if (paint.transform)
1594
- paint.transform = null;
1595
- if (matrix)
1596
- style.setTransform ? style.setTransform(matrix) : paint.transform = matrix;
1597
- }
1598
- catch (_a) {
1599
- paint.transform = matrix;
1600
- }
1601
- paint.style = style;
1602
- return true;
1603
- }
1604
- else {
1605
- return false;
1606
- }
1607
- }
1608
-
2068
+ const { abs } = Math;
1609
2069
  function checkImage(ui, canvas, paint, allowPaint) {
1610
- const { scaleX, scaleY } = ui.__world;
2070
+ const { scaleX, scaleY } = ImageManager.patternLocked ? ui.__world : ui.__nowWorld;
1611
2071
  if (!paint.data || paint.patternId === scaleX + '-' + scaleY) {
1612
2072
  return false;
1613
2073
  }
1614
2074
  else {
2075
+ const { data } = paint;
1615
2076
  if (allowPaint) {
1616
- if (paint.image.isSVG && paint.data.mode !== 'repeat') {
1617
- let { width, height } = paint.data;
1618
- width *= scaleX * canvas.pixelRatio;
1619
- height *= scaleY * canvas.pixelRatio;
1620
- allowPaint = width > 4096 || height > 4096;
2077
+ if (!data.repeat) {
2078
+ let { width, height } = data;
2079
+ width *= abs(scaleX) * canvas.pixelRatio;
2080
+ height *= abs(scaleY) * canvas.pixelRatio;
2081
+ if (data.scaleX) {
2082
+ width *= data.scaleX;
2083
+ height *= data.scaleY;
2084
+ }
2085
+ allowPaint = (width * height > Platform.image.maxCacheSize) || Export.running;
1621
2086
  }
1622
2087
  else {
1623
2088
  allowPaint = false;
@@ -1626,7 +2091,6 @@ function checkImage(ui, canvas, paint, allowPaint) {
1626
2091
  if (allowPaint) {
1627
2092
  canvas.save();
1628
2093
  canvas.clip();
1629
- const { data } = paint;
1630
2094
  if (paint.blendMode)
1631
2095
  canvas.blendMode = paint.blendMode;
1632
2096
  if (data.opacity)
@@ -1635,290 +2099,74 @@ function checkImage(ui, canvas, paint, allowPaint) {
1635
2099
  canvas.transform(data.transform);
1636
2100
  canvas.drawImage(paint.image.view, 0, 0, data.width, data.height);
1637
2101
  canvas.restore();
1638
- return true;
1639
- }
1640
- else {
1641
- if (!paint.style) {
1642
- createPattern(ui, paint, canvas.pixelRatio);
1643
- }
1644
- else {
1645
- ImageManager.patternTasker.add(() => __awaiter(this, void 0, void 0, function* () {
1646
- if (canvas.bounds.hit(ui.__world) && createPattern(ui, paint, canvas.pixelRatio))
1647
- ui.forceUpdate('surface');
1648
- }), 300);
1649
- }
1650
- return false;
1651
- }
1652
- }
1653
- }
1654
-
1655
- function recycleImage(data, attrName) {
1656
- const paints = (attrName === 'fill' ? data._fill : data._stroke);
1657
- if (paints instanceof Array) {
1658
- let image, recycleMap, input, url;
1659
- for (let i = 0, len = paints.length; i < len; i++) {
1660
- image = paints[i].image;
1661
- url = image && image.url;
1662
- if (url) {
1663
- if (!recycleMap)
1664
- recycleMap = {};
1665
- recycleMap[url] = true;
1666
- ImageManager.recycle(image);
1667
- if (image.loading) {
1668
- if (!input) {
1669
- input = (data.__input && data.__input[attrName]) || [];
1670
- if (!(input instanceof Array))
1671
- input = [input];
1672
- }
1673
- image.unload(paints[i].loadId, !input.some((item) => item.url === url));
1674
- }
1675
- }
1676
- }
1677
- return recycleMap;
1678
- }
1679
- return null;
1680
- }
1681
-
1682
- function fillText(ui, canvas) {
1683
- let row;
1684
- const { rows, decorationY, decorationHeight } = ui.__.__textDrawData;
1685
- for (let i = 0, len = rows.length; i < len; i++) {
1686
- row = rows[i];
1687
- if (row.text) {
1688
- canvas.fillText(row.text, row.x, row.y);
1689
- }
1690
- else if (row.data) {
1691
- row.data.forEach(charData => {
1692
- canvas.fillText(charData.char, charData.x, row.y);
1693
- });
1694
- }
1695
- if (decorationY)
1696
- canvas.fillRect(row.x, row.y + decorationY, row.width, decorationHeight);
1697
- }
1698
- }
1699
-
1700
- function fill(ui, canvas, fill) {
1701
- canvas.fillStyle = fill;
1702
- ui.__.__font ? fillText(ui, canvas) : (ui.__.windingRule ? canvas.fill(ui.__.windingRule) : canvas.fill());
1703
- }
1704
- function fills(ui, canvas, fills) {
1705
- let item;
1706
- const { windingRule, __font } = ui.__;
1707
- for (let i = 0, len = fills.length; i < len; i++) {
1708
- item = fills[i];
1709
- if (item.image && checkImage(ui, canvas, item, !__font))
1710
- continue;
1711
- if (item.style) {
1712
- canvas.fillStyle = item.style;
1713
- if (item.transform) {
1714
- canvas.save();
1715
- canvas.transform(item.transform);
1716
- if (item.blendMode)
1717
- canvas.blendMode = item.blendMode;
1718
- __font ? fillText(ui, canvas) : (windingRule ? canvas.fill(windingRule) : canvas.fill());
1719
- canvas.restore();
1720
- }
1721
- else {
1722
- if (item.blendMode) {
1723
- canvas.saveBlendMode(item.blendMode);
1724
- __font ? fillText(ui, canvas) : (windingRule ? canvas.fill(windingRule) : canvas.fill());
1725
- canvas.restoreBlendMode();
1726
- }
1727
- else {
1728
- __font ? fillText(ui, canvas) : (windingRule ? canvas.fill(windingRule) : canvas.fill());
1729
- }
1730
- }
1731
- }
1732
- }
1733
- }
1734
-
1735
- function strokeText(ui, canvas, stroke) {
1736
- const { strokeAlign } = ui.__;
1737
- const isStrokes = typeof stroke !== 'string';
1738
- switch (strokeAlign) {
1739
- case 'center':
1740
- canvas.setStroke(isStrokes ? undefined : stroke, ui.__.strokeWidth, ui.__);
1741
- isStrokes ? drawStrokesStyle(ui, stroke, canvas, true) : drawTextStroke(ui, canvas);
1742
- break;
1743
- case 'inside':
1744
- drawAlignStroke(ui, canvas, stroke, 'inside', isStrokes);
1745
- break;
1746
- case 'outside':
1747
- drawAlignStroke(ui, canvas, stroke, 'outside', isStrokes);
1748
- break;
1749
- }
1750
- }
1751
- function drawAlignStroke(ui, canvas, stroke, align, isStrokes) {
1752
- const { strokeWidth, __font } = ui.__;
1753
- const out = canvas.getSameCanvas(true);
1754
- out.setStroke(isStrokes ? undefined : stroke, strokeWidth * 2, ui.__);
1755
- out.font = __font;
1756
- isStrokes ? drawStrokesStyle(ui, stroke, out, true) : drawTextStroke(ui, out);
1757
- out.blendMode = align === 'outside' ? 'destination-out' : 'destination-in';
1758
- fillText(ui, out);
1759
- out.blendMode = 'normal';
1760
- canvas.copyWorldToInner(out, ui.__world, ui.__layout.renderBounds);
1761
- out.recycle();
1762
- }
1763
- function drawTextStroke(ui, canvas) {
1764
- let row;
1765
- const { rows, decorationY, decorationHeight } = ui.__.__textDrawData;
1766
- for (let i = 0, len = rows.length; i < len; i++) {
1767
- row = rows[i];
1768
- if (row.text) {
1769
- canvas.strokeText(row.text, row.x, row.y);
1770
- }
1771
- else if (row.data) {
1772
- row.data.forEach(charData => {
1773
- canvas.strokeText(charData.char, charData.x, row.y);
1774
- });
2102
+ return true;
1775
2103
  }
1776
- if (decorationY)
1777
- canvas.strokeRect(row.x, row.y + decorationY, row.width, decorationHeight);
1778
- }
1779
- }
1780
- function drawStrokesStyle(ui, strokes, canvas, isText) {
1781
- let item;
1782
- for (let i = 0, len = strokes.length; i < len; i++) {
1783
- item = strokes[i];
1784
- if (item.image && checkImage(ui, canvas, item, false))
1785
- continue;
1786
- if (item.style) {
1787
- canvas.strokeStyle = item.style;
1788
- if (item.blendMode) {
1789
- canvas.saveBlendMode(item.blendMode);
1790
- isText ? drawTextStroke(ui, canvas) : canvas.stroke();
1791
- canvas.restoreBlendMode();
2104
+ else {
2105
+ if (!paint.style || Export.running) {
2106
+ createPattern(ui, paint, canvas.pixelRatio);
1792
2107
  }
1793
2108
  else {
1794
- isText ? drawTextStroke(ui, canvas) : canvas.stroke();
2109
+ if (!paint.patternTask) {
2110
+ paint.patternTask = ImageManager.patternTasker.add(() => __awaiter(this, void 0, void 0, function* () {
2111
+ paint.patternTask = null;
2112
+ if (canvas.bounds.hit(ui.__nowWorld))
2113
+ createPattern(ui, paint, canvas.pixelRatio);
2114
+ ui.forceUpdate('surface');
2115
+ }), 300);
2116
+ }
1795
2117
  }
2118
+ return false;
1796
2119
  }
1797
2120
  }
1798
2121
  }
1799
2122
 
1800
- function stroke(ui, canvas, stroke) {
1801
- const options = ui.__;
1802
- const { strokeWidth, strokeAlign, __font } = options;
1803
- if (!strokeWidth)
1804
- return;
1805
- if (__font) {
1806
- strokeText(ui, canvas, stroke);
1807
- }
1808
- else {
1809
- switch (strokeAlign) {
1810
- case 'center':
1811
- canvas.setStroke(stroke, strokeWidth, options);
1812
- canvas.stroke();
1813
- break;
1814
- case 'inside':
1815
- canvas.save();
1816
- canvas.setStroke(stroke, strokeWidth * 2, options);
1817
- options.windingRule ? canvas.clip(options.windingRule) : canvas.clip();
1818
- canvas.stroke();
1819
- canvas.restore();
1820
- break;
1821
- case 'outside':
1822
- const out = canvas.getSameCanvas(true);
1823
- out.setStroke(stroke, strokeWidth * 2, ui.__);
1824
- ui.__drawRenderPath(out);
1825
- out.stroke();
1826
- options.windingRule ? out.clip(options.windingRule) : out.clip();
1827
- out.clearWorld(ui.__layout.renderBounds);
1828
- canvas.copyWorldToInner(out, ui.__world, ui.__layout.renderBounds);
1829
- out.recycle();
1830
- break;
1831
- }
1832
- }
1833
- }
1834
- function strokes(ui, canvas, strokes) {
1835
- const options = ui.__;
1836
- const { strokeWidth, strokeAlign, __font } = options;
1837
- if (!strokeWidth)
1838
- return;
1839
- if (__font) {
1840
- strokeText(ui, canvas, strokes);
1841
- }
1842
- else {
1843
- switch (strokeAlign) {
1844
- case 'center':
1845
- canvas.setStroke(undefined, strokeWidth, options);
1846
- drawStrokesStyle(ui, strokes, canvas);
1847
- break;
1848
- case 'inside':
1849
- canvas.save();
1850
- canvas.setStroke(undefined, strokeWidth * 2, options);
1851
- options.windingRule ? canvas.clip(options.windingRule) : canvas.clip();
1852
- drawStrokesStyle(ui, strokes, canvas);
1853
- canvas.restore();
1854
- break;
1855
- case 'outside':
1856
- const { renderBounds } = ui.__layout;
1857
- const out = canvas.getSameCanvas(true);
1858
- ui.__drawRenderPath(out);
1859
- out.setStroke(undefined, strokeWidth * 2, ui.__);
1860
- drawStrokesStyle(ui, strokes, out);
1861
- options.windingRule ? out.clip(options.windingRule) : out.clip();
1862
- out.clearWorld(renderBounds);
1863
- canvas.copyWorldToInner(out, ui.__world, renderBounds);
1864
- out.recycle();
1865
- break;
2123
+ function recycleImage(attrName, data) {
2124
+ const paints = data['_' + attrName];
2125
+ if (paints instanceof Array) {
2126
+ let image, recycleMap, input, url;
2127
+ for (let i = 0, len = paints.length; i < len; i++) {
2128
+ image = paints[i].image;
2129
+ url = image && image.url;
2130
+ if (url) {
2131
+ if (!recycleMap)
2132
+ recycleMap = {};
2133
+ recycleMap[url] = true;
2134
+ ImageManager.recycle(image);
2135
+ if (image.loading) {
2136
+ if (!input) {
2137
+ input = (data.__input && data.__input[attrName]) || [];
2138
+ if (!(input instanceof Array))
2139
+ input = [input];
2140
+ }
2141
+ image.unload(paints[i].loadId, !input.some((item) => item.url === url));
2142
+ }
2143
+ }
1866
2144
  }
2145
+ return recycleMap;
1867
2146
  }
2147
+ return null;
1868
2148
  }
1869
2149
 
1870
- const { getSpread, getOuterOf, getByMove, getIntersectData } = BoundsHelper;
1871
- function shape(ui, current, options) {
1872
- const canvas = current.getSameCanvas();
1873
- let bounds, matrix, shapeBounds;
1874
- let worldCanvas;
1875
- const { __world } = ui;
1876
- let { scaleX, scaleY } = __world;
1877
- if (scaleX < 0)
1878
- scaleX = -scaleX;
1879
- if (scaleY < 0)
1880
- scaleY = -scaleY;
1881
- if (!current.bounds.includes(__world, options.matrix)) {
1882
- const { renderShapeSpread: spread } = ui.__layout;
1883
- const worldClipBounds = getIntersectData(spread ? getSpread(current.bounds, spread * scaleX, spread * scaleY) : current.bounds, __world, options.matrix);
1884
- matrix = current.bounds.getFitMatrix(worldClipBounds);
1885
- if (matrix.a < 1) {
1886
- worldCanvas = current.getSameCanvas();
1887
- ui.__renderShape(worldCanvas, options);
1888
- scaleX *= matrix.a;
1889
- scaleY *= matrix.d;
1890
- }
1891
- shapeBounds = getOuterOf(__world, matrix);
1892
- bounds = getByMove(shapeBounds, -matrix.e, -matrix.f);
1893
- if (options.matrix)
1894
- matrix.multiply(options.matrix);
1895
- options = Object.assign(Object.assign({}, options), { matrix });
1896
- }
1897
- else {
1898
- if (options.matrix) {
1899
- scaleX *= options.matrix.a;
1900
- scaleY *= options.matrix.d;
1901
- bounds = shapeBounds = getOuterOf(__world, options.matrix);
1902
- }
1903
- else {
1904
- bounds = shapeBounds = __world;
1905
- }
1906
- worldCanvas = canvas;
1907
- }
1908
- ui.__renderShape(canvas, options);
1909
- return {
1910
- canvas, matrix, bounds,
1911
- worldCanvas, shapeBounds, scaleX, scaleY
1912
- };
1913
- }
2150
+ const PaintImageModule = {
2151
+ image,
2152
+ checkImage,
2153
+ createPattern,
2154
+ recycleImage,
2155
+ createData,
2156
+ getPatternData,
2157
+ fillOrFitMode,
2158
+ clipMode,
2159
+ repeatMode
2160
+ };
1914
2161
 
1915
- const defaultFrom$2 = { x: 0.5, y: 0 };
1916
- const defaultTo$2 = { x: 0.5, y: 1 };
2162
+ const { toPoint: toPoint$2 } = AroundHelper;
2163
+ const realFrom$2 = {};
2164
+ const realTo$2 = {};
1917
2165
  function linearGradient(paint, box) {
1918
2166
  let { from, to, type, blendMode, opacity } = paint;
1919
- from || (from = defaultFrom$2);
1920
- to || (to = defaultTo$2);
1921
- const style = Platform.canvas.createLinearGradient(box.x + from.x * box.width, box.y + from.y * box.height, box.x + to.x * box.width, box.y + to.y * box.height);
2167
+ toPoint$2(from || 'top', box, realFrom$2);
2168
+ toPoint$2(to || 'bottom', box, realTo$2);
2169
+ const style = Platform.canvas.createLinearGradient(realFrom$2.x, realFrom$2.y, realTo$2.x, realTo$2.y);
1922
2170
  applyStops(style, paint.stops, opacity);
1923
2171
  const data = { type, style };
1924
2172
  if (blendMode)
@@ -1929,124 +2177,84 @@ function applyStops(gradient, stops, opacity) {
1929
2177
  let stop;
1930
2178
  for (let i = 0, len = stops.length; i < len; i++) {
1931
2179
  stop = stops[i];
1932
- gradient.addColorStop(stop.offset, ColorConvert$1.string(stop.color, opacity));
2180
+ if (typeof stop === 'string') {
2181
+ gradient.addColorStop(i / (len - 1), ColorConvert.string(stop, opacity));
2182
+ }
2183
+ else {
2184
+ gradient.addColorStop(stop.offset, ColorConvert.string(stop.color, opacity));
2185
+ }
1933
2186
  }
1934
2187
  }
1935
2188
 
1936
- const { set: set$1, getAngle: getAngle$1, getDistance: getDistance$1 } = PointHelper;
1937
- const { get: get$1, rotateOfOuter: rotateOfOuter$1, scaleOfOuter: scaleOfOuter$1 } = MatrixHelper;
1938
- const defaultFrom$1 = { x: 0.5, y: 0.5 };
1939
- const defaultTo$1 = { x: 0.5, y: 1 };
2189
+ const { getAngle, getDistance: getDistance$1 } = PointHelper;
2190
+ const { get, rotateOfOuter, scaleOfOuter } = MatrixHelper;
2191
+ const { toPoint: toPoint$1 } = AroundHelper;
1940
2192
  const realFrom$1 = {};
1941
2193
  const realTo$1 = {};
1942
2194
  function radialGradient(paint, box) {
1943
2195
  let { from, to, type, opacity, blendMode, stretch } = paint;
1944
- from || (from = defaultFrom$1);
1945
- to || (to = defaultTo$1);
1946
- const { x, y, width, height } = box;
1947
- set$1(realFrom$1, x + from.x * width, y + from.y * height);
1948
- set$1(realTo$1, x + to.x * width, y + to.y * height);
1949
- let transform;
1950
- if (width !== height || stretch) {
1951
- transform = get$1();
1952
- scaleOfOuter$1(transform, realFrom$1, width / height * (stretch || 1), 1);
1953
- rotateOfOuter$1(transform, realFrom$1, getAngle$1(realFrom$1, realTo$1) + 90);
1954
- }
2196
+ toPoint$1(from || 'center', box, realFrom$1);
2197
+ toPoint$1(to || 'bottom', box, realTo$1);
1955
2198
  const style = Platform.canvas.createRadialGradient(realFrom$1.x, realFrom$1.y, 0, realFrom$1.x, realFrom$1.y, getDistance$1(realFrom$1, realTo$1));
1956
2199
  applyStops(style, paint.stops, opacity);
1957
- const data = { type, style, transform };
2200
+ const data = { type, style };
2201
+ const transform = getTransform(box, realFrom$1, realTo$1, stretch, true);
2202
+ if (transform)
2203
+ data.transform = transform;
1958
2204
  if (blendMode)
1959
2205
  data.blendMode = blendMode;
1960
2206
  return data;
1961
2207
  }
2208
+ function getTransform(box, from, to, stretch, rotate90) {
2209
+ let transform;
2210
+ const { width, height } = box;
2211
+ if (width !== height || stretch) {
2212
+ const angle = getAngle(from, to);
2213
+ transform = get();
2214
+ if (rotate90) {
2215
+ scaleOfOuter(transform, from, width / height * (stretch || 1), 1);
2216
+ rotateOfOuter(transform, from, angle + 90);
2217
+ }
2218
+ else {
2219
+ scaleOfOuter(transform, from, 1, width / height * (stretch || 1));
2220
+ rotateOfOuter(transform, from, angle);
2221
+ }
2222
+ }
2223
+ return transform;
2224
+ }
1962
2225
 
1963
- const { set, getAngle, getDistance } = PointHelper;
1964
- const { get, rotateOfOuter, scaleOfOuter } = MatrixHelper;
1965
- const defaultFrom = { x: 0.5, y: 0.5 };
1966
- const defaultTo = { x: 0.5, y: 1 };
2226
+ const { getDistance } = PointHelper;
2227
+ const { toPoint } = AroundHelper;
1967
2228
  const realFrom = {};
1968
2229
  const realTo = {};
1969
2230
  function conicGradient(paint, box) {
1970
2231
  let { from, to, type, opacity, blendMode, stretch } = paint;
1971
- from || (from = defaultFrom);
1972
- to || (to = defaultTo);
1973
- const { x, y, width, height } = box;
1974
- set(realFrom, x + from.x * width, y + from.y * height);
1975
- set(realTo, x + to.x * width, y + to.y * height);
1976
- const transform = get();
1977
- const angle = getAngle(realFrom, realTo);
1978
- if (Platform.conicGradientRotate90) {
1979
- scaleOfOuter(transform, realFrom, width / height * (stretch || 1), 1);
1980
- rotateOfOuter(transform, realFrom, angle + 90);
1981
- }
1982
- else {
1983
- scaleOfOuter(transform, realFrom, 1, width / height * (stretch || 1));
1984
- rotateOfOuter(transform, realFrom, angle);
1985
- }
2232
+ toPoint(from || 'center', box, realFrom);
2233
+ toPoint(to || 'bottom', box, realTo);
1986
2234
  const style = Platform.conicGradientSupport ? Platform.canvas.createConicGradient(0, realFrom.x, realFrom.y) : Platform.canvas.createRadialGradient(realFrom.x, realFrom.y, 0, realFrom.x, realFrom.y, getDistance(realFrom, realTo));
1987
2235
  applyStops(style, paint.stops, opacity);
1988
- const data = { type, style, transform };
2236
+ const data = { type, style };
2237
+ const transform = getTransform(box, realFrom, realTo, stretch || 1, Platform.conicGradientRotate90);
2238
+ if (transform)
2239
+ data.transform = transform;
1989
2240
  if (blendMode)
1990
2241
  data.blendMode = blendMode;
1991
2242
  return data;
1992
2243
  }
1993
2244
 
1994
- let recycleMap;
1995
- function compute(ui, attrName) {
1996
- const value = [];
1997
- let item;
1998
- let paints = ui.__.__input[attrName];
1999
- if (!(paints instanceof Array))
2000
- paints = [paints];
2001
- recycleMap = recycleImage(ui.__, attrName);
2002
- for (let i = 0, len = paints.length; i < len; i++) {
2003
- item = getLeafPaint(ui, paints[i], attrName);
2004
- if (item)
2005
- value.push(item);
2006
- }
2007
- ui.__['_' + attrName] = value.length ? value : undefined;
2008
- }
2009
- function getLeafPaint(ui, paint, attrName) {
2010
- if (typeof paint !== 'object' || paint.visible === false || paint.opacity === 0)
2011
- return undefined;
2012
- const { boxBounds } = ui.__layout;
2013
- switch (paint.type) {
2014
- case 'solid':
2015
- let { type, blendMode, color, opacity } = paint;
2016
- return { type, blendMode, style: ColorConvert$1.string(color, opacity) };
2017
- case 'image':
2018
- return image(ui, attrName, paint, boxBounds, !recycleMap || !recycleMap[paint.url]);
2019
- case 'linear':
2020
- return linearGradient(paint, boxBounds);
2021
- case 'radial':
2022
- return radialGradient(paint, boxBounds);
2023
- case 'angular':
2024
- return conicGradient(paint, boxBounds);
2025
- default:
2026
- return paint.r ? { type: 'solid', style: ColorConvert$1.string(paint) } : undefined;
2027
- }
2028
- }
2029
-
2030
- var UIPaint = /*#__PURE__*/Object.freeze({
2031
- __proto__: null,
2032
- compute: compute,
2033
- drawTextStroke: drawTextStroke,
2034
- fill: fill,
2035
- fillText: fillText,
2036
- fills: fills,
2037
- recycleImage: recycleImage,
2038
- shape: shape,
2039
- stroke: stroke,
2040
- strokeText: strokeText,
2041
- strokes: strokes
2042
- });
2245
+ const PaintGradientModule = {
2246
+ linearGradient,
2247
+ radialGradient,
2248
+ conicGradient,
2249
+ getTransform
2250
+ };
2043
2251
 
2044
2252
  const { copy, toOffsetOutBounds: toOffsetOutBounds$1 } = BoundsHelper;
2045
2253
  const tempBounds = {};
2046
2254
  const offsetOutBounds$1 = {};
2047
- function shadow(ui, current, shape, _options) {
2255
+ function shadow(ui, current, shape) {
2048
2256
  let copyBounds, spreadScale;
2049
- const { __world, __layout } = ui;
2257
+ const { __nowWorld: nowWorld, __layout } = ui;
2050
2258
  const { shadow } = ui.__;
2051
2259
  const { worldCanvas, bounds, shapeBounds, scaleX, scaleY } = shape;
2052
2260
  const other = current.getSameCanvas();
@@ -2061,21 +2269,21 @@ function shadow(ui, current, shape, _options) {
2061
2269
  other.restore();
2062
2270
  other.save();
2063
2271
  if (worldCanvas) {
2064
- other.copyWorld(other, bounds, __world, 'copy');
2065
- copyBounds = __world;
2272
+ other.copyWorld(other, bounds, nowWorld, 'copy');
2273
+ copyBounds = nowWorld;
2066
2274
  }
2067
- worldCanvas ? other.copyWorld(worldCanvas, __world, __world, 'destination-out') : other.copyWorld(shape.canvas, shapeBounds, bounds, 'destination-out');
2275
+ worldCanvas ? other.copyWorld(worldCanvas, nowWorld, nowWorld, 'destination-out') : other.copyWorld(shape.canvas, shapeBounds, bounds, 'destination-out');
2068
2276
  }
2069
- if (ui.__hasMirror) {
2070
- current.copyWorldByReset(other, copyBounds, __world, item.blendMode);
2277
+ if (ui.__worldFlipped) {
2278
+ current.copyWorldByReset(other, copyBounds, nowWorld, item.blendMode);
2071
2279
  }
2072
2280
  else {
2073
2281
  current.copyWorldToInner(other, copyBounds, __layout.renderBounds, item.blendMode);
2074
2282
  }
2075
2283
  if (end && index < end)
2076
- other.clear();
2284
+ other.clearWorld(copyBounds, true);
2077
2285
  });
2078
- other.recycle();
2286
+ other.recycle(copyBounds);
2079
2287
  }
2080
2288
  function drawWorldShadow(canvas, outBounds, spreadScale, shape) {
2081
2289
  const { bounds, shapeBounds } = shape;
@@ -2106,9 +2314,9 @@ function drawWorldShadow(canvas, outBounds, spreadScale, shape) {
2106
2314
 
2107
2315
  const { toOffsetOutBounds } = BoundsHelper;
2108
2316
  const offsetOutBounds = {};
2109
- function innerShadow(ui, current, shape, _options) {
2317
+ function innerShadow(ui, current, shape) {
2110
2318
  let copyBounds, spreadScale;
2111
- const { __world, __layout: __layout } = ui;
2319
+ const { __nowWorld: nowWorld, __layout: __layout } = ui;
2112
2320
  const { innerShadow } = ui.__;
2113
2321
  const { worldCanvas, bounds, shapeBounds, scaleX, scaleY } = shape;
2114
2322
  const other = current.getSameCanvas();
@@ -2121,40 +2329,115 @@ function innerShadow(ui, current, shape, _options) {
2121
2329
  drawWorldShadow(other, offsetOutBounds, spreadScale, shape);
2122
2330
  other.restore();
2123
2331
  if (worldCanvas) {
2124
- other.copyWorld(other, bounds, __world, 'copy');
2125
- other.copyWorld(worldCanvas, __world, __world, 'source-out');
2126
- copyBounds = __world;
2332
+ other.copyWorld(other, bounds, nowWorld, 'copy');
2333
+ other.copyWorld(worldCanvas, nowWorld, nowWorld, 'source-out');
2334
+ copyBounds = nowWorld;
2127
2335
  }
2128
2336
  else {
2129
2337
  other.copyWorld(shape.canvas, shapeBounds, bounds, 'source-out');
2130
2338
  copyBounds = bounds;
2131
2339
  }
2132
2340
  other.fillWorld(copyBounds, item.color, 'source-in');
2133
- if (ui.__hasMirror) {
2134
- current.copyWorldByReset(other, copyBounds, __world, item.blendMode);
2341
+ if (ui.__worldFlipped) {
2342
+ current.copyWorldByReset(other, copyBounds, nowWorld, item.blendMode);
2135
2343
  }
2136
2344
  else {
2137
2345
  current.copyWorldToInner(other, copyBounds, __layout.renderBounds, item.blendMode);
2138
2346
  }
2139
2347
  if (end && index < end)
2140
- other.clear();
2348
+ other.clearWorld(copyBounds, true);
2141
2349
  });
2142
- other.recycle();
2350
+ other.recycle(copyBounds);
2143
2351
  }
2144
2352
 
2145
2353
  function blur(ui, current, origin) {
2146
2354
  const { blur } = ui.__;
2147
- origin.setWorldBlur(blur * ui.__world.a);
2148
- origin.copyWorldToInner(current, ui.__world, ui.__layout.renderBounds);
2355
+ origin.setWorldBlur(blur * ui.__nowWorld.a);
2356
+ origin.copyWorldToInner(current, ui.__nowWorld, ui.__layout.renderBounds);
2149
2357
  origin.filter = 'none';
2150
2358
  }
2151
2359
 
2152
- var UIEffect = /*#__PURE__*/Object.freeze({
2153
- __proto__: null,
2154
- blur: blur,
2155
- innerShadow: innerShadow,
2156
- shadow: shadow
2157
- });
2360
+ function backgroundBlur(_ui, _current, _shape) {
2361
+ }
2362
+
2363
+ const EffectModule = {
2364
+ shadow,
2365
+ innerShadow,
2366
+ blur,
2367
+ backgroundBlur
2368
+ };
2369
+
2370
+ const { excludeRenderBounds } = LeafBoundsHelper;
2371
+ Group.prototype.__renderMask = function (canvas, options) {
2372
+ let child, maskCanvas, contentCanvas, maskOpacity, currentMask;
2373
+ const { children } = this;
2374
+ for (let i = 0, len = children.length; i < len; i++) {
2375
+ child = children[i];
2376
+ if (child.__.mask) {
2377
+ if (currentMask) {
2378
+ maskEnd(this, currentMask, canvas, contentCanvas, maskCanvas, maskOpacity);
2379
+ maskCanvas = contentCanvas = null;
2380
+ }
2381
+ if (child.__.mask === 'path') {
2382
+ if (child.opacity < 1) {
2383
+ currentMask = 'opacity-path';
2384
+ maskOpacity = child.opacity;
2385
+ if (!contentCanvas)
2386
+ contentCanvas = getCanvas(canvas);
2387
+ }
2388
+ else {
2389
+ currentMask = 'path';
2390
+ canvas.save();
2391
+ }
2392
+ child.__clip(contentCanvas || canvas, options);
2393
+ }
2394
+ else {
2395
+ currentMask = 'alpha';
2396
+ if (!maskCanvas)
2397
+ maskCanvas = getCanvas(canvas);
2398
+ if (!contentCanvas)
2399
+ contentCanvas = getCanvas(canvas);
2400
+ child.__render(maskCanvas, options);
2401
+ }
2402
+ if (child.__.mask !== 'clipping')
2403
+ continue;
2404
+ }
2405
+ if (excludeRenderBounds(child, options))
2406
+ continue;
2407
+ child.__render(contentCanvas || canvas, options);
2408
+ }
2409
+ maskEnd(this, currentMask, canvas, contentCanvas, maskCanvas, maskOpacity);
2410
+ };
2411
+ function maskEnd(leaf, maskMode, canvas, contentCanvas, maskCanvas, maskOpacity) {
2412
+ switch (maskMode) {
2413
+ case 'alpha':
2414
+ usePixelMask(leaf, canvas, contentCanvas, maskCanvas);
2415
+ break;
2416
+ case 'opacity-path':
2417
+ copyContent(leaf, canvas, contentCanvas, maskOpacity);
2418
+ break;
2419
+ case 'path':
2420
+ canvas.restore();
2421
+ }
2422
+ }
2423
+ function getCanvas(canvas) {
2424
+ return canvas.getSameCanvas(false, true);
2425
+ }
2426
+ function usePixelMask(leaf, canvas, content, mask) {
2427
+ const realBounds = leaf.__nowWorld;
2428
+ content.resetTransform();
2429
+ content.opacity = 1;
2430
+ content.useMask(mask, realBounds);
2431
+ mask.recycle(realBounds);
2432
+ copyContent(leaf, canvas, content, 1);
2433
+ }
2434
+ function copyContent(leaf, canvas, content, maskOpacity) {
2435
+ const realBounds = leaf.__nowWorld;
2436
+ canvas.resetTransform();
2437
+ canvas.opacity = maskOpacity;
2438
+ canvas.copyWorld(content, realBounds);
2439
+ content.recycle(realBounds);
2440
+ }
2158
2441
 
2159
2442
  const money = '¥¥$€££¢¢';
2160
2443
  const letter = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz';
@@ -2279,6 +2562,8 @@ function createRows(drawData, content, style) {
2279
2562
  const { width, height } = bounds;
2280
2563
  const charMode = width || height || __letterSpacing || (textCase !== 'none');
2281
2564
  if (charMode) {
2565
+ const wrap = style.textWrap !== 'none';
2566
+ const breakAll = style.textWrap === 'break';
2282
2567
  paraStart = true;
2283
2568
  lastCharType = null;
2284
2569
  startCharSize = charWidth = charSize = wordWidth = rowWidth = 0;
@@ -2305,16 +2590,26 @@ function createRows(drawData, content, style) {
2305
2590
  langBreak = (charType === Single && (lastCharType === Single || lastCharType === Letter)) || (lastCharType === Single && charType !== After);
2306
2591
  afterBreak = ((charType === Before || charType === Single) && (lastCharType === Symbol || lastCharType === After));
2307
2592
  realWidth = paraStart && paraIndent ? width - paraIndent : width;
2308
- if (width && rowWidth + wordWidth + charWidth > realWidth) {
2309
- if (!afterBreak)
2310
- afterBreak = charType === Letter && lastCharType == After;
2311
- if (langBreak || afterBreak || charType === Break || charType === Before || charType === Single || (wordWidth + charWidth > realWidth)) {
2593
+ if (wrap && (width && rowWidth + wordWidth + charWidth > realWidth)) {
2594
+ if (breakAll) {
2312
2595
  if (wordWidth)
2313
2596
  addWord();
2314
- addRow();
2597
+ if (rowWidth)
2598
+ addRow();
2315
2599
  }
2316
2600
  else {
2317
- addRow();
2601
+ if (!afterBreak)
2602
+ afterBreak = charType === Letter && lastCharType == After;
2603
+ if (langBreak || afterBreak || charType === Break || charType === Before || charType === Single || (wordWidth + charWidth > realWidth)) {
2604
+ if (wordWidth)
2605
+ addWord();
2606
+ if (rowWidth)
2607
+ addRow();
2608
+ }
2609
+ else {
2610
+ if (rowWidth)
2611
+ addRow();
2612
+ }
2318
2613
  }
2319
2614
  }
2320
2615
  if (char === ' ' && paraStart !== true && (rowWidth + wordWidth) === 0) ;
@@ -2384,7 +2679,7 @@ function addRow() {
2384
2679
 
2385
2680
  const CharMode = 0;
2386
2681
  const WordMode = 1;
2387
- const RowMode = 2;
2682
+ const TextMode = 2;
2388
2683
  function layoutChar(drawData, style, width, _height) {
2389
2684
  const { rows } = drawData;
2390
2685
  const { textAlign, paraIndent, letterSpacing } = style;
@@ -2393,15 +2688,12 @@ function layoutChar(drawData, style, width, _height) {
2393
2688
  if (row.words) {
2394
2689
  indentWidth = paraIndent && row.paraStart ? paraIndent : 0;
2395
2690
  addWordWidth = (width && textAlign === 'justify' && row.words.length > 1) ? (width - row.width - indentWidth) / (row.words.length - 1) : 0;
2396
- mode = (letterSpacing || row.isOverflow) ? CharMode : (addWordWidth > 0.01 ? WordMode : RowMode);
2397
- if (mode === RowMode) {
2398
- row.text = '';
2691
+ mode = (letterSpacing || row.isOverflow) ? CharMode : (addWordWidth > 0.01 ? WordMode : TextMode);
2692
+ if (row.isOverflow && !letterSpacing)
2693
+ row.textMode = true;
2694
+ if (mode === TextMode) {
2399
2695
  row.x += indentWidth;
2400
- row.words.forEach(word => {
2401
- word.data.forEach(char => {
2402
- row.text += char.char;
2403
- });
2404
- });
2696
+ toTextChar$1(row);
2405
2697
  }
2406
2698
  else {
2407
2699
  row.x += indentWidth;
@@ -2411,11 +2703,11 @@ function layoutChar(drawData, style, width, _height) {
2411
2703
  if (mode === WordMode) {
2412
2704
  wordChar = { char: '', x: charX };
2413
2705
  charX = toWordChar(word.data, charX, wordChar);
2414
- if (wordChar.char !== ' ')
2706
+ if (row.isOverflow || wordChar.char !== ' ')
2415
2707
  row.data.push(wordChar);
2416
2708
  }
2417
2709
  else {
2418
- charX = toChar(word.data, charX, row.data);
2710
+ charX = toChar(word.data, charX, row.data, row.isOverflow);
2419
2711
  }
2420
2712
  if (!row.paraEnd && addWordWidth) {
2421
2713
  charX += addWordWidth;
@@ -2427,6 +2719,14 @@ function layoutChar(drawData, style, width, _height) {
2427
2719
  }
2428
2720
  });
2429
2721
  }
2722
+ function toTextChar$1(row) {
2723
+ row.text = '';
2724
+ row.words.forEach(word => {
2725
+ word.data.forEach(char => {
2726
+ row.text += char.char;
2727
+ });
2728
+ });
2729
+ }
2430
2730
  function toWordChar(data, charX, wordChar) {
2431
2731
  data.forEach(char => {
2432
2732
  wordChar.char += char.char;
@@ -2434,9 +2734,9 @@ function toWordChar(data, charX, wordChar) {
2434
2734
  });
2435
2735
  return charX;
2436
2736
  }
2437
- function toChar(data, charX, rowData) {
2737
+ function toChar(data, charX, rowData, isOverflow) {
2438
2738
  data.forEach(char => {
2439
- if (char.char !== ' ') {
2739
+ if (isOverflow || char.char !== ' ') {
2440
2740
  char.x = charX;
2441
2741
  rowData.push(char);
2442
2742
  }
@@ -2447,10 +2747,10 @@ function toChar(data, charX, rowData) {
2447
2747
 
2448
2748
  function layoutText(drawData, style) {
2449
2749
  const { rows, bounds } = drawData;
2450
- const { __lineHeight, __baseLine, __letterSpacing, textAlign, verticalAlign, paraSpacing, textOverflow } = style;
2750
+ const { __lineHeight, __baseLine, __letterSpacing, __clipText, textAlign, verticalAlign, paraSpacing } = style;
2451
2751
  let { x, y, width, height } = bounds, realHeight = __lineHeight * rows.length + (paraSpacing ? paraSpacing * (drawData.paraNumber - 1) : 0);
2452
2752
  let starY = __baseLine;
2453
- if (textOverflow !== 'show' && realHeight > height) {
2753
+ if (__clipText && realHeight > height) {
2454
2754
  realHeight = Math.max(height, __lineHeight);
2455
2755
  drawData.overflow = rows.length;
2456
2756
  }
@@ -2468,12 +2768,14 @@ function layoutText(drawData, style) {
2468
2768
  for (let i = 0, len = rows.length; i < len; i++) {
2469
2769
  row = rows[i];
2470
2770
  row.x = x;
2471
- switch (textAlign) {
2472
- case 'center':
2473
- row.x += (width - row.width) / 2;
2474
- break;
2475
- case 'right':
2476
- row.x += width - row.width;
2771
+ if (row.width < width || (row.width > width && !__clipText)) {
2772
+ switch (textAlign) {
2773
+ case 'center':
2774
+ row.x += (width - row.width) / 2;
2775
+ break;
2776
+ case 'right':
2777
+ row.x += width - row.width;
2778
+ }
2477
2779
  }
2478
2780
  if (row.paraStart && paraSpacing && i > 0)
2479
2781
  starY += paraSpacing;
@@ -2499,39 +2801,62 @@ function layoutText(drawData, style) {
2499
2801
  bounds.x = rowX;
2500
2802
  if (rowWidth > bounds.width)
2501
2803
  bounds.width = rowWidth;
2804
+ if (__clipText && width && width < rowWidth) {
2805
+ row.isOverflow = true;
2806
+ if (!drawData.overflow)
2807
+ drawData.overflow = rows.length;
2808
+ }
2502
2809
  }
2503
2810
  bounds.y = y;
2504
2811
  bounds.height = realHeight;
2505
2812
  }
2506
2813
 
2507
- function clipText(drawData, textOverflow) {
2814
+ function clipText(drawData, style, x, width) {
2815
+ if (!width)
2816
+ return;
2508
2817
  const { rows, overflow } = drawData;
2818
+ let { textOverflow } = style;
2509
2819
  rows.splice(overflow);
2510
- if (textOverflow !== 'hide') {
2511
- if (textOverflow === 'ellipsis')
2820
+ if (textOverflow && textOverflow !== 'show') {
2821
+ if (textOverflow === 'hide')
2822
+ textOverflow = '';
2823
+ else if (textOverflow === 'ellipsis')
2512
2824
  textOverflow = '...';
2513
- const ellipsisWidth = Platform.canvas.measureText(textOverflow).width;
2514
- const row = rows[overflow - 1];
2515
- let char, end = row.data.length - 1, charRight;
2516
- const { x, width } = drawData.bounds;
2825
+ let char, charRight;
2826
+ const ellipsisWidth = textOverflow ? Platform.canvas.measureText(textOverflow).width : 0;
2517
2827
  const right = x + width - ellipsisWidth;
2518
- for (let i = end; i > -1; i--) {
2519
- char = row.data[i];
2520
- charRight = char.x + char.width;
2521
- if (i === end && charRight < right) {
2522
- break;
2523
- }
2524
- else if (charRight < right && char.char !== ' ') {
2525
- row.data.splice(i + 1);
2526
- row.width -= char.width;
2527
- break;
2828
+ const list = style.textWrap === 'none' ? rows : [rows[overflow - 1]];
2829
+ list.forEach(row => {
2830
+ if (row.isOverflow && row.data) {
2831
+ let end = row.data.length - 1;
2832
+ for (let i = end; i > -1; i--) {
2833
+ char = row.data[i];
2834
+ charRight = char.x + char.width;
2835
+ if (i === end && charRight < right) {
2836
+ break;
2837
+ }
2838
+ else if (charRight < right && char.char !== ' ') {
2839
+ row.data.splice(i + 1);
2840
+ row.width -= char.width;
2841
+ break;
2842
+ }
2843
+ row.width -= char.width;
2844
+ }
2845
+ row.width += ellipsisWidth;
2846
+ row.data.push({ char: textOverflow, x: charRight });
2847
+ if (row.textMode)
2848
+ toTextChar(row);
2528
2849
  }
2529
- row.width -= char.width;
2530
- }
2531
- row.width += ellipsisWidth;
2532
- row.data.push({ char: textOverflow, x: charRight });
2850
+ });
2533
2851
  }
2534
2852
  }
2853
+ function toTextChar(row) {
2854
+ row.text = '';
2855
+ row.data.forEach(char => {
2856
+ row.text += char.char;
2857
+ });
2858
+ row.data = null;
2859
+ }
2535
2860
 
2536
2861
  function decorationText(drawData, style) {
2537
2862
  const { fontSize } = style;
@@ -2545,100 +2870,214 @@ function decorationText(drawData, style) {
2545
2870
  }
2546
2871
  }
2547
2872
 
2548
- const TextConvert = {
2549
- getDrawData(content, style) {
2550
- if (typeof content !== 'string')
2551
- content = String(content);
2552
- let x = 0, y = 0;
2553
- let width = style.__getInput('width') || 0;
2554
- let height = style.__getInput('height') || 0;
2555
- const { textDecoration, textOverflow, __font, padding } = style;
2556
- if (padding) {
2557
- const [top, right, bottom, left] = MathHelper.fourNumber(padding);
2558
- if (width) {
2559
- x = left;
2560
- width -= (right + left);
2561
- }
2562
- if (height) {
2563
- y = top;
2564
- height -= (top + bottom);
2565
- }
2873
+ const { top, right, bottom, left } = Direction4;
2874
+ function getDrawData(content, style) {
2875
+ if (typeof content !== 'string')
2876
+ content = String(content);
2877
+ let x = 0, y = 0;
2878
+ let width = style.__getInput('width') || 0;
2879
+ let height = style.__getInput('height') || 0;
2880
+ const { textDecoration, __font, __padding: padding } = style;
2881
+ if (padding) {
2882
+ if (width) {
2883
+ x = padding[left];
2884
+ width -= (padding[right] + padding[left]);
2885
+ }
2886
+ if (height) {
2887
+ y = padding[top];
2888
+ height -= (padding[top] + padding[bottom]);
2889
+ }
2890
+ }
2891
+ const drawData = {
2892
+ bounds: { x, y, width, height },
2893
+ rows: [],
2894
+ paraNumber: 0,
2895
+ font: Platform.canvas.font = __font
2896
+ };
2897
+ createRows(drawData, content, style);
2898
+ if (padding)
2899
+ padAutoText(padding, drawData, style, width, height);
2900
+ layoutText(drawData, style);
2901
+ layoutChar(drawData, style, width);
2902
+ if (drawData.overflow)
2903
+ clipText(drawData, style, x, width);
2904
+ if (textDecoration !== 'none')
2905
+ decorationText(drawData, style);
2906
+ return drawData;
2907
+ }
2908
+ function padAutoText(padding, drawData, style, width, height) {
2909
+ if (!width) {
2910
+ switch (style.textAlign) {
2911
+ case 'left':
2912
+ offsetText(drawData, 'x', padding[left]);
2913
+ break;
2914
+ case 'right':
2915
+ offsetText(drawData, 'x', -padding[right]);
2916
+ }
2917
+ }
2918
+ if (!height) {
2919
+ switch (style.verticalAlign) {
2920
+ case 'top':
2921
+ offsetText(drawData, 'y', padding[top]);
2922
+ break;
2923
+ case 'bottom':
2924
+ offsetText(drawData, 'y', -padding[bottom]);
2566
2925
  }
2567
- const drawData = {
2568
- bounds: { x, y, width, height },
2569
- rows: [],
2570
- paraNumber: 0,
2571
- font: Platform.canvas.font = __font
2572
- };
2573
- createRows(drawData, content, style);
2574
- layoutText(drawData, style);
2575
- layoutChar(drawData, style, width);
2576
- if (drawData.overflow)
2577
- clipText(drawData, textOverflow);
2578
- if (textDecoration !== 'none')
2579
- decorationText(drawData, style);
2580
- return drawData;
2581
2926
  }
2927
+ }
2928
+ function offsetText(drawData, attrName, value) {
2929
+ const { bounds, rows } = drawData;
2930
+ bounds[attrName] += value;
2931
+ for (let i = 0; i < rows.length; i++)
2932
+ rows[i][attrName] += value;
2933
+ }
2934
+
2935
+ const TextConvertModule = {
2936
+ getDrawData
2582
2937
  };
2583
2938
 
2584
- const ColorConvert = {
2585
- string(color, opacity) {
2586
- if (typeof color === 'string')
2587
- return color;
2588
- let a = color.a === undefined ? 1 : color.a;
2589
- if (opacity)
2590
- a *= opacity;
2591
- const rgb = color.r + ',' + color.g + ',' + color.b;
2592
- return a === 1 ? 'rgb(' + rgb + ')' : 'rgba(' + rgb + ',' + a + ')';
2593
- }
2939
+ function string(color, opacity) {
2940
+ if (typeof color === 'string')
2941
+ return color;
2942
+ let a = color.a === undefined ? 1 : color.a;
2943
+ if (opacity)
2944
+ a *= opacity;
2945
+ const rgb = color.r + ',' + color.g + ',' + color.b;
2946
+ return a === 1 ? 'rgb(' + rgb + ')' : 'rgba(' + rgb + ',' + a + ')';
2947
+ }
2948
+
2949
+ const ColorConvertModule = {
2950
+ string
2594
2951
  };
2595
2952
 
2596
- const Export = {
2953
+ const { setPoint, addPoint, toBounds } = TwoPointBoundsHelper;
2954
+ function getTrimBounds(canvas) {
2955
+ const { width, height } = canvas.view;
2956
+ const { data } = canvas.context.getImageData(0, 0, width, height);
2957
+ let x, y, pointBounds, index = 0;
2958
+ for (let i = 0; i < data.length; i += 4) {
2959
+ if (data[i + 3] !== 0) {
2960
+ x = index % width;
2961
+ y = (index - x) / width;
2962
+ pointBounds ? addPoint(pointBounds, x, y) : setPoint(pointBounds = {}, x, y);
2963
+ }
2964
+ index++;
2965
+ }
2966
+ const bounds = new Bounds();
2967
+ toBounds(pointBounds, bounds);
2968
+ return bounds.scale(1 / canvas.pixelRatio).ceil();
2969
+ }
2970
+
2971
+ const ExportModule = {
2597
2972
  export(leaf, filename, options) {
2973
+ this.running = true;
2974
+ const fileType = FileHelper.fileType(filename);
2975
+ options = FileHelper.getExportOptions(options);
2598
2976
  return addTask((success) => new Promise((resolve) => {
2977
+ const over = (result) => {
2978
+ success(result);
2979
+ resolve();
2980
+ this.running = false;
2981
+ };
2982
+ const { toURL } = Platform;
2983
+ const { download } = Platform.origin;
2984
+ if (filename === 'json') {
2985
+ return over({ data: leaf.toJSON(options.json) });
2986
+ }
2987
+ else if (fileType === 'json') {
2988
+ download(toURL(JSON.stringify(leaf.toJSON(options.json)), 'text'), filename);
2989
+ return over({ data: true });
2990
+ }
2991
+ if (filename === 'svg') {
2992
+ return over({ data: leaf.toSVG() });
2993
+ }
2994
+ else if (fileType === 'svg') {
2995
+ download(toURL(leaf.toSVG(), 'svg'), filename);
2996
+ return over({ data: true });
2997
+ }
2599
2998
  const { leafer } = leaf;
2600
2999
  if (leafer) {
3000
+ checkLazy(leaf);
2601
3001
  leafer.waitViewCompleted(() => __awaiter(this, void 0, void 0, function* () {
2602
- let quality, blob;
2603
- let { canvas } = leafer;
2604
- let { unreal } = canvas;
2605
- if (unreal) {
2606
- canvas = canvas.getSameCanvas();
2607
- canvas.backgroundColor = leafer.config.fill;
2608
- leafer.__render(canvas, {});
3002
+ let renderBounds, trimBounds, scaleX = 1, scaleY = 1;
3003
+ const { worldTransform, isLeafer, isFrame } = leaf;
3004
+ const { slice, trim, onCanvas } = options;
3005
+ let scale = options.scale || 1;
3006
+ let pixelRatio = options.pixelRatio || 1;
3007
+ const smooth = options.smooth === undefined ? leafer.config.smooth : options.smooth;
3008
+ const contextSettings = options.contextSettings || leafer.config.contextSettings;
3009
+ if (leaf.isApp) {
3010
+ scale *= pixelRatio;
3011
+ pixelRatio = leaf.app.pixelRatio;
3012
+ }
3013
+ const screenshot = options.screenshot || leaf.isApp;
3014
+ const fill = (isLeafer && screenshot) ? (options.fill === undefined ? leaf.fill : options.fill) : options.fill;
3015
+ const needFill = FileHelper.isOpaqueImage(filename) || fill, matrix = new Matrix();
3016
+ if (screenshot) {
3017
+ renderBounds = screenshot === true ? (isLeafer ? leafer.canvas.bounds : leaf.worldRenderBounds) : screenshot;
2609
3018
  }
2610
- switch (typeof options) {
2611
- case 'object':
2612
- if (options.quality)
2613
- quality = options.quality;
2614
- if (options.blob)
2615
- blob = true;
2616
- break;
2617
- case 'number':
2618
- quality = options;
2619
- break;
2620
- case 'boolean':
2621
- blob = options;
3019
+ else {
3020
+ let relative = options.relative || (isLeafer ? 'inner' : 'local');
3021
+ scaleX = worldTransform.scaleX;
3022
+ scaleY = worldTransform.scaleY;
3023
+ switch (relative) {
3024
+ case 'inner':
3025
+ matrix.set(worldTransform);
3026
+ break;
3027
+ case 'local':
3028
+ matrix.set(worldTransform).divide(leaf.localTransform);
3029
+ scaleX /= leaf.scaleX;
3030
+ scaleY /= leaf.scaleY;
3031
+ break;
3032
+ case 'world':
3033
+ scaleX = 1;
3034
+ scaleY = 1;
3035
+ break;
3036
+ case 'page':
3037
+ relative = leaf.leafer;
3038
+ default:
3039
+ matrix.set(worldTransform).divide(leaf.getTransform(relative));
3040
+ const l = relative.worldTransform;
3041
+ scaleX /= scaleX / l.scaleX;
3042
+ scaleY /= scaleY / l.scaleY;
3043
+ }
3044
+ renderBounds = leaf.getBounds('render', relative);
2622
3045
  }
2623
- let data;
2624
- if (filename.includes('.')) {
2625
- data = yield canvas.saveAs(filename, quality);
3046
+ const { x, y, width, height } = new Bounds(renderBounds).scale(scale);
3047
+ let canvas = Creator.canvas({ width: Math.round(width), height: Math.round(height), pixelRatio, smooth, contextSettings });
3048
+ const renderOptions = { matrix: matrix.scale(1 / scale).invert().translate(-x, -y).withScale(1 / scaleX * scale, 1 / scaleY * scale) };
3049
+ if (slice) {
3050
+ leaf = leafer;
3051
+ renderOptions.bounds = canvas.bounds;
2626
3052
  }
2627
- else if (blob) {
2628
- data = yield canvas.toBlob(filename, quality);
3053
+ canvas.save();
3054
+ if (isFrame && fill !== undefined) {
3055
+ const oldFill = leaf.get('fill');
3056
+ leaf.fill = '';
3057
+ leaf.__render(canvas, renderOptions);
3058
+ leaf.fill = oldFill;
2629
3059
  }
2630
3060
  else {
2631
- data = yield canvas.toDataURL(filename, quality);
3061
+ leaf.__render(canvas, renderOptions);
2632
3062
  }
2633
- success({ data });
2634
- resolve();
2635
- if (unreal)
2636
- canvas.recycle();
3063
+ canvas.restore();
3064
+ if (trim) {
3065
+ trimBounds = getTrimBounds(canvas);
3066
+ const old = canvas, { width, height } = trimBounds;
3067
+ const config = { x: 0, y: 0, width, height, pixelRatio };
3068
+ canvas = Creator.canvas(config);
3069
+ canvas.copyWorld(old, trimBounds, config);
3070
+ }
3071
+ if (needFill)
3072
+ canvas.fillWorld(canvas.bounds, fill || '#FFFFFF', 'destination-over');
3073
+ if (onCanvas)
3074
+ onCanvas(canvas);
3075
+ const data = filename === 'canvas' ? canvas : yield canvas.export(filename, options);
3076
+ over({ data, width: canvas.pixelWidth, height: canvas.pixelHeight, renderBounds, trimBounds });
2637
3077
  }));
2638
3078
  }
2639
3079
  else {
2640
- success({ data: false });
2641
- resolve();
3080
+ over({ data: false });
2642
3081
  }
2643
3082
  }));
2644
3083
  }
@@ -2651,13 +3090,64 @@ function addTask(task) {
2651
3090
  tasker.add(() => __awaiter(this, void 0, void 0, function* () { return yield task(resolve); }), { parallel: false });
2652
3091
  });
2653
3092
  }
3093
+ function checkLazy(leaf) {
3094
+ if (leaf.__.__needComputePaint)
3095
+ leaf.__.__computePaint();
3096
+ if (leaf.isBranch)
3097
+ leaf.children.forEach(child => checkLazy(child));
3098
+ }
3099
+
3100
+ const canvas = LeaferCanvasBase.prototype;
3101
+ const debug = Debug.get('@leafer-ui/export');
3102
+ canvas.export = function (filename, options) {
3103
+ const { quality, blob } = FileHelper.getExportOptions(options);
3104
+ if (filename.includes('.')) {
3105
+ return this.saveAs(filename, quality);
3106
+ }
3107
+ else if (blob) {
3108
+ return this.toBlob(filename, quality);
3109
+ }
3110
+ else {
3111
+ return this.toDataURL(filename, quality);
3112
+ }
3113
+ };
3114
+ canvas.toBlob = function (type, quality) {
3115
+ return new Promise((resolve) => {
3116
+ Platform.origin.canvasToBolb(this.view, type, quality).then((blob) => {
3117
+ resolve(blob);
3118
+ }).catch((e) => {
3119
+ debug.error(e);
3120
+ resolve(null);
3121
+ });
3122
+ });
3123
+ };
3124
+ canvas.toDataURL = function (type, quality) {
3125
+ return Platform.origin.canvasToDataURL(this.view, type, quality);
3126
+ };
3127
+ canvas.saveAs = function (filename, quality) {
3128
+ return new Promise((resolve) => {
3129
+ Platform.origin.canvasSaveAs(this.view, filename, quality).then(() => {
3130
+ resolve(true);
3131
+ }).catch((e) => {
3132
+ debug.error(e);
3133
+ resolve(false);
3134
+ });
3135
+ });
3136
+ };
2654
3137
 
2655
- Object.assign(Paint, UIPaint);
2656
- Object.assign(Effect, UIEffect);
2657
- Object.assign(TextConvert$1, TextConvert);
2658
- Object.assign(ColorConvert$1, ColorConvert);
2659
- Object.assign(Export$1, Export);
3138
+ Object.assign(TextConvert, TextConvertModule);
3139
+ Object.assign(ColorConvert, ColorConvertModule);
3140
+ Object.assign(Paint, PaintModule);
3141
+ Object.assign(PaintImage, PaintImageModule);
3142
+ Object.assign(PaintGradient, PaintGradientModule);
3143
+ Object.assign(Effect, EffectModule);
3144
+ Object.assign(Export, ExportModule);
2660
3145
 
3146
+ Object.assign(Creator, {
3147
+ interaction: (target, canvas, selector, options) => new Interaction(target, canvas, selector, options),
3148
+ hitCanvas: (options, manager) => new LeaferCanvas(options, manager),
3149
+ hitCanvasManager: () => new HitCanvasManager()
3150
+ });
2661
3151
  useCanvas();
2662
3152
 
2663
3153
  export { Interaction, Layouter, LeaferCanvas, Renderer, Selector, Watcher, useCanvas };