leafer-draw 1.6.2 → 1.6.3

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.cjs CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  var core = require('@leafer/core');
4
4
  var draw = require('@leafer-ui/draw');
5
+ var core$1 = require('@leafer-ui/core');
5
6
 
6
7
  const debug$2 = core.Debug.get('LeaferCanvas');
7
8
  class LeaferCanvas extends core.LeaferCanvasBase {
@@ -341,17 +342,15 @@ class Watcher {
341
342
  this.target.emitEvent(new core.WatchEvent(core.WatchEvent.DATA, { updatedList: this.updatedList }));
342
343
  this.__updatedList = new core.LeafList();
343
344
  this.totalTimes++;
344
- this.changed = false;
345
- this.hasVisible = false;
346
- this.hasRemove = false;
347
- this.hasAdd = false;
345
+ this.changed = this.hasVisible = this.hasRemove = this.hasAdd = false;
348
346
  }
349
347
  __listenEvents() {
350
- const { target } = this;
351
348
  this.__eventIds = [
352
- target.on_(core.PropertyEvent.CHANGE, this.__onAttrChange, this),
353
- target.on_([core.ChildEvent.ADD, core.ChildEvent.REMOVE], this.__onChildEvent, this),
354
- target.on_(core.WatchEvent.REQUEST, this.__onRquestData, this)
349
+ this.target.on_([
350
+ [core.PropertyEvent.CHANGE, this.__onAttrChange, this],
351
+ [[core.ChildEvent.ADD, core.ChildEvent.REMOVE], this.__onChildEvent, this],
352
+ [core.WatchEvent.REQUEST, this.__onRquestData, this]
353
+ ])
355
354
  ];
356
355
  }
357
356
  __removeListenEvents() {
@@ -361,8 +360,7 @@ class Watcher {
361
360
  if (this.target) {
362
361
  this.stop();
363
362
  this.__removeListenEvents();
364
- this.target = null;
365
- this.__updatedList = null;
363
+ this.target = this.__updatedList = null;
366
364
  }
367
365
  }
368
366
  }
@@ -467,7 +465,7 @@ class Layouter {
467
465
  this.disabled = true;
468
466
  }
469
467
  layout() {
470
- if (!this.running)
468
+ if (this.layouting || !this.running)
471
469
  return;
472
470
  const { target } = this;
473
471
  this.times = 0;
@@ -550,12 +548,10 @@ class Layouter {
550
548
  }
551
549
  static fullLayout(target) {
552
550
  updateAllMatrix(target, true);
553
- if (target.isBranch) {
551
+ if (target.isBranch)
554
552
  core.BranchHelper.updateBounds(target);
555
- }
556
- else {
553
+ else
557
554
  core.LeafHelper.updateBounds(target);
558
- }
559
555
  updateAllChange(target);
560
556
  }
561
557
  addExtra(leaf) {
@@ -578,11 +574,12 @@ class Layouter {
578
574
  this.__updatedList = event.data.updatedList;
579
575
  }
580
576
  __listenEvents() {
581
- const { target } = this;
582
577
  this.__eventIds = [
583
- target.on_(core.LayoutEvent.REQUEST, this.layout, this),
584
- target.on_(core.LayoutEvent.AGAIN, this.layoutAgain, this),
585
- target.on_(core.WatchEvent.DATA, this.__onReceiveWatchData, this)
578
+ this.target.on_([
579
+ [core.LayoutEvent.REQUEST, this.layout, this],
580
+ [core.LayoutEvent.AGAIN, this.layoutAgain, this],
581
+ [core.WatchEvent.DATA, this.__onReceiveWatchData, this]
582
+ ])
586
583
  ];
587
584
  }
588
585
  __removeListenEvents() {
@@ -813,12 +810,13 @@ class Renderer {
813
810
  this.target.emitEvent(new core.RenderEvent(type, this.times, bounds, options));
814
811
  }
815
812
  __listenEvents() {
816
- const { target } = this;
817
813
  this.__eventIds = [
818
- target.on_(core.RenderEvent.REQUEST, this.update, this),
819
- target.on_(core.LayoutEvent.END, this.__onLayoutEnd, this),
820
- target.on_(core.RenderEvent.AGAIN, this.renderAgain, this),
821
- target.on_(core.ResizeEvent.RESIZE, this.__onResize, this)
814
+ this.target.on_([
815
+ [core.RenderEvent.REQUEST, this.update, this],
816
+ [core.LayoutEvent.END, this.__onLayoutEnd, this],
817
+ [core.RenderEvent.AGAIN, this.renderAgain, this],
818
+ [core.ResizeEvent.RESIZE, this.__onResize, this]
819
+ ])
822
820
  ];
823
821
  }
824
822
  __removeListenEvents() {
@@ -904,32 +902,34 @@ function fillPathOrText(ui, canvas) {
904
902
  ui.__.__font ? fillText(ui, canvas) : (ui.__.windingRule ? canvas.fill(ui.__.windingRule) : canvas.fill());
905
903
  }
906
904
 
905
+ const Paint = {};
906
+
907
907
  function strokeText(stroke, ui, canvas) {
908
- const { strokeAlign } = ui.__;
909
- const isStrokes = typeof stroke !== 'string';
910
- switch (strokeAlign) {
908
+ switch (ui.__.strokeAlign) {
911
909
  case 'center':
912
- canvas.setStroke(isStrokes ? undefined : stroke, ui.__.strokeWidth, ui.__);
913
- isStrokes ? drawStrokesStyle(stroke, true, ui, canvas) : drawTextStroke(ui, canvas);
910
+ drawCenter$1(stroke, 1, ui, canvas);
914
911
  break;
915
912
  case 'inside':
916
- drawAlignStroke('inside', stroke, isStrokes, ui, canvas);
913
+ drawAlign(stroke, 'inside', ui, canvas);
917
914
  break;
918
915
  case 'outside':
919
- drawAlignStroke('outside', stroke, isStrokes, ui, canvas);
916
+ ui.__.__fillAfterStroke ? drawCenter$1(stroke, 2, ui, canvas) : drawAlign(stroke, 'outside', ui, canvas);
920
917
  break;
921
918
  }
922
919
  }
923
- function drawAlignStroke(align, stroke, isStrokes, ui, canvas) {
924
- const { __strokeWidth, __font } = ui.__;
920
+ function drawCenter$1(stroke, strokeWidthScale, ui, canvas) {
921
+ const data = ui.__;
922
+ canvas.setStroke(!data.__isStrokes && stroke, data.strokeWidth * strokeWidthScale, data);
923
+ data.__isStrokes ? drawStrokesStyle(stroke, true, ui, canvas) : drawTextStroke(ui, canvas);
924
+ }
925
+ function drawAlign(stroke, align, ui, canvas) {
925
926
  const out = canvas.getSameCanvas(true, true);
926
- out.setStroke(isStrokes ? undefined : stroke, __strokeWidth * 2, ui.__);
927
- out.font = __font;
928
- isStrokes ? drawStrokesStyle(stroke, true, ui, out) : drawTextStroke(ui, out);
927
+ out.font = ui.__.__font;
928
+ drawCenter$1(stroke, 2, ui, out);
929
929
  out.blendMode = align === 'outside' ? 'destination-out' : 'destination-in';
930
930
  fillText(ui, out);
931
931
  out.blendMode = 'normal';
932
- if (ui.__worldFlipped)
932
+ if (ui.__worldFlipped || core$1.Platform.fullImageShadow)
933
933
  canvas.copyWorldByReset(out, ui.__nowWorld);
934
934
  else
935
935
  canvas.copyWorldToInner(out, ui.__nowWorld, ui.__layout.renderBounds);
@@ -971,90 +971,60 @@ function drawStrokesStyle(strokes, isText, ui, canvas) {
971
971
  }
972
972
 
973
973
  function stroke(stroke, ui, canvas) {
974
- const options = ui.__;
975
- const { __strokeWidth, strokeAlign, __font } = options;
976
- if (!__strokeWidth)
974
+ const data = ui.__;
975
+ if (!data.__strokeWidth)
977
976
  return;
978
- if (__font) {
977
+ if (data.__font) {
979
978
  strokeText(stroke, ui, canvas);
980
979
  }
981
980
  else {
982
- switch (strokeAlign) {
981
+ switch (data.strokeAlign) {
983
982
  case 'center':
984
- canvas.setStroke(stroke, __strokeWidth, options);
985
- canvas.stroke();
986
- if (options.__useArrow)
987
- strokeArrow(ui, canvas);
983
+ drawCenter(stroke, 1, ui, canvas);
988
984
  break;
989
985
  case 'inside':
990
- canvas.save();
991
- canvas.setStroke(stroke, __strokeWidth * 2, options);
992
- options.windingRule ? canvas.clip(options.windingRule) : canvas.clip();
993
- canvas.stroke();
994
- canvas.restore();
986
+ drawInside(stroke, ui, canvas);
995
987
  break;
996
988
  case 'outside':
997
- const out = canvas.getSameCanvas(true, true);
998
- out.setStroke(stroke, __strokeWidth * 2, options);
999
- ui.__drawRenderPath(out);
1000
- out.stroke();
1001
- options.windingRule ? out.clip(options.windingRule) : out.clip();
1002
- out.clearWorld(ui.__layout.renderBounds);
1003
- if (ui.__worldFlipped)
1004
- canvas.copyWorldByReset(out, ui.__nowWorld);
1005
- else
1006
- canvas.copyWorldToInner(out, ui.__nowWorld, ui.__layout.renderBounds);
1007
- out.recycle(ui.__nowWorld);
989
+ drawOutside(stroke, ui, canvas);
1008
990
  break;
1009
991
  }
1010
992
  }
1011
993
  }
1012
994
  function strokes(strokes, ui, canvas) {
1013
- const options = ui.__;
1014
- const { __strokeWidth, strokeAlign, __font } = options;
1015
- if (!__strokeWidth)
1016
- return;
1017
- if (__font) {
1018
- strokeText(strokes, ui, canvas);
995
+ stroke(strokes, ui, canvas);
996
+ }
997
+ function drawCenter(stroke, strokeWidthScale, ui, canvas) {
998
+ const data = ui.__;
999
+ canvas.setStroke(!data.__isStrokes && stroke, data.__strokeWidth * strokeWidthScale, data);
1000
+ data.__isStrokes ? drawStrokesStyle(stroke, false, ui, canvas) : canvas.stroke();
1001
+ if (data.__useArrow)
1002
+ Paint.strokeArrow(stroke, ui, canvas);
1003
+ }
1004
+ function drawInside(stroke, ui, canvas) {
1005
+ const data = ui.__;
1006
+ canvas.save();
1007
+ data.windingRule ? canvas.clip(data.windingRule) : canvas.clip();
1008
+ drawCenter(stroke, 2, ui, canvas);
1009
+ canvas.restore();
1010
+ }
1011
+ function drawOutside(stroke, ui, canvas) {
1012
+ const data = ui.__;
1013
+ if (data.__fillAfterStroke) {
1014
+ drawCenter(stroke, 2, ui, canvas);
1019
1015
  }
1020
1016
  else {
1021
- switch (strokeAlign) {
1022
- case 'center':
1023
- canvas.setStroke(undefined, __strokeWidth, options);
1024
- drawStrokesStyle(strokes, false, ui, canvas);
1025
- if (options.__useArrow)
1026
- strokeArrow(ui, canvas);
1027
- break;
1028
- case 'inside':
1029
- canvas.save();
1030
- canvas.setStroke(undefined, __strokeWidth * 2, options);
1031
- options.windingRule ? canvas.clip(options.windingRule) : canvas.clip();
1032
- drawStrokesStyle(strokes, false, ui, canvas);
1033
- canvas.restore();
1034
- break;
1035
- case 'outside':
1036
- const { renderBounds } = ui.__layout;
1037
- const out = canvas.getSameCanvas(true, true);
1038
- ui.__drawRenderPath(out);
1039
- out.setStroke(undefined, __strokeWidth * 2, options);
1040
- drawStrokesStyle(strokes, false, ui, out);
1041
- options.windingRule ? out.clip(options.windingRule) : out.clip();
1042
- out.clearWorld(renderBounds);
1043
- if (ui.__worldFlipped)
1044
- canvas.copyWorldByReset(out, ui.__nowWorld);
1045
- else
1046
- canvas.copyWorldToInner(out, ui.__nowWorld, renderBounds);
1047
- out.recycle(ui.__nowWorld);
1048
- break;
1049
- }
1050
- }
1051
- }
1052
- function strokeArrow(ui, canvas) {
1053
- if (ui.__.dashPattern) {
1054
- canvas.beginPath();
1055
- ui.__drawPathByData(canvas, ui.__.__pathForArrow);
1056
- canvas.dashPattern = null;
1057
- canvas.stroke();
1017
+ const { renderBounds } = ui.__layout;
1018
+ const out = canvas.getSameCanvas(true, true);
1019
+ ui.__drawRenderPath(out);
1020
+ drawCenter(stroke, 2, ui, out);
1021
+ data.windingRule ? out.clip(data.windingRule) : out.clip();
1022
+ out.clearWorld(renderBounds);
1023
+ if (ui.__worldFlipped || core$1.Platform.fullImageShadow)
1024
+ canvas.copyWorldByReset(out, ui.__nowWorld);
1025
+ else
1026
+ canvas.copyWorldToInner(out, ui.__nowWorld, renderBounds);
1027
+ out.recycle(ui.__nowWorld);
1058
1028
  }
1059
1029
  }
1060
1030
 
@@ -1101,9 +1071,10 @@ function shape(ui, current, options) {
1101
1071
  }
1102
1072
 
1103
1073
  let recycleMap;
1074
+ const { stintSet } = core.DataHelper, { hasTransparent: hasTransparent$1 } = draw.ColorConvert;
1104
1075
  function compute(attrName, ui) {
1105
1076
  const data = ui.__, leafPaints = [];
1106
- let paints = data.__input[attrName], hasOpacityPixel;
1077
+ let paints = data.__input[attrName], isAlphaPixel, isTransparent;
1107
1078
  if (!(paints instanceof Array))
1108
1079
  paints = [paints];
1109
1080
  recycleMap = draw.PaintImage.recycleImage(attrName, data);
@@ -1113,29 +1084,55 @@ function compute(attrName, ui) {
1113
1084
  leafPaints.push(item);
1114
1085
  }
1115
1086
  data['_' + attrName] = leafPaints.length ? leafPaints : undefined;
1116
- if (leafPaints.length && leafPaints[0].image)
1117
- hasOpacityPixel = leafPaints[0].image.hasOpacityPixel;
1118
- attrName === 'fill' ? data.__pixelFill = hasOpacityPixel : data.__pixelStroke = hasOpacityPixel;
1087
+ if (leafPaints.length) {
1088
+ if (leafPaints.every(item => item.isTransparent)) {
1089
+ if (leafPaints.some(item => item.image))
1090
+ isAlphaPixel = true;
1091
+ isTransparent = true;
1092
+ }
1093
+ }
1094
+ if (attrName === 'fill') {
1095
+ stintSet(data, '__isAlphaPixelFill', isAlphaPixel);
1096
+ stintSet(data, '__isTransparentFill', isTransparent);
1097
+ }
1098
+ else {
1099
+ stintSet(data, '__isAlphaPixelStroke', isAlphaPixel);
1100
+ stintSet(data, '__isTransparentStroke', isTransparent);
1101
+ }
1119
1102
  }
1120
1103
  function getLeafPaint(attrName, paint, ui) {
1121
1104
  if (typeof paint !== 'object' || paint.visible === false || paint.opacity === 0)
1122
1105
  return undefined;
1106
+ let data;
1123
1107
  const { boxBounds } = ui.__layout;
1124
1108
  switch (paint.type) {
1125
- case 'solid':
1126
- let { type, blendMode, color, opacity } = paint;
1127
- return { type, blendMode, style: draw.ColorConvert.string(color, opacity) };
1128
1109
  case 'image':
1129
- return draw.PaintImage.image(ui, attrName, paint, boxBounds, !recycleMap || !recycleMap[paint.url]);
1110
+ data = draw.PaintImage.image(ui, attrName, paint, boxBounds, !recycleMap || !recycleMap[paint.url]);
1111
+ break;
1130
1112
  case 'linear':
1131
- return draw.PaintGradient.linearGradient(paint, boxBounds);
1113
+ data = draw.PaintGradient.linearGradient(paint, boxBounds);
1114
+ break;
1132
1115
  case 'radial':
1133
- return draw.PaintGradient.radialGradient(paint, boxBounds);
1116
+ data = draw.PaintGradient.radialGradient(paint, boxBounds);
1117
+ break;
1134
1118
  case 'angular':
1135
- return draw.PaintGradient.conicGradient(paint, boxBounds);
1119
+ data = draw.PaintGradient.conicGradient(paint, boxBounds);
1120
+ break;
1121
+ case 'solid':
1122
+ const { type, blendMode, color, opacity } = paint;
1123
+ data = { type, blendMode, style: draw.ColorConvert.string(color, opacity) };
1124
+ break;
1136
1125
  default:
1137
- return paint.r !== undefined ? { type: 'solid', style: draw.ColorConvert.string(paint) } : undefined;
1126
+ if (paint.r !== undefined)
1127
+ data = { type: 'solid', style: draw.ColorConvert.string(paint) };
1138
1128
  }
1129
+ if (data) {
1130
+ if (typeof data.style === 'string' && hasTransparent$1(data.style))
1131
+ data.isTransparent = true;
1132
+ if (paint.blendMode)
1133
+ data.blendMode = paint.blendMode;
1134
+ }
1135
+ return data;
1139
1136
  }
1140
1137
 
1141
1138
  const PaintModule = {
@@ -1201,12 +1198,10 @@ function repeatMode(data, box, width, height, x, y, scaleX, scaleY, rotation, al
1201
1198
 
1202
1199
  const { get: get$2, translate } = core.MatrixHelper;
1203
1200
  const tempBox = new core.Bounds();
1204
- const tempPoint = {};
1205
1201
  const tempScaleData = {};
1202
+ const tempImage = {};
1206
1203
  function createData(leafPaint, image, paint, box) {
1207
- const { blendMode, changeful, sync } = paint;
1208
- if (blendMode)
1209
- leafPaint.blendMode = blendMode;
1204
+ const { changeful, sync } = paint;
1210
1205
  if (changeful)
1211
1206
  leafPaint.changeful = changeful;
1212
1207
  if (sync)
@@ -1214,38 +1209,38 @@ function createData(leafPaint, image, paint, box) {
1214
1209
  leafPaint.data = getPatternData(paint, box, image);
1215
1210
  }
1216
1211
  function getPatternData(paint, box, image) {
1217
- let { width, height } = image;
1218
1212
  if (paint.padding)
1219
1213
  box = tempBox.set(box).shrink(paint.padding);
1220
1214
  if (paint.mode === 'strench')
1221
1215
  paint.mode = 'stretch';
1216
+ let { width, height } = image;
1222
1217
  const { opacity, mode, align, offset, scale, size, rotation, repeat, filters } = paint;
1223
1218
  const sameBox = box.width === width && box.height === height;
1224
1219
  const data = { mode };
1225
1220
  const swapSize = align !== 'center' && (rotation || 0) % 180 === 90;
1226
- const swapWidth = swapSize ? height : width, swapHeight = swapSize ? width : height;
1227
- let x = 0, y = 0, scaleX, scaleY;
1221
+ core.BoundsHelper.set(tempImage, 0, 0, swapSize ? height : width, swapSize ? width : height);
1222
+ let scaleX, scaleY;
1228
1223
  if (!mode || mode === 'cover' || mode === 'fit') {
1229
1224
  if (!sameBox || rotation) {
1230
- const sw = box.width / swapWidth, sh = box.height / swapHeight;
1231
- scaleX = scaleY = mode === 'fit' ? Math.min(sw, sh) : Math.max(sw, sh);
1232
- x += (box.width - width * scaleX) / 2, y += (box.height - height * scaleY) / 2;
1225
+ scaleX = scaleY = core.BoundsHelper.getFitScale(box, tempImage, mode !== 'fit');
1226
+ core.BoundsHelper.put(box, image, align, scaleX, false, tempImage);
1227
+ core.BoundsHelper.scale(tempImage, scaleX, scaleY, true);
1233
1228
  }
1234
1229
  }
1235
- else if (scale || size) {
1236
- core.MathHelper.getScaleData(scale, size, image, tempScaleData);
1237
- scaleX = tempScaleData.scaleX;
1238
- scaleY = tempScaleData.scaleY;
1239
- }
1240
- if (align) {
1241
- const imageBounds = { x, y, width: swapWidth, height: swapHeight };
1242
- if (scaleX)
1243
- imageBounds.width *= scaleX, imageBounds.height *= scaleY;
1244
- core.AlignHelper.toPoint(align, imageBounds, box, tempPoint, true);
1245
- x += tempPoint.x, y += tempPoint.y;
1230
+ else {
1231
+ if (scale || size) {
1232
+ core.MathHelper.getScaleData(scale, size, image, tempScaleData);
1233
+ scaleX = tempScaleData.scaleX;
1234
+ scaleY = tempScaleData.scaleY;
1235
+ }
1236
+ if (align) {
1237
+ if (scaleX)
1238
+ core.BoundsHelper.scale(tempImage, scaleX, scaleY, true);
1239
+ core.AlignHelper.toPoint(align, tempImage, box, tempImage, true, true);
1240
+ }
1246
1241
  }
1247
1242
  if (offset)
1248
- x += offset.x, y += offset.y;
1243
+ core.PointHelper.move(tempImage, offset);
1249
1244
  switch (mode) {
1250
1245
  case 'stretch':
1251
1246
  if (!sameBox)
@@ -1253,12 +1248,12 @@ function getPatternData(paint, box, image) {
1253
1248
  break;
1254
1249
  case 'normal':
1255
1250
  case 'clip':
1256
- if (x || y || scaleX || rotation)
1257
- clipMode(data, box, x, y, scaleX, scaleY, rotation);
1251
+ if (tempImage.x || tempImage.y || scaleX || rotation)
1252
+ clipMode(data, box, tempImage.x, tempImage.y, scaleX, scaleY, rotation);
1258
1253
  break;
1259
1254
  case 'repeat':
1260
1255
  if (!sameBox || scaleX || rotation)
1261
- repeatMode(data, box, width, height, x, y, scaleX, scaleY, rotation, align);
1256
+ repeatMode(data, box, width, height, tempImage.x, tempImage.y, scaleX, scaleY, rotation, align);
1262
1257
  if (!repeat)
1263
1258
  data.repeat = 'repeat';
1264
1259
  break;
@@ -1266,7 +1261,7 @@ function getPatternData(paint, box, image) {
1266
1261
  case 'cover':
1267
1262
  default:
1268
1263
  if (scaleX)
1269
- fillOrFitMode(data, box, x, y, scaleX, scaleY, rotation);
1264
+ fillOrFitMode(data, box, tempImage.x, tempImage.y, scaleX, scaleY, rotation);
1270
1265
  }
1271
1266
  if (!data.transform) {
1272
1267
  if (box.x || box.y) {
@@ -1299,6 +1294,8 @@ function image(ui, attrName, paint, boxBounds, firstUse) {
1299
1294
  }
1300
1295
  else {
1301
1296
  leafPaint = { type: paint.type, image };
1297
+ if (image.hasAlphaPixel)
1298
+ leafPaint.isTransparent = true;
1302
1299
  cache = image.use > 1 ? { leafPaint, paint, boxBounds: box.set(boxBounds) } : null;
1303
1300
  }
1304
1301
  if (firstUse || image.loading)
@@ -1323,7 +1320,7 @@ function image(ui, attrName, paint, boxBounds, firstUse) {
1323
1320
  ignoreRender(ui, false);
1324
1321
  if (!ui.destroyed) {
1325
1322
  if (checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds)) {
1326
- if (image.hasOpacityPixel)
1323
+ if (image.hasAlphaPixel)
1327
1324
  ui.__layout.hitCanvasChanged = true;
1328
1325
  ui.forceUpdate('surface');
1329
1326
  }
@@ -1335,13 +1332,17 @@ function image(ui, attrName, paint, boxBounds, firstUse) {
1335
1332
  onLoadError(ui, event, error);
1336
1333
  leafPaint.loadId = null;
1337
1334
  });
1338
- if (ui.placeholderColor)
1339
- setTimeout(() => {
1340
- if (!(image.ready || image.isPlacehold)) {
1341
- image.isPlacehold = true;
1342
- ui.forceUpdate('surface');
1343
- }
1344
- }, 100);
1335
+ if (ui.placeholderColor) {
1336
+ if (!ui.placeholderDelay)
1337
+ image.isPlacehold = true;
1338
+ else
1339
+ setTimeout(() => {
1340
+ if (!image.ready) {
1341
+ image.isPlacehold = true;
1342
+ ui.forceUpdate('surface');
1343
+ }
1344
+ }, ui.placeholderDelay);
1345
+ }
1345
1346
  }
1346
1347
  return leafPaint;
1347
1348
  }
@@ -1579,32 +1580,33 @@ const PaintImageModule = {
1579
1580
  repeatMode
1580
1581
  };
1581
1582
 
1582
- const { toPoint: toPoint$2 } = core.AroundHelper;
1583
+ const { toPoint: toPoint$2 } = core.AroundHelper, { hasTransparent } = draw.ColorConvert;
1583
1584
  const realFrom$2 = {};
1584
1585
  const realTo$2 = {};
1585
1586
  function linearGradient(paint, box) {
1586
- let { from, to, type, blendMode, opacity } = paint;
1587
+ let { from, to, type, opacity } = paint;
1587
1588
  toPoint$2(from || 'top', box, realFrom$2);
1588
1589
  toPoint$2(to || 'bottom', box, realTo$2);
1589
1590
  const style = core.Platform.canvas.createLinearGradient(realFrom$2.x, realFrom$2.y, realTo$2.x, realTo$2.y);
1590
- applyStops(style, paint.stops, opacity);
1591
1591
  const data = { type, style };
1592
- if (blendMode)
1593
- data.blendMode = blendMode;
1592
+ applyStops(data, style, paint.stops, opacity);
1594
1593
  return data;
1595
1594
  }
1596
- function applyStops(gradient, stops, opacity) {
1595
+ function applyStops(data, gradient, stops, opacity) {
1597
1596
  if (stops) {
1598
- let stop;
1597
+ let stop, color, offset, isTransparent;
1599
1598
  for (let i = 0, len = stops.length; i < len; i++) {
1600
1599
  stop = stops[i];
1601
- if (typeof stop === 'string') {
1602
- gradient.addColorStop(i / (len - 1), draw.ColorConvert.string(stop, opacity));
1603
- }
1604
- else {
1605
- gradient.addColorStop(stop.offset, draw.ColorConvert.string(stop.color, opacity));
1606
- }
1600
+ if (typeof stop === 'string')
1601
+ offset = i / (len - 1), color = draw.ColorConvert.string(stop, opacity);
1602
+ else
1603
+ offset = stop.offset, color = draw.ColorConvert.string(stop.color, opacity);
1604
+ gradient.addColorStop(offset, color);
1605
+ if (!isTransparent && hasTransparent(color))
1606
+ isTransparent = true;
1607
1607
  }
1608
+ if (isTransparent)
1609
+ data.isTransparent = true;
1608
1610
  }
1609
1611
  }
1610
1612
 
@@ -1614,17 +1616,15 @@ const { toPoint: toPoint$1 } = core.AroundHelper;
1614
1616
  const realFrom$1 = {};
1615
1617
  const realTo$1 = {};
1616
1618
  function radialGradient(paint, box) {
1617
- let { from, to, type, opacity, blendMode, stretch } = paint;
1619
+ let { from, to, type, opacity, stretch } = paint;
1618
1620
  toPoint$1(from || 'center', box, realFrom$1);
1619
1621
  toPoint$1(to || 'bottom', box, realTo$1);
1620
1622
  const style = core.Platform.canvas.createRadialGradient(realFrom$1.x, realFrom$1.y, 0, realFrom$1.x, realFrom$1.y, getDistance$1(realFrom$1, realTo$1));
1621
- applyStops(style, paint.stops, opacity);
1622
1623
  const data = { type, style };
1624
+ applyStops(data, style, paint.stops, opacity);
1623
1625
  const transform = getTransform(box, realFrom$1, realTo$1, stretch, true);
1624
1626
  if (transform)
1625
1627
  data.transform = transform;
1626
- if (blendMode)
1627
- data.blendMode = blendMode;
1628
1628
  return data;
1629
1629
  }
1630
1630
  function getTransform(box, from, to, stretch, rotate90) {
@@ -1650,17 +1650,15 @@ const { toPoint } = core.AroundHelper;
1650
1650
  const realFrom = {};
1651
1651
  const realTo = {};
1652
1652
  function conicGradient(paint, box) {
1653
- let { from, to, type, opacity, blendMode, stretch } = paint;
1653
+ let { from, to, type, opacity, stretch } = paint;
1654
1654
  toPoint(from || 'center', box, realFrom);
1655
1655
  toPoint(to || 'bottom', box, realTo);
1656
1656
  const style = core.Platform.conicGradientSupport ? core.Platform.canvas.createConicGradient(0, realFrom.x, realFrom.y) : core.Platform.canvas.createRadialGradient(realFrom.x, realFrom.y, 0, realFrom.x, realFrom.y, getDistance(realFrom, realTo));
1657
- applyStops(style, paint.stops, opacity);
1658
1657
  const data = { type, style };
1658
+ applyStops(data, style, paint.stops, opacity);
1659
1659
  const transform = getTransform(box, realFrom, realTo, stretch || 1, core.Platform.conicGradientRotate90);
1660
1660
  if (transform)
1661
1661
  data.transform = transform;
1662
- if (blendMode)
1663
- data.blendMode = blendMode;
1664
1662
  return data;
1665
1663
  }
1666
1664
 
@@ -1993,6 +1991,8 @@ function createRows(drawData, content, style) {
1993
1991
  lastCharType = null;
1994
1992
  startCharSize = charWidth = charSize = wordWidth = rowWidth = 0;
1995
1993
  word = { data: [] }, row = { words: [] };
1994
+ if (__letterSpacing)
1995
+ content = [...content];
1996
1996
  for (let i = 0, len = content.length; i < len; i++) {
1997
1997
  char = content[i];
1998
1998
  if (char === '\n') {