leafer-draw 1.0.0-rc.9 → 1.0.0

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,21 +1,41 @@
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, MatrixHelper, ImageEvent, PointHelper, Direction4, TwoPointBoundsHelper, TaskProcessor, Matrix } 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, 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, Export, Group, TextConvert, Paint, PaintImage as PaintImage$1, PaintGradient as PaintGradient$1, Effect } from '@leafer-ui/draw';
4
+ import { PaintImage, ColorConvert, PaintGradient, Export, Group, TextConvert, Paint, Effect } from '@leafer-ui/draw';
5
5
  export * from '@leafer-ui/draw';
6
6
 
7
- const debug$2 = Debug.get('LeaferCanvas');
7
+ const debug$3 = Debug.get('LeaferCanvas');
8
8
  class LeaferCanvas extends LeaferCanvasBase {
9
+ set zIndex(zIndex) {
10
+ const { style } = this.view;
11
+ style.zIndex = zIndex;
12
+ this.setAbsolute(this.view);
13
+ }
14
+ set childIndex(index) {
15
+ const { view, parentView } = this;
16
+ if (view && parentView) {
17
+ const beforeNode = parentView.children[index];
18
+ if (beforeNode) {
19
+ this.setAbsolute(beforeNode);
20
+ parentView.insertBefore(view, beforeNode);
21
+ }
22
+ else {
23
+ parentView.appendChild(beforeNode);
24
+ }
25
+ }
26
+ }
9
27
  init() {
10
28
  const { view } = this.config;
11
29
  view ? this.__createViewFrom(view) : this.__createView();
12
30
  const { style } = this.view;
13
31
  style.display || (style.display = 'block');
14
32
  this.parentView = this.view.parentElement;
15
- if (this.parentView)
16
- this.parentView.style.userSelect = 'none';
33
+ if (this.parentView) {
34
+ const pStyle = this.parentView.style;
35
+ pStyle.webkitUserSelect = pStyle.userSelect = 'none';
36
+ }
17
37
  if (Platform.syncDomFont && !this.parentView) {
18
- this.view.style.display = 'none';
38
+ style.display = 'none';
19
39
  document.body.appendChild(this.view);
20
40
  }
21
41
  this.__createContext();
@@ -48,19 +68,22 @@ class LeaferCanvas extends LeaferCanvasBase {
48
68
  this.__createView();
49
69
  const view = this.view;
50
70
  if (parent.hasChildNodes()) {
51
- const { style } = view;
52
- style.position = 'absolute';
53
- style.top = style.left = '0px';
71
+ this.setAbsolute(view);
54
72
  parent.style.position || (parent.style.position = 'relative');
55
73
  }
56
74
  parent.appendChild(view);
57
75
  }
58
76
  }
59
77
  else {
60
- debug$2.error(`no id: ${inputView}`);
78
+ debug$3.error(`no id: ${inputView}`);
61
79
  this.__createView();
62
80
  }
63
81
  }
82
+ setAbsolute(view) {
83
+ const { style } = view;
84
+ style.position = 'absolute';
85
+ style.top = style.left = '0px';
86
+ }
64
87
  updateViewSize() {
65
88
  const { width, height, pixelRatio } = this;
66
89
  const { style } = this.view;
@@ -86,6 +109,10 @@ class LeaferCanvas extends LeaferCanvasBase {
86
109
  this.resizeObserver.observe(parent);
87
110
  this.checkAutoBounds(parent.getBoundingClientRect());
88
111
  }
112
+ else {
113
+ this.checkAutoBounds(this.view);
114
+ debug$3.warn('no parent');
115
+ }
89
116
  }
90
117
  catch (_a) {
91
118
  this.imitateResizeObserver();
@@ -163,9 +190,13 @@ function useCanvas(_canvasType, _power) {
163
190
  canvasToDataURL: (canvas, type, quality) => canvas.toDataURL(mineType(type), quality),
164
191
  canvasToBolb: (canvas, type, quality) => new Promise((resolve) => canvas.toBlob(resolve, mineType(type), quality)),
165
192
  canvasSaveAs: (canvas, filename, quality) => {
193
+ const url = canvas.toDataURL(mineType(fileType(filename)), quality);
194
+ return Platform.origin.download(url, filename);
195
+ },
196
+ download(url, filename) {
166
197
  return new Promise((resolve) => {
167
198
  let el = document.createElement('a');
168
- el.href = canvas.toDataURL(mineType(fileType(filename)), quality);
199
+ el.href = url;
169
200
  el.download = filename;
170
201
  document.body.appendChild(el);
171
202
  el.click();
@@ -176,16 +207,14 @@ function useCanvas(_canvasType, _power) {
176
207
  loadImage(src) {
177
208
  return new Promise((resolve, reject) => {
178
209
  const img = new Image();
179
- const { suffix, crossOrigin } = Platform.image;
210
+ const { crossOrigin } = Platform.image;
180
211
  if (crossOrigin) {
181
212
  img.setAttribute('crossOrigin', crossOrigin);
182
213
  img.crossOrigin = crossOrigin;
183
214
  }
184
215
  img.onload = () => { resolve(img); };
185
216
  img.onerror = (e) => { reject(e); };
186
- if (!src.startsWith('data:') && !src.startsWith('blob:') && suffix)
187
- src += (src.includes("?") ? "&" : "?") + suffix;
188
- img.src = src;
217
+ img.src = Platform.image.getRealURL(src);
189
218
  });
190
219
  }
191
220
  };
@@ -402,7 +431,7 @@ class LayoutBlockData {
402
431
  }
403
432
 
404
433
  const { updateAllMatrix, updateAllChange } = LeafHelper;
405
- const debug$1 = Debug.get('Layouter');
434
+ const debug$2 = Debug.get('Layouter');
406
435
  class Layouter {
407
436
  constructor(target, userConfig) {
408
437
  this.totalTimes = 0;
@@ -437,7 +466,7 @@ class Layouter {
437
466
  target.emitEvent(new LayoutEvent(LayoutEvent.END, this.layoutedBlocks, this.times));
438
467
  }
439
468
  catch (e) {
440
- debug$1.error(e);
469
+ debug$2.error(e);
441
470
  }
442
471
  this.layoutedBlocks = null;
443
472
  }
@@ -451,9 +480,9 @@ class Layouter {
451
480
  }
452
481
  layoutOnce() {
453
482
  if (this.layouting)
454
- return debug$1.warn('layouting');
483
+ return debug$2.warn('layouting');
455
484
  if (this.times > 3)
456
- return debug$1.warn('layout max times');
485
+ return debug$2.warn('layout max times');
457
486
  this.times++;
458
487
  this.totalTimes++;
459
488
  this.layouting = true;
@@ -519,9 +548,11 @@ class Layouter {
519
548
  updateAllChange(target);
520
549
  }
521
550
  addExtra(leaf) {
522
- const block = this.extraBlock || (this.extraBlock = new LayoutBlockData([]));
523
- block.updatedList.add(leaf);
524
- block.beforeBounds.add(leaf.__world);
551
+ if (!this.__updatedList.has(leaf)) {
552
+ const { updatedList, beforeBounds } = this.extraBlock || (this.extraBlock = new LayoutBlockData([]));
553
+ updatedList.length ? beforeBounds.add(leaf.__world) : beforeBounds.set(leaf.__world);
554
+ updatedList.add(leaf);
555
+ }
525
556
  }
526
557
  createBlock(data) {
527
558
  return new LayoutBlockData(data);
@@ -555,7 +586,7 @@ class Layouter {
555
586
  }
556
587
  }
557
588
 
558
- const debug = Debug.get('Renderer');
589
+ const debug$1 = Debug.get('Renderer');
559
590
  class Renderer {
560
591
  get needFill() { return !!(!this.canvas.allowBackgroundColor && this.config.fill); }
561
592
  constructor(target, canvas, userConfig) {
@@ -593,7 +624,7 @@ class Renderer {
593
624
  const { target } = this;
594
625
  this.times = 0;
595
626
  this.totalBounds = new Bounds();
596
- debug.log(target.innerName, '--->');
627
+ debug$1.log(target.innerName, '--->');
597
628
  try {
598
629
  this.emitRender(RenderEvent.START);
599
630
  this.renderOnce(callback);
@@ -602,9 +633,9 @@ class Renderer {
602
633
  }
603
634
  catch (e) {
604
635
  this.rendering = false;
605
- debug.error(e);
636
+ debug$1.error(e);
606
637
  }
607
- debug.log('-------------|');
638
+ debug$1.log('-------------|');
608
639
  }
609
640
  renderAgain() {
610
641
  if (this.rendering) {
@@ -616,9 +647,9 @@ class Renderer {
616
647
  }
617
648
  renderOnce(callback) {
618
649
  if (this.rendering)
619
- return debug.warn('rendering');
650
+ return debug$1.warn('rendering');
620
651
  if (this.times > 3)
621
- return debug.warn('render max times');
652
+ return debug$1.warn('render max times');
622
653
  this.times++;
623
654
  this.totalTimes++;
624
655
  this.rendering = true;
@@ -631,6 +662,10 @@ class Renderer {
631
662
  }
632
663
  else {
633
664
  this.requestLayout();
665
+ if (this.ignore) {
666
+ this.ignore = this.rendering = false;
667
+ return;
668
+ }
634
669
  this.emitRender(RenderEvent.BEFORE);
635
670
  if (this.config.usePartRender && this.totalTimes > 1) {
636
671
  this.partRender();
@@ -651,7 +686,7 @@ class Renderer {
651
686
  partRender() {
652
687
  const { canvas, updateBlocks: list } = this;
653
688
  if (!list)
654
- return debug.warn('PartRender: need update attr');
689
+ return debug$1.warn('PartRender: need update attr');
655
690
  this.mergeBlocks();
656
691
  list.forEach(block => { if (canvas.bounds.hit(block) && !block.isEmpty())
657
692
  this.clipRender(block); });
@@ -667,7 +702,7 @@ class Renderer {
667
702
  canvas.clear();
668
703
  }
669
704
  else {
670
- bounds.spread(1 + 1 / this.canvas.pixelRatio).ceil();
705
+ bounds.spread(10 + 1 / this.canvas.pixelRatio).ceil();
671
706
  canvas.clearWorld(bounds, true);
672
707
  canvas.clipWorld(bounds, true);
673
708
  }
@@ -739,8 +774,11 @@ class Renderer {
739
774
  if (!bounds.includes(this.target.__world) || this.needFill || !e.samePixelRatio) {
740
775
  this.addBlock(this.canvas.bounds);
741
776
  this.target.forceUpdate('surface');
777
+ return;
742
778
  }
743
779
  }
780
+ this.addBlock(new Bounds(0, 0, 1, 1));
781
+ this.changed = true;
744
782
  }
745
783
  __onLayoutEnd(event) {
746
784
  if (event.data)
@@ -751,7 +789,7 @@ class Renderer {
751
789
  empty = (!leaf.__world.width || !leaf.__world.height);
752
790
  if (empty) {
753
791
  if (!leaf.isLeafer)
754
- debug.tip(leaf.innerName, ': empty');
792
+ debug$1.tip(leaf.innerName, ': empty');
755
793
  empty = (!leaf.isBranch || leaf.isBranchLeaf);
756
794
  }
757
795
  return empty;
@@ -778,9 +816,7 @@ class Renderer {
778
816
  if (this.target) {
779
817
  this.stop();
780
818
  this.__removeListenEvents();
781
- this.target = null;
782
- this.canvas = null;
783
- this.config = null;
819
+ this.target = this.canvas = this.config = null;
784
820
  }
785
821
  }
786
822
  }
@@ -794,9 +830,6 @@ Object.assign(Creator, {
794
830
  });
795
831
  Platform.layout = Layouter.fullLayout;
796
832
 
797
- const PaintImage = {};
798
- const PaintGradient = {};
799
-
800
833
  function fillText(ui, canvas) {
801
834
  let row;
802
835
  const { rows, decorationY, decorationHeight } = ui.__.__textDrawData;
@@ -1016,7 +1049,7 @@ function shape(ui, current, options) {
1016
1049
  }
1017
1050
  else {
1018
1051
  const { renderShapeSpread: spread } = ui.__layout;
1019
- const worldClipBounds = getIntersectData(spread ? getSpread(current.bounds, spread * scaleX, spread * scaleY) : current.bounds, nowWorld);
1052
+ const worldClipBounds = getIntersectData(spread ? getSpread(current.bounds, scaleX === scaleY ? spread * scaleX : [spread * scaleY, spread * scaleX]) : current.bounds, nowWorld);
1020
1053
  fitMatrix = current.bounds.getFitMatrix(worldClipBounds);
1021
1054
  let { a: fitScaleX, d: fitScaleY } = fitMatrix;
1022
1055
  if (fitMatrix.a < 1) {
@@ -1081,7 +1114,7 @@ function getLeafPaint(attrName, paint, ui) {
1081
1114
  case 'angular':
1082
1115
  return PaintGradient.conicGradient(paint, boxBounds);
1083
1116
  default:
1084
- return paint.r ? { type: 'solid', style: ColorConvert.string(paint) } : undefined;
1117
+ return paint.r !== undefined ? { type: 'solid', style: ColorConvert.string(paint) } : undefined;
1085
1118
  }
1086
1119
  }
1087
1120
 
@@ -1098,77 +1131,80 @@ const PaintModule = {
1098
1131
  };
1099
1132
 
1100
1133
  let origin = {};
1101
- const { get: get$4, rotateOfOuter: rotateOfOuter$2, translate: translate$1, scaleOfOuter: scaleOfOuter$2, scale: scaleHelper, rotate } = MatrixHelper;
1102
- function fillOrFitMode(data, mode, box, width, height, rotation) {
1103
- const transform = get$4();
1104
- const swap = rotation && rotation !== 180;
1105
- const sw = box.width / (swap ? height : width);
1106
- const sh = box.height / (swap ? width : height);
1107
- const scale = mode === 'fit' ? Math.min(sw, sh) : Math.max(sw, sh);
1108
- const x = box.x + (box.width - width * scale) / 2;
1109
- const y = box.y + (box.height - height * scale) / 2;
1110
- translate$1(transform, x, y);
1111
- scaleHelper(transform, scale);
1134
+ const { get: get$3, rotateOfOuter: rotateOfOuter$1, translate: translate$1, scaleOfOuter: scaleOfOuter$1, scale: scaleHelper, rotate } = MatrixHelper;
1135
+ function fillOrFitMode(data, box, x, y, scaleX, scaleY, rotation) {
1136
+ const transform = get$3();
1137
+ translate$1(transform, box.x + x, box.y + y);
1138
+ scaleHelper(transform, scaleX, scaleY);
1112
1139
  if (rotation)
1113
- rotateOfOuter$2(transform, { x: box.x + box.width / 2, y: box.y + box.height / 2 }, rotation);
1114
- data.scaleX = data.scaleY = scale;
1140
+ rotateOfOuter$1(transform, { x: box.x + box.width / 2, y: box.y + box.height / 2 }, rotation);
1115
1141
  data.transform = transform;
1116
1142
  }
1117
1143
  function clipMode(data, box, x, y, scaleX, scaleY, rotation) {
1118
- const transform = get$4();
1119
- translate$1(transform, box.x, box.y);
1120
- if (x || y)
1121
- translate$1(transform, x, y);
1122
- if (scaleX) {
1144
+ const transform = get$3();
1145
+ translate$1(transform, box.x + x, box.y + y);
1146
+ if (scaleX)
1123
1147
  scaleHelper(transform, scaleX, scaleY);
1124
- data.scaleX = transform.a;
1125
- data.scaleY = transform.d;
1126
- }
1127
1148
  if (rotation)
1128
1149
  rotate(transform, rotation);
1129
1150
  data.transform = transform;
1130
1151
  }
1131
- function repeatMode(data, box, width, height, x, y, scaleX, scaleY, rotation) {
1132
- const transform = get$4();
1152
+ function repeatMode(data, box, width, height, x, y, scaleX, scaleY, rotation, align) {
1153
+ const transform = get$3();
1133
1154
  if (rotation) {
1134
- rotate(transform, rotation);
1135
- switch (rotation) {
1136
- case 90:
1137
- translate$1(transform, height, 0);
1138
- break;
1139
- case 180:
1140
- translate$1(transform, width, height);
1141
- break;
1142
- case 270:
1143
- translate$1(transform, 0, width);
1144
- break;
1155
+ if (align === 'center') {
1156
+ rotateOfOuter$1(transform, { x: width / 2, y: height / 2 }, rotation);
1157
+ }
1158
+ else {
1159
+ rotate(transform, rotation);
1160
+ switch (rotation) {
1161
+ case 90:
1162
+ translate$1(transform, height, 0);
1163
+ break;
1164
+ case 180:
1165
+ translate$1(transform, width, height);
1166
+ break;
1167
+ case 270:
1168
+ translate$1(transform, 0, width);
1169
+ break;
1170
+ }
1145
1171
  }
1146
1172
  }
1147
- origin.x = box.x;
1148
- origin.y = box.y;
1149
- if (x || y)
1150
- origin.x += x, origin.y += y;
1173
+ origin.x = box.x + x;
1174
+ origin.y = box.y + y;
1151
1175
  translate$1(transform, origin.x, origin.y);
1152
- if (scaleX) {
1153
- scaleOfOuter$2(transform, origin, scaleX, scaleY);
1154
- data.scaleX = scaleX;
1155
- data.scaleY = scaleY;
1156
- }
1176
+ if (scaleX)
1177
+ scaleOfOuter$1(transform, origin, scaleX, scaleY);
1157
1178
  data.transform = transform;
1158
1179
  }
1159
1180
 
1160
- const { get: get$3, translate } = MatrixHelper;
1181
+ const { get: get$2, translate } = MatrixHelper;
1182
+ const tempBox = new Bounds();
1183
+ const tempPoint = {};
1161
1184
  function createData(leafPaint, image, paint, box) {
1162
- let { width, height } = image;
1163
- const { opacity, mode, offset, scale, size, rotation, blendMode, repeat } = paint;
1164
- const sameBox = box.width === width && box.height === height;
1185
+ const { blendMode } = paint;
1165
1186
  if (blendMode)
1166
1187
  leafPaint.blendMode = blendMode;
1167
- const data = leafPaint.data = { mode };
1168
- let x, y, scaleX, scaleY;
1169
- if (offset)
1170
- x = offset.x, y = offset.y;
1171
- if (size) {
1188
+ leafPaint.data = getPatternData(paint, box, image);
1189
+ }
1190
+ function getPatternData(paint, box, image) {
1191
+ let { width, height } = image;
1192
+ if (paint.padding)
1193
+ box = tempBox.set(box).shrink(paint.padding);
1194
+ const { opacity, mode, align, offset, scale, size, rotation, repeat } = paint;
1195
+ const sameBox = box.width === width && box.height === height;
1196
+ const data = { mode };
1197
+ const swapSize = align !== 'center' && (rotation || 0) % 180 === 90;
1198
+ const swapWidth = swapSize ? height : width, swapHeight = swapSize ? width : height;
1199
+ let x = 0, y = 0, scaleX, scaleY;
1200
+ if (!mode || mode === 'cover' || mode === 'fit') {
1201
+ if (!sameBox || rotation) {
1202
+ const sw = box.width / swapWidth, sh = box.height / swapHeight;
1203
+ scaleX = scaleY = mode === 'fit' ? Math.min(sw, sh) : Math.max(sw, sh);
1204
+ x += (box.width - width * scaleX) / 2, y += (box.height - height * scaleY) / 2;
1205
+ }
1206
+ }
1207
+ else if (size) {
1172
1208
  scaleX = (typeof size === 'number' ? size : size.width) / width;
1173
1209
  scaleY = (typeof size === 'number' ? size : size.height) / height;
1174
1210
  }
@@ -1176,30 +1212,46 @@ function createData(leafPaint, image, paint, box) {
1176
1212
  scaleX = typeof scale === 'number' ? scale : scale.x;
1177
1213
  scaleY = typeof scale === 'number' ? scale : scale.y;
1178
1214
  }
1215
+ if (align) {
1216
+ const imageBounds = { x, y, width: swapWidth, height: swapHeight };
1217
+ if (scaleX)
1218
+ imageBounds.width *= scaleX, imageBounds.height *= scaleY;
1219
+ AlignHelper.toPoint(align, imageBounds, box, tempPoint, true);
1220
+ x += tempPoint.x, y += tempPoint.y;
1221
+ }
1222
+ if (offset)
1223
+ x += offset.x, y += offset.y;
1179
1224
  switch (mode) {
1180
1225
  case 'strench':
1181
1226
  if (!sameBox)
1182
1227
  width = box.width, height = box.height;
1183
- if (box.x || box.y) {
1184
- data.transform = get$3();
1185
- translate(data.transform, box.x, box.y);
1186
- }
1187
1228
  break;
1229
+ case 'normal':
1188
1230
  case 'clip':
1189
- if (offset || scaleX || rotation)
1231
+ if (x || y || scaleX || rotation)
1190
1232
  clipMode(data, box, x, y, scaleX, scaleY, rotation);
1191
1233
  break;
1192
1234
  case 'repeat':
1193
1235
  if (!sameBox || scaleX || rotation)
1194
- repeatMode(data, box, width, height, x, y, scaleX, scaleY, rotation);
1236
+ repeatMode(data, box, width, height, x, y, scaleX, scaleY, rotation, align);
1195
1237
  if (!repeat)
1196
1238
  data.repeat = 'repeat';
1197
1239
  break;
1198
1240
  case 'fit':
1199
1241
  case 'cover':
1200
1242
  default:
1201
- if (!sameBox || rotation)
1202
- fillOrFitMode(data, mode, box, width, height, rotation);
1243
+ if (scaleX)
1244
+ fillOrFitMode(data, box, x, y, scaleX, scaleY, rotation);
1245
+ }
1246
+ if (!data.transform) {
1247
+ if (box.x || box.y) {
1248
+ data.transform = get$2();
1249
+ translate(data.transform, box.x, box.y);
1250
+ }
1251
+ }
1252
+ if (scaleX && mode !== 'strench') {
1253
+ data.scaleX = scaleX;
1254
+ data.scaleY = scaleY;
1203
1255
  }
1204
1256
  data.width = width;
1205
1257
  data.height = height;
@@ -1207,6 +1259,7 @@ function createData(leafPaint, image, paint, box) {
1207
1259
  data.opacity = opacity;
1208
1260
  if (repeat)
1209
1261
  data.repeat = typeof repeat === 'string' ? (repeat === 'x' ? 'repeat-x' : 'repeat-y') : 'repeat';
1262
+ return data;
1210
1263
  }
1211
1264
 
1212
1265
  let cache, box = new Bounds();
@@ -1218,8 +1271,7 @@ function image(ui, attrName, paint, boxBounds, firstUse) {
1218
1271
  leafPaint = cache.leafPaint;
1219
1272
  }
1220
1273
  else {
1221
- leafPaint = { type: paint.type };
1222
- leafPaint.image = image;
1274
+ leafPaint = { type: paint.type, image };
1223
1275
  cache = image.use > 1 ? { leafPaint, paint, boxBounds: box.set(boxBounds) } : null;
1224
1276
  }
1225
1277
  if (firstUse || image.loading)
@@ -1236,16 +1288,22 @@ function image(ui, attrName, paint, boxBounds, firstUse) {
1236
1288
  onLoadError(ui, event, image.error);
1237
1289
  }
1238
1290
  else {
1291
+ ignoreRender(ui, true);
1239
1292
  if (firstUse)
1240
1293
  onLoad(ui, event);
1241
1294
  leafPaint.loadId = image.load(() => {
1295
+ ignoreRender(ui, false);
1242
1296
  if (!ui.destroyed) {
1243
- if (checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds))
1297
+ if (checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds)) {
1298
+ if (image.hasOpacityPixel)
1299
+ ui.__layout.hitCanvasChanged = true;
1244
1300
  ui.forceUpdate('surface');
1301
+ }
1245
1302
  onLoadSuccess(ui, event);
1246
1303
  }
1247
1304
  leafPaint.loadId = null;
1248
1305
  }, (error) => {
1306
+ ignoreRender(ui, false);
1249
1307
  onLoadError(ui, event, error);
1250
1308
  leafPaint.loadId = null;
1251
1309
  });
@@ -1255,9 +1313,9 @@ function image(ui, attrName, paint, boxBounds, firstUse) {
1255
1313
  function checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds) {
1256
1314
  if (attrName === 'fill' && !ui.__.__naturalWidth) {
1257
1315
  const data = ui.__;
1258
- data.__naturalWidth = image.width;
1259
- data.__naturalHeight = image.height;
1260
- if (data.__autoWidth || data.__autoHeight) {
1316
+ data.__naturalWidth = image.width / data.pixelRatio;
1317
+ data.__naturalHeight = image.height / data.pixelRatio;
1318
+ if (data.__autoSide) {
1261
1319
  ui.forceUpdate('width');
1262
1320
  if (ui.__proxyData) {
1263
1321
  ui.setProxyAttr('width', data.width);
@@ -1285,11 +1343,16 @@ function emit(ui, type, data) {
1285
1343
  if (ui.hasEvent(type))
1286
1344
  ui.emitEvent(new ImageEvent(type, data));
1287
1345
  }
1346
+ function ignoreRender(ui, value) {
1347
+ const { leafer } = ui;
1348
+ if (leafer && leafer.viewReady)
1349
+ leafer.renderer.ignore = value;
1350
+ }
1288
1351
 
1289
- const { get: get$2, scale, copy: copy$1 } = MatrixHelper;
1290
- const { round, abs: abs$1 } = Math;
1352
+ const { get: get$1, scale, copy: copy$1 } = MatrixHelper;
1353
+ const { ceil, abs: abs$1 } = Math;
1291
1354
  function createPattern(ui, paint, pixelRatio) {
1292
- let { scaleX, scaleY } = ui.__world;
1355
+ let { scaleX, scaleY } = ImageManager.patternLocked ? ui.__world : ui.__nowWorld;
1293
1356
  const id = scaleX + '-' + scaleY;
1294
1357
  if (paint.patternId !== id && !ui.destroyed) {
1295
1358
  scaleX = abs$1(scaleX);
@@ -1297,7 +1360,7 @@ function createPattern(ui, paint, pixelRatio) {
1297
1360
  const { image, data } = paint;
1298
1361
  let imageScale, imageMatrix, { width, height, scaleX: sx, scaleY: sy, opacity, transform, repeat } = data;
1299
1362
  if (sx) {
1300
- imageMatrix = get$2();
1363
+ imageMatrix = get$1();
1301
1364
  copy$1(imageMatrix, transform);
1302
1365
  scale(imageMatrix, 1 / sx, 1 / sy);
1303
1366
  scaleX *= sx;
@@ -1332,13 +1395,13 @@ function createPattern(ui, paint, pixelRatio) {
1332
1395
  }
1333
1396
  if (transform || scaleX !== 1 || scaleY !== 1) {
1334
1397
  if (!imageMatrix) {
1335
- imageMatrix = get$2();
1398
+ imageMatrix = get$1();
1336
1399
  if (transform)
1337
1400
  copy$1(imageMatrix, transform);
1338
1401
  }
1339
1402
  scale(imageMatrix, 1 / scaleX, 1 / scaleY);
1340
1403
  }
1341
- const canvas = image.getCanvas(width < 1 ? 1 : round(width), height < 1 ? 1 : round(height), opacity);
1404
+ const canvas = image.getCanvas(ceil(width) || 1, ceil(height) || 1, opacity);
1342
1405
  const pattern = image.getPattern(canvas, repeat || (Platform.origin.noRepeat || 'no-repeat'), imageMatrix, paint);
1343
1406
  paint.style = pattern;
1344
1407
  paint.patternId = id;
@@ -1383,7 +1446,7 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
1383
1446
 
1384
1447
  const { abs } = Math;
1385
1448
  function checkImage(ui, canvas, paint, allowPaint) {
1386
- const { scaleX, scaleY } = ui.__world;
1449
+ const { scaleX, scaleY } = ImageManager.patternLocked ? ui.__world : ui.__nowWorld;
1387
1450
  if (!paint.data || paint.patternId === scaleX + '-' + scaleY) {
1388
1451
  return false;
1389
1452
  }
@@ -1398,7 +1461,7 @@ function checkImage(ui, canvas, paint, allowPaint) {
1398
1461
  width *= data.scaleX;
1399
1462
  height *= data.scaleY;
1400
1463
  }
1401
- allowPaint = width * height > Platform.image.maxCacheSize;
1464
+ allowPaint = (width * height > Platform.image.maxCacheSize) || Export.running;
1402
1465
  }
1403
1466
  else {
1404
1467
  allowPaint = false;
@@ -1425,7 +1488,7 @@ function checkImage(ui, canvas, paint, allowPaint) {
1425
1488
  if (!paint.patternTask) {
1426
1489
  paint.patternTask = ImageManager.patternTasker.add(() => __awaiter(this, void 0, void 0, function* () {
1427
1490
  paint.patternTask = null;
1428
- if (canvas.bounds.hit(ui.__world))
1491
+ if (canvas.bounds.hit(ui.__nowWorld))
1429
1492
  createPattern(ui, paint, canvas.pixelRatio);
1430
1493
  ui.forceUpdate('surface');
1431
1494
  }), 300);
@@ -1465,22 +1528,24 @@ function recycleImage(attrName, data) {
1465
1528
 
1466
1529
  const PaintImageModule = {
1467
1530
  image,
1531
+ checkImage,
1532
+ createPattern,
1533
+ recycleImage,
1468
1534
  createData,
1535
+ getPatternData,
1469
1536
  fillOrFitMode,
1470
1537
  clipMode,
1471
- repeatMode,
1472
- createPattern,
1473
- checkImage,
1474
- recycleImage
1538
+ repeatMode
1475
1539
  };
1476
1540
 
1477
- const defaultFrom$2 = { x: 0.5, y: 0 };
1478
- const defaultTo$2 = { x: 0.5, y: 1 };
1541
+ const { toPoint: toPoint$2 } = AroundHelper;
1542
+ const realFrom$2 = {};
1543
+ const realTo$2 = {};
1479
1544
  function linearGradient(paint, box) {
1480
1545
  let { from, to, type, blendMode, opacity } = paint;
1481
- from || (from = defaultFrom$2);
1482
- to || (to = defaultTo$2);
1483
- 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);
1546
+ toPoint$2(from || 'top', box, realFrom$2);
1547
+ toPoint$2(to || 'bottom', box, realTo$2);
1548
+ const style = Platform.canvas.createLinearGradient(realFrom$2.x, realFrom$2.y, realTo$2.x, realTo$2.y);
1484
1549
  applyStops(style, paint.stops, opacity);
1485
1550
  const data = { type, style };
1486
1551
  if (blendMode)
@@ -1491,63 +1556,66 @@ function applyStops(gradient, stops, opacity) {
1491
1556
  let stop;
1492
1557
  for (let i = 0, len = stops.length; i < len; i++) {
1493
1558
  stop = stops[i];
1494
- gradient.addColorStop(stop.offset, ColorConvert.string(stop.color, opacity));
1559
+ if (typeof stop === 'string') {
1560
+ gradient.addColorStop(i / (len - 1), ColorConvert.string(stop, opacity));
1561
+ }
1562
+ else {
1563
+ gradient.addColorStop(stop.offset, ColorConvert.string(stop.color, opacity));
1564
+ }
1495
1565
  }
1496
1566
  }
1497
1567
 
1498
- const { set: set$1, getAngle: getAngle$1, getDistance: getDistance$1 } = PointHelper;
1499
- const { get: get$1, rotateOfOuter: rotateOfOuter$1, scaleOfOuter: scaleOfOuter$1 } = MatrixHelper;
1500
- const defaultFrom$1 = { x: 0.5, y: 0.5 };
1501
- const defaultTo$1 = { x: 0.5, y: 1 };
1568
+ const { getAngle, getDistance: getDistance$1 } = PointHelper;
1569
+ const { get, rotateOfOuter, scaleOfOuter } = MatrixHelper;
1570
+ const { toPoint: toPoint$1 } = AroundHelper;
1502
1571
  const realFrom$1 = {};
1503
1572
  const realTo$1 = {};
1504
1573
  function radialGradient(paint, box) {
1505
1574
  let { from, to, type, opacity, blendMode, stretch } = paint;
1506
- from || (from = defaultFrom$1);
1507
- to || (to = defaultTo$1);
1508
- const { x, y, width, height } = box;
1509
- set$1(realFrom$1, x + from.x * width, y + from.y * height);
1510
- set$1(realTo$1, x + to.x * width, y + to.y * height);
1511
- let transform;
1512
- if (width !== height || stretch) {
1513
- transform = get$1();
1514
- scaleOfOuter$1(transform, realFrom$1, width / height * (stretch || 1), 1);
1515
- rotateOfOuter$1(transform, realFrom$1, getAngle$1(realFrom$1, realTo$1) + 90);
1516
- }
1575
+ toPoint$1(from || 'center', box, realFrom$1);
1576
+ toPoint$1(to || 'bottom', box, realTo$1);
1517
1577
  const style = Platform.canvas.createRadialGradient(realFrom$1.x, realFrom$1.y, 0, realFrom$1.x, realFrom$1.y, getDistance$1(realFrom$1, realTo$1));
1518
1578
  applyStops(style, paint.stops, opacity);
1519
- const data = { type, style, transform };
1579
+ const data = { type, style };
1580
+ const transform = getTransform(box, realFrom$1, realTo$1, stretch, true);
1581
+ if (transform)
1582
+ data.transform = transform;
1520
1583
  if (blendMode)
1521
1584
  data.blendMode = blendMode;
1522
1585
  return data;
1523
1586
  }
1587
+ function getTransform(box, from, to, stretch, rotate90) {
1588
+ let transform;
1589
+ const { width, height } = box;
1590
+ if (width !== height || stretch) {
1591
+ const angle = getAngle(from, to);
1592
+ transform = get();
1593
+ if (rotate90) {
1594
+ scaleOfOuter(transform, from, width / height * (stretch || 1), 1);
1595
+ rotateOfOuter(transform, from, angle + 90);
1596
+ }
1597
+ else {
1598
+ scaleOfOuter(transform, from, 1, width / height * (stretch || 1));
1599
+ rotateOfOuter(transform, from, angle);
1600
+ }
1601
+ }
1602
+ return transform;
1603
+ }
1524
1604
 
1525
- const { set, getAngle, getDistance } = PointHelper;
1526
- const { get, rotateOfOuter, scaleOfOuter } = MatrixHelper;
1527
- const defaultFrom = { x: 0.5, y: 0.5 };
1528
- const defaultTo = { x: 0.5, y: 1 };
1605
+ const { getDistance } = PointHelper;
1606
+ const { toPoint } = AroundHelper;
1529
1607
  const realFrom = {};
1530
1608
  const realTo = {};
1531
1609
  function conicGradient(paint, box) {
1532
1610
  let { from, to, type, opacity, blendMode, stretch } = paint;
1533
- from || (from = defaultFrom);
1534
- to || (to = defaultTo);
1535
- const { x, y, width, height } = box;
1536
- set(realFrom, x + from.x * width, y + from.y * height);
1537
- set(realTo, x + to.x * width, y + to.y * height);
1538
- const transform = get();
1539
- const angle = getAngle(realFrom, realTo);
1540
- if (Platform.conicGradientRotate90) {
1541
- scaleOfOuter(transform, realFrom, width / height * (stretch || 1), 1);
1542
- rotateOfOuter(transform, realFrom, angle + 90);
1543
- }
1544
- else {
1545
- scaleOfOuter(transform, realFrom, 1, width / height * (stretch || 1));
1546
- rotateOfOuter(transform, realFrom, angle);
1547
- }
1611
+ toPoint(from || 'center', box, realFrom);
1612
+ toPoint(to || 'bottom', box, realTo);
1548
1613
  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));
1549
1614
  applyStops(style, paint.stops, opacity);
1550
- const data = { type, style, transform };
1615
+ const data = { type, style };
1616
+ const transform = getTransform(box, realFrom, realTo, stretch || 1, Platform.conicGradientRotate90);
1617
+ if (transform)
1618
+ data.transform = transform;
1551
1619
  if (blendMode)
1552
1620
  data.blendMode = blendMode;
1553
1621
  return data;
@@ -1556,7 +1624,8 @@ function conicGradient(paint, box) {
1556
1624
  const PaintGradientModule = {
1557
1625
  linearGradient,
1558
1626
  radialGradient,
1559
- conicGradient
1627
+ conicGradient,
1628
+ getTransform
1560
1629
  };
1561
1630
 
1562
1631
  const { copy, toOffsetOutBounds: toOffsetOutBounds$1 } = BoundsHelper;
@@ -1662,8 +1731,8 @@ function innerShadow(ui, current, shape) {
1662
1731
 
1663
1732
  function blur(ui, current, origin) {
1664
1733
  const { blur } = ui.__;
1665
- origin.setWorldBlur(blur * ui.__world.a);
1666
- origin.copyWorldToInner(current, ui.__world, ui.__layout.renderBounds);
1734
+ origin.setWorldBlur(blur * ui.__nowWorld.a);
1735
+ origin.copyWorldToInner(current, ui.__nowWorld, ui.__layout.renderBounds);
1667
1736
  origin.filter = 'none';
1668
1737
  }
1669
1738
 
@@ -1688,7 +1757,7 @@ Group.prototype.__renderMask = function (canvas, options) {
1688
1757
  maskEnd(this, currentMask, canvas, contentCanvas, maskCanvas, maskOpacity);
1689
1758
  maskCanvas = contentCanvas = null;
1690
1759
  }
1691
- if (child.__.maskType === 'path') {
1760
+ if (child.__.mask === 'path') {
1692
1761
  if (child.opacity < 1) {
1693
1762
  currentMask = 'opacity-path';
1694
1763
  maskOpacity = child.opacity;
@@ -1709,7 +1778,7 @@ Group.prototype.__renderMask = function (canvas, options) {
1709
1778
  contentCanvas = getCanvas(canvas);
1710
1779
  child.__render(maskCanvas, options);
1711
1780
  }
1712
- if (child.__.maskType !== 'clipping')
1781
+ if (child.__.mask !== 'clipping')
1713
1782
  continue;
1714
1783
  }
1715
1784
  if (excludeRenderBounds(child, options))
@@ -1904,7 +1973,8 @@ function createRows(drawData, content, style) {
1904
1973
  if (breakAll) {
1905
1974
  if (wordWidth)
1906
1975
  addWord();
1907
- addRow();
1976
+ if (rowWidth)
1977
+ addRow();
1908
1978
  }
1909
1979
  else {
1910
1980
  if (!afterBreak)
@@ -1912,10 +1982,12 @@ function createRows(drawData, content, style) {
1912
1982
  if (langBreak || afterBreak || charType === Break || charType === Before || charType === Single || (wordWidth + charWidth > realWidth)) {
1913
1983
  if (wordWidth)
1914
1984
  addWord();
1915
- addRow();
1985
+ if (rowWidth)
1986
+ addRow();
1916
1987
  }
1917
1988
  else {
1918
- addRow();
1989
+ if (rowWidth)
1990
+ addRow();
1919
1991
  }
1920
1992
  }
1921
1993
  }
@@ -2010,11 +2082,11 @@ function layoutChar(drawData, style, width, _height) {
2010
2082
  if (mode === WordMode) {
2011
2083
  wordChar = { char: '', x: charX };
2012
2084
  charX = toWordChar(word.data, charX, wordChar);
2013
- if (wordChar.char !== ' ')
2085
+ if (row.isOverflow || wordChar.char !== ' ')
2014
2086
  row.data.push(wordChar);
2015
2087
  }
2016
2088
  else {
2017
- charX = toChar(word.data, charX, row.data);
2089
+ charX = toChar(word.data, charX, row.data, row.isOverflow);
2018
2090
  }
2019
2091
  if (!row.paraEnd && addWordWidth) {
2020
2092
  charX += addWordWidth;
@@ -2041,9 +2113,9 @@ function toWordChar(data, charX, wordChar) {
2041
2113
  });
2042
2114
  return charX;
2043
2115
  }
2044
- function toChar(data, charX, rowData) {
2116
+ function toChar(data, charX, rowData, isOverflow) {
2045
2117
  data.forEach(char => {
2046
- if (char.char !== ' ') {
2118
+ if (isOverflow || char.char !== ' ') {
2047
2119
  char.x = charX;
2048
2120
  rowData.push(char);
2049
2121
  }
@@ -2075,12 +2147,14 @@ function layoutText(drawData, style) {
2075
2147
  for (let i = 0, len = rows.length; i < len; i++) {
2076
2148
  row = rows[i];
2077
2149
  row.x = x;
2078
- switch (textAlign) {
2079
- case 'center':
2080
- row.x += (width - row.width) / 2;
2081
- break;
2082
- case 'right':
2083
- row.x += width - row.width;
2150
+ if (row.width < width || (row.width > width && !__clipText)) {
2151
+ switch (textAlign) {
2152
+ case 'center':
2153
+ row.x += (width - row.width) / 2;
2154
+ break;
2155
+ case 'right':
2156
+ row.x += width - row.width;
2157
+ }
2084
2158
  }
2085
2159
  if (row.paraStart && paraSpacing && i > 0)
2086
2160
  starY += paraSpacing;
@@ -2116,16 +2190,20 @@ function layoutText(drawData, style) {
2116
2190
  bounds.height = realHeight;
2117
2191
  }
2118
2192
 
2119
- function clipText(drawData, style) {
2193
+ function clipText(drawData, style, x, width) {
2194
+ if (!width)
2195
+ return;
2120
2196
  const { rows, overflow } = drawData;
2121
2197
  let { textOverflow } = style;
2122
2198
  rows.splice(overflow);
2123
- if (textOverflow !== 'hide') {
2124
- if (textOverflow === 'ellipsis')
2199
+ if (textOverflow && textOverflow !== 'show') {
2200
+ if (textOverflow === 'hide')
2201
+ textOverflow = '';
2202
+ else if (textOverflow === 'ellipsis')
2125
2203
  textOverflow = '...';
2126
2204
  let char, charRight;
2127
- const ellipsisWidth = Platform.canvas.measureText(textOverflow).width;
2128
- const right = style.x + style.width - ellipsisWidth;
2205
+ const ellipsisWidth = textOverflow ? Platform.canvas.measureText(textOverflow).width : 0;
2206
+ const right = x + width - ellipsisWidth;
2129
2207
  const list = style.textWrap === 'none' ? rows : [rows[overflow - 1]];
2130
2208
  list.forEach(row => {
2131
2209
  if (row.isOverflow && row.data) {
@@ -2201,7 +2279,7 @@ function getDrawData(content, style) {
2201
2279
  layoutText(drawData, style);
2202
2280
  layoutChar(drawData, style, width);
2203
2281
  if (drawData.overflow)
2204
- clipText(drawData, style);
2282
+ clipText(drawData, style, x, width);
2205
2283
  if (textDecoration !== 'none')
2206
2284
  decorationText(drawData, style);
2207
2285
  return drawData;
@@ -2272,45 +2350,95 @@ function getTrimBounds(canvas) {
2272
2350
  const ExportModule = {
2273
2351
  export(leaf, filename, options) {
2274
2352
  this.running = true;
2353
+ const fileType = FileHelper.fileType(filename);
2354
+ options = FileHelper.getExportOptions(options);
2275
2355
  return addTask((success) => new Promise((resolve) => {
2276
2356
  const over = (result) => {
2277
2357
  success(result);
2278
2358
  resolve();
2279
2359
  this.running = false;
2280
2360
  };
2361
+ const { toURL } = Platform;
2362
+ const { download } = Platform.origin;
2363
+ if (filename === 'json') {
2364
+ return over({ data: leaf.toJSON(options.json) });
2365
+ }
2366
+ else if (fileType === 'json') {
2367
+ download(toURL(JSON.stringify(leaf.toJSON(options.json)), 'text'), filename);
2368
+ return over({ data: true });
2369
+ }
2370
+ if (filename === 'svg') {
2371
+ return over({ data: leaf.toSVG() });
2372
+ }
2373
+ else if (fileType === 'svg') {
2374
+ download(toURL(leaf.toSVG(), 'svg'), filename);
2375
+ return over({ data: true });
2376
+ }
2281
2377
  const { leafer } = leaf;
2282
2378
  if (leafer) {
2379
+ checkLazy(leaf);
2283
2380
  leafer.waitViewCompleted(() => __awaiter(this, void 0, void 0, function* () {
2284
2381
  let renderBounds, trimBounds, scaleX = 1, scaleY = 1;
2285
- options = FileHelper.getExportOptions(options);
2286
- const { scale, pixelRatio, slice, trim } = options;
2382
+ const { worldTransform, isLeafer, isFrame } = leaf;
2383
+ const { slice, trim, onCanvas } = options;
2384
+ let scale = options.scale || 1;
2385
+ let pixelRatio = options.pixelRatio || 1;
2386
+ const smooth = options.smooth === undefined ? leafer.config.smooth : options.smooth;
2387
+ const contextSettings = options.contextSettings || leafer.config.contextSettings;
2388
+ if (leaf.isApp) {
2389
+ scale *= pixelRatio;
2390
+ pixelRatio = leaf.app.pixelRatio;
2391
+ }
2287
2392
  const screenshot = options.screenshot || leaf.isApp;
2288
- const fill = options.fill === undefined ? ((leaf.isLeafer && screenshot) ? leaf.fill : '') : options.fill;
2393
+ const fill = (isLeafer && screenshot) ? (options.fill === undefined ? leaf.fill : options.fill) : options.fill;
2289
2394
  const needFill = FileHelper.isOpaqueImage(filename) || fill, matrix = new Matrix();
2290
2395
  if (screenshot) {
2291
- renderBounds = screenshot === true ? (leaf.isLeafer ? leafer.canvas.bounds : leaf.worldRenderBounds) : screenshot;
2396
+ renderBounds = screenshot === true ? (isLeafer ? leafer.canvas.bounds : leaf.worldRenderBounds) : screenshot;
2292
2397
  }
2293
2398
  else {
2294
- const { localTransform, __world: world } = leaf;
2295
- matrix.set(world).divide(localTransform).invert();
2296
- scaleX = 1 / (world.scaleX / leaf.scaleX);
2297
- scaleY = 1 / (world.scaleY / leaf.scaleY);
2298
- renderBounds = leaf.getBounds('render', 'local');
2299
- }
2300
- let { x, y, width, height } = renderBounds;
2301
- if (scale) {
2302
- matrix.scale(scale);
2303
- width *= scale, height *= scale;
2304
- scaleX *= scale, scaleY *= scale;
2399
+ let relative = options.relative || (isLeafer ? 'inner' : 'local');
2400
+ scaleX = worldTransform.scaleX;
2401
+ scaleY = worldTransform.scaleY;
2402
+ switch (relative) {
2403
+ case 'inner':
2404
+ matrix.set(worldTransform);
2405
+ break;
2406
+ case 'local':
2407
+ matrix.set(worldTransform).divide(leaf.localTransform);
2408
+ scaleX /= leaf.scaleX;
2409
+ scaleY /= leaf.scaleY;
2410
+ break;
2411
+ case 'world':
2412
+ scaleX = 1;
2413
+ scaleY = 1;
2414
+ break;
2415
+ case 'page':
2416
+ relative = leaf.leafer;
2417
+ default:
2418
+ matrix.set(worldTransform).divide(leaf.getTransform(relative));
2419
+ const l = relative.worldTransform;
2420
+ scaleX /= scaleX / l.scaleX;
2421
+ scaleY /= scaleY / l.scaleY;
2422
+ }
2423
+ renderBounds = leaf.getBounds('render', relative);
2305
2424
  }
2306
- let canvas = Creator.canvas({ width, height, pixelRatio });
2307
- const renderOptions = { matrix: matrix.translate(-x, -y).withScale(scaleX, scaleY) };
2425
+ const { x, y, width, height } = new Bounds(renderBounds).scale(scale);
2426
+ let canvas = Creator.canvas({ width: Math.round(width), height: Math.round(height), pixelRatio, smooth, contextSettings });
2427
+ const renderOptions = { matrix: matrix.scale(1 / scale).invert().translate(-x, -y).withScale(1 / scaleX * scale, 1 / scaleY * scale) };
2308
2428
  if (slice) {
2309
2429
  leaf = leafer;
2310
2430
  renderOptions.bounds = canvas.bounds;
2311
2431
  }
2312
2432
  canvas.save();
2313
- leaf.__render(canvas, renderOptions);
2433
+ if (isFrame && fill !== undefined) {
2434
+ const oldFill = leaf.get('fill');
2435
+ leaf.fill = '';
2436
+ leaf.__render(canvas, renderOptions);
2437
+ leaf.fill = oldFill;
2438
+ }
2439
+ else {
2440
+ leaf.__render(canvas, renderOptions);
2441
+ }
2314
2442
  canvas.restore();
2315
2443
  if (trim) {
2316
2444
  trimBounds = getTrimBounds(canvas);
@@ -2321,8 +2449,10 @@ const ExportModule = {
2321
2449
  }
2322
2450
  if (needFill)
2323
2451
  canvas.fillWorld(canvas.bounds, fill || '#FFFFFF', 'destination-over');
2452
+ if (onCanvas)
2453
+ onCanvas(canvas);
2324
2454
  const data = filename === 'canvas' ? canvas : yield canvas.export(filename, options);
2325
- over({ data, renderBounds, trimBounds });
2455
+ over({ data, width: canvas.pixelWidth, height: canvas.pixelHeight, renderBounds, trimBounds });
2326
2456
  }));
2327
2457
  }
2328
2458
  else {
@@ -2339,12 +2469,56 @@ function addTask(task) {
2339
2469
  tasker.add(() => __awaiter(this, void 0, void 0, function* () { return yield task(resolve); }), { parallel: false });
2340
2470
  });
2341
2471
  }
2472
+ function checkLazy(leaf) {
2473
+ if (leaf.__.__needComputePaint)
2474
+ leaf.__.__computePaint();
2475
+ if (leaf.isBranch)
2476
+ leaf.children.forEach(child => checkLazy(child));
2477
+ }
2478
+
2479
+ const canvas = LeaferCanvasBase.prototype;
2480
+ const debug = Debug.get('@leafer-ui/export');
2481
+ canvas.export = function (filename, options) {
2482
+ const { quality, blob } = FileHelper.getExportOptions(options);
2483
+ if (filename.includes('.')) {
2484
+ return this.saveAs(filename, quality);
2485
+ }
2486
+ else if (blob) {
2487
+ return this.toBlob(filename, quality);
2488
+ }
2489
+ else {
2490
+ return this.toDataURL(filename, quality);
2491
+ }
2492
+ };
2493
+ canvas.toBlob = function (type, quality) {
2494
+ return new Promise((resolve) => {
2495
+ Platform.origin.canvasToBolb(this.view, type, quality).then((blob) => {
2496
+ resolve(blob);
2497
+ }).catch((e) => {
2498
+ debug.error(e);
2499
+ resolve(null);
2500
+ });
2501
+ });
2502
+ };
2503
+ canvas.toDataURL = function (type, quality) {
2504
+ return Platform.origin.canvasToDataURL(this.view, type, quality);
2505
+ };
2506
+ canvas.saveAs = function (filename, quality) {
2507
+ return new Promise((resolve) => {
2508
+ Platform.origin.canvasSaveAs(this.view, filename, quality).then(() => {
2509
+ resolve(true);
2510
+ }).catch((e) => {
2511
+ debug.error(e);
2512
+ resolve(false);
2513
+ });
2514
+ });
2515
+ };
2342
2516
 
2343
2517
  Object.assign(TextConvert, TextConvertModule);
2344
2518
  Object.assign(ColorConvert, ColorConvertModule);
2345
2519
  Object.assign(Paint, PaintModule);
2346
- Object.assign(PaintImage$1, PaintImageModule);
2347
- Object.assign(PaintGradient$1, PaintGradientModule);
2520
+ Object.assign(PaintImage, PaintImageModule);
2521
+ Object.assign(PaintGradient, PaintGradientModule);
2348
2522
  Object.assign(Effect, EffectModule);
2349
2523
  Object.assign(Export, ExportModule);
2350
2524