@leafer-draw/miniapp 1.6.2 → 1.6.4

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.
@@ -1,7 +1,7 @@
1
- import { LeaferCanvasBase, Platform, canvasPatch, DataHelper, canvasSizeAttrs, ResizeEvent, FileHelper, Creator, LeaferImage, defineKey, LeafList, RenderEvent, ChildEvent, WatchEvent, PropertyEvent, LeafHelper, BranchHelper, LeafBoundsHelper, Bounds, Debug, LeafLevelList, LayoutEvent, Run, ImageManager, BoundsHelper, MatrixHelper, MathHelper, AlignHelper, ImageEvent, AroundHelper, PointHelper, Direction4 } from '@leafer/core';
1
+ import { LeaferCanvasBase, Platform, canvasPatch, DataHelper, canvasSizeAttrs, ResizeEvent, FileHelper, Creator, LeaferImage, defineKey, LeafList, RenderEvent, ChildEvent, WatchEvent, PropertyEvent, LeafHelper, BranchHelper, LeafBoundsHelper, Bounds, Debug, LeafLevelList, LayoutEvent, Run, ImageManager, BoundsHelper, MatrixHelper, MathHelper, AlignHelper, PointHelper, ImageEvent, AroundHelper, Direction4 } from '@leafer/core';
2
2
  export * from '@leafer/core';
3
3
  export { LeaferImage } from '@leafer/core';
4
- import { PaintImage, ColorConvert, PaintGradient, Export, Group, TextConvert, Paint, Effect } from '@leafer-ui/draw';
4
+ import { PaintImage, Paint, ColorConvert, PaintGradient, Export, Group, TextConvert, Effect } from '@leafer-ui/draw';
5
5
  export * from '@leafer-ui/draw';
6
6
 
7
7
  class LeaferCanvas extends LeaferCanvasBase {
@@ -300,17 +300,15 @@ class Watcher {
300
300
  this.target.emitEvent(new WatchEvent(WatchEvent.DATA, { updatedList: this.updatedList }));
301
301
  this.__updatedList = new LeafList();
302
302
  this.totalTimes++;
303
- this.changed = false;
304
- this.hasVisible = false;
305
- this.hasRemove = false;
306
- this.hasAdd = false;
303
+ this.changed = this.hasVisible = this.hasRemove = this.hasAdd = false;
307
304
  }
308
305
  __listenEvents() {
309
- const { target } = this;
310
306
  this.__eventIds = [
311
- target.on_(PropertyEvent.CHANGE, this.__onAttrChange, this),
312
- target.on_([ChildEvent.ADD, ChildEvent.REMOVE], this.__onChildEvent, this),
313
- target.on_(WatchEvent.REQUEST, this.__onRquestData, this)
307
+ this.target.on_([
308
+ [PropertyEvent.CHANGE, this.__onAttrChange, this],
309
+ [[ChildEvent.ADD, ChildEvent.REMOVE], this.__onChildEvent, this],
310
+ [WatchEvent.REQUEST, this.__onRquestData, this]
311
+ ])
314
312
  ];
315
313
  }
316
314
  __removeListenEvents() {
@@ -320,8 +318,7 @@ class Watcher {
320
318
  if (this.target) {
321
319
  this.stop();
322
320
  this.__removeListenEvents();
323
- this.target = null;
324
- this.__updatedList = null;
321
+ this.target = this.__updatedList = null;
325
322
  }
326
323
  }
327
324
  }
@@ -426,7 +423,7 @@ class Layouter {
426
423
  this.disabled = true;
427
424
  }
428
425
  layout() {
429
- if (!this.running)
426
+ if (this.layouting || !this.running)
430
427
  return;
431
428
  const { target } = this;
432
429
  this.times = 0;
@@ -509,12 +506,10 @@ class Layouter {
509
506
  }
510
507
  static fullLayout(target) {
511
508
  updateAllMatrix(target, true);
512
- if (target.isBranch) {
509
+ if (target.isBranch)
513
510
  BranchHelper.updateBounds(target);
514
- }
515
- else {
511
+ else
516
512
  LeafHelper.updateBounds(target);
517
- }
518
513
  updateAllChange(target);
519
514
  }
520
515
  addExtra(leaf) {
@@ -537,11 +532,12 @@ class Layouter {
537
532
  this.__updatedList = event.data.updatedList;
538
533
  }
539
534
  __listenEvents() {
540
- const { target } = this;
541
535
  this.__eventIds = [
542
- target.on_(LayoutEvent.REQUEST, this.layout, this),
543
- target.on_(LayoutEvent.AGAIN, this.layoutAgain, this),
544
- target.on_(WatchEvent.DATA, this.__onReceiveWatchData, this)
536
+ this.target.on_([
537
+ [LayoutEvent.REQUEST, this.layout, this],
538
+ [LayoutEvent.AGAIN, this.layoutAgain, this],
539
+ [WatchEvent.DATA, this.__onReceiveWatchData, this]
540
+ ])
545
541
  ];
546
542
  }
547
543
  __removeListenEvents() {
@@ -772,12 +768,13 @@ class Renderer {
772
768
  this.target.emitEvent(new RenderEvent(type, this.times, bounds, options));
773
769
  }
774
770
  __listenEvents() {
775
- const { target } = this;
776
771
  this.__eventIds = [
777
- target.on_(RenderEvent.REQUEST, this.update, this),
778
- target.on_(LayoutEvent.END, this.__onLayoutEnd, this),
779
- target.on_(RenderEvent.AGAIN, this.renderAgain, this),
780
- target.on_(ResizeEvent.RESIZE, this.__onResize, this)
772
+ this.target.on_([
773
+ [RenderEvent.REQUEST, this.update, this],
774
+ [LayoutEvent.END, this.__onLayoutEnd, this],
775
+ [RenderEvent.AGAIN, this.renderAgain, this],
776
+ [ResizeEvent.RESIZE, this.__onResize, this]
777
+ ])
781
778
  ];
782
779
  }
783
780
  __removeListenEvents() {
@@ -864,35 +861,38 @@ function fillPathOrText(ui, canvas) {
864
861
  }
865
862
 
866
863
  function strokeText(stroke, ui, canvas) {
867
- const { strokeAlign } = ui.__;
868
- const isStrokes = typeof stroke !== 'string';
869
- switch (strokeAlign) {
864
+ switch (ui.__.strokeAlign) {
870
865
  case 'center':
871
- canvas.setStroke(isStrokes ? undefined : stroke, ui.__.strokeWidth, ui.__);
872
- isStrokes ? drawStrokesStyle(stroke, true, ui, canvas) : drawTextStroke(ui, canvas);
866
+ drawCenter$1(stroke, 1, ui, canvas);
873
867
  break;
874
868
  case 'inside':
875
- drawAlignStroke('inside', stroke, isStrokes, ui, canvas);
869
+ drawAlign(stroke, 'inside', ui, canvas);
876
870
  break;
877
871
  case 'outside':
878
- drawAlignStroke('outside', stroke, isStrokes, ui, canvas);
872
+ ui.__.__fillAfterStroke ? drawCenter$1(stroke, 2, ui, canvas) : drawAlign(stroke, 'outside', ui, canvas);
879
873
  break;
880
874
  }
881
875
  }
882
- function drawAlignStroke(align, stroke, isStrokes, ui, canvas) {
883
- const { __strokeWidth, __font } = ui.__;
876
+ function drawCenter$1(stroke, strokeWidthScale, ui, canvas) {
877
+ const data = ui.__;
878
+ canvas.setStroke(!data.__isStrokes && stroke, data.strokeWidth * strokeWidthScale, data);
879
+ data.__isStrokes ? drawStrokesStyle(stroke, true, ui, canvas) : drawTextStroke(ui, canvas);
880
+ }
881
+ function drawAlign(stroke, align, ui, canvas) {
884
882
  const out = canvas.getSameCanvas(true, true);
885
- out.setStroke(isStrokes ? undefined : stroke, __strokeWidth * 2, ui.__);
886
- out.font = __font;
887
- isStrokes ? drawStrokesStyle(stroke, true, ui, out) : drawTextStroke(ui, out);
883
+ out.font = ui.__.__font;
884
+ drawCenter$1(stroke, 2, ui, out);
888
885
  out.blendMode = align === 'outside' ? 'destination-out' : 'destination-in';
889
886
  fillText(ui, out);
890
887
  out.blendMode = 'normal';
891
- if (ui.__worldFlipped)
888
+ copyWorld(canvas, out, ui);
889
+ out.recycle(ui.__nowWorld);
890
+ }
891
+ function copyWorld(canvas, out, ui) {
892
+ if (ui.__worldFlipped || Platform.fullImageShadow)
892
893
  canvas.copyWorldByReset(out, ui.__nowWorld);
893
894
  else
894
895
  canvas.copyWorldToInner(out, ui.__nowWorld, ui.__layout.renderBounds);
895
- out.recycle(ui.__nowWorld);
896
896
  }
897
897
  function drawTextStroke(ui, canvas) {
898
898
  let row, data = ui.__.__textDrawData;
@@ -930,90 +930,56 @@ function drawStrokesStyle(strokes, isText, ui, canvas) {
930
930
  }
931
931
 
932
932
  function stroke(stroke, ui, canvas) {
933
- const options = ui.__;
934
- const { __strokeWidth, strokeAlign, __font } = options;
935
- if (!__strokeWidth)
933
+ const data = ui.__;
934
+ if (!data.__strokeWidth)
936
935
  return;
937
- if (__font) {
936
+ if (data.__font) {
938
937
  strokeText(stroke, ui, canvas);
939
938
  }
940
939
  else {
941
- switch (strokeAlign) {
940
+ switch (data.strokeAlign) {
942
941
  case 'center':
943
- canvas.setStroke(stroke, __strokeWidth, options);
944
- canvas.stroke();
945
- if (options.__useArrow)
946
- strokeArrow(ui, canvas);
942
+ drawCenter(stroke, 1, ui, canvas);
947
943
  break;
948
944
  case 'inside':
949
- canvas.save();
950
- canvas.setStroke(stroke, __strokeWidth * 2, options);
951
- options.windingRule ? canvas.clip(options.windingRule) : canvas.clip();
952
- canvas.stroke();
953
- canvas.restore();
945
+ drawInside(stroke, ui, canvas);
954
946
  break;
955
947
  case 'outside':
956
- const out = canvas.getSameCanvas(true, true);
957
- out.setStroke(stroke, __strokeWidth * 2, options);
958
- ui.__drawRenderPath(out);
959
- out.stroke();
960
- options.windingRule ? out.clip(options.windingRule) : out.clip();
961
- out.clearWorld(ui.__layout.renderBounds);
962
- if (ui.__worldFlipped)
963
- canvas.copyWorldByReset(out, ui.__nowWorld);
964
- else
965
- canvas.copyWorldToInner(out, ui.__nowWorld, ui.__layout.renderBounds);
966
- out.recycle(ui.__nowWorld);
948
+ drawOutside(stroke, ui, canvas);
967
949
  break;
968
950
  }
969
951
  }
970
952
  }
971
953
  function strokes(strokes, ui, canvas) {
972
- const options = ui.__;
973
- const { __strokeWidth, strokeAlign, __font } = options;
974
- if (!__strokeWidth)
975
- return;
976
- if (__font) {
977
- strokeText(strokes, ui, canvas);
954
+ stroke(strokes, ui, canvas);
955
+ }
956
+ function drawCenter(stroke, strokeWidthScale, ui, canvas) {
957
+ const data = ui.__;
958
+ canvas.setStroke(!data.__isStrokes && stroke, data.__strokeWidth * strokeWidthScale, data);
959
+ data.__isStrokes ? drawStrokesStyle(stroke, false, ui, canvas) : canvas.stroke();
960
+ if (data.__useArrow)
961
+ Paint.strokeArrow(stroke, ui, canvas);
962
+ }
963
+ function drawInside(stroke, ui, canvas) {
964
+ canvas.save();
965
+ canvas.clipUI(ui);
966
+ drawCenter(stroke, 2, ui, canvas);
967
+ canvas.restore();
968
+ }
969
+ function drawOutside(stroke, ui, canvas) {
970
+ const data = ui.__;
971
+ if (data.__fillAfterStroke) {
972
+ drawCenter(stroke, 2, ui, canvas);
978
973
  }
979
974
  else {
980
- switch (strokeAlign) {
981
- case 'center':
982
- canvas.setStroke(undefined, __strokeWidth, options);
983
- drawStrokesStyle(strokes, false, ui, canvas);
984
- if (options.__useArrow)
985
- strokeArrow(ui, canvas);
986
- break;
987
- case 'inside':
988
- canvas.save();
989
- canvas.setStroke(undefined, __strokeWidth * 2, options);
990
- options.windingRule ? canvas.clip(options.windingRule) : canvas.clip();
991
- drawStrokesStyle(strokes, false, ui, canvas);
992
- canvas.restore();
993
- break;
994
- case 'outside':
995
- const { renderBounds } = ui.__layout;
996
- const out = canvas.getSameCanvas(true, true);
997
- ui.__drawRenderPath(out);
998
- out.setStroke(undefined, __strokeWidth * 2, options);
999
- drawStrokesStyle(strokes, false, ui, out);
1000
- options.windingRule ? out.clip(options.windingRule) : out.clip();
1001
- out.clearWorld(renderBounds);
1002
- if (ui.__worldFlipped)
1003
- canvas.copyWorldByReset(out, ui.__nowWorld);
1004
- else
1005
- canvas.copyWorldToInner(out, ui.__nowWorld, renderBounds);
1006
- out.recycle(ui.__nowWorld);
1007
- break;
1008
- }
1009
- }
1010
- }
1011
- function strokeArrow(ui, canvas) {
1012
- if (ui.__.dashPattern) {
1013
- canvas.beginPath();
1014
- ui.__drawPathByData(canvas, ui.__.__pathForArrow);
1015
- canvas.dashPattern = null;
1016
- canvas.stroke();
975
+ const { renderBounds } = ui.__layout;
976
+ const out = canvas.getSameCanvas(true, true);
977
+ ui.__drawRenderPath(out);
978
+ drawCenter(stroke, 2, ui, out);
979
+ out.clipUI(data);
980
+ out.clearWorld(renderBounds);
981
+ copyWorld(canvas, out, ui);
982
+ out.recycle(ui.__nowWorld);
1017
983
  }
1018
984
  }
1019
985
 
@@ -1060,41 +1026,66 @@ function shape(ui, current, options) {
1060
1026
  }
1061
1027
 
1062
1028
  let recycleMap;
1029
+ const { stintSet } = DataHelper, { hasTransparent: hasTransparent$1 } = ColorConvert;
1063
1030
  function compute(attrName, ui) {
1064
1031
  const data = ui.__, leafPaints = [];
1065
- let paints = data.__input[attrName], hasOpacityPixel;
1032
+ let paints = data.__input[attrName], isAlphaPixel, isTransparent;
1066
1033
  if (!(paints instanceof Array))
1067
1034
  paints = [paints];
1068
1035
  recycleMap = PaintImage.recycleImage(attrName, data);
1069
1036
  for (let i = 0, len = paints.length, item; i < len; i++) {
1070
- item = getLeafPaint(attrName, paints[i], ui);
1071
- if (item)
1072
- leafPaints.push(item);
1037
+ (item = getLeafPaint(attrName, paints[i], ui)) && leafPaints.push(item);
1073
1038
  }
1074
1039
  data['_' + attrName] = leafPaints.length ? leafPaints : undefined;
1075
- if (leafPaints.length && leafPaints[0].image)
1076
- hasOpacityPixel = leafPaints[0].image.hasOpacityPixel;
1077
- attrName === 'fill' ? data.__pixelFill = hasOpacityPixel : data.__pixelStroke = hasOpacityPixel;
1040
+ if (leafPaints.length) {
1041
+ if (leafPaints.every(item => item.isTransparent)) {
1042
+ if (leafPaints.some(item => item.image))
1043
+ isAlphaPixel = true;
1044
+ isTransparent = true;
1045
+ }
1046
+ }
1047
+ if (attrName === 'fill') {
1048
+ stintSet(data, '__isAlphaPixelFill', isAlphaPixel);
1049
+ stintSet(data, '__isTransparentFill', isTransparent);
1050
+ }
1051
+ else {
1052
+ stintSet(data, '__isAlphaPixelStroke', isAlphaPixel);
1053
+ stintSet(data, '__isTransparentStroke', isTransparent);
1054
+ }
1078
1055
  }
1079
1056
  function getLeafPaint(attrName, paint, ui) {
1080
1057
  if (typeof paint !== 'object' || paint.visible === false || paint.opacity === 0)
1081
1058
  return undefined;
1059
+ let data;
1082
1060
  const { boxBounds } = ui.__layout;
1083
1061
  switch (paint.type) {
1084
- case 'solid':
1085
- let { type, blendMode, color, opacity } = paint;
1086
- return { type, blendMode, style: ColorConvert.string(color, opacity) };
1087
1062
  case 'image':
1088
- return PaintImage.image(ui, attrName, paint, boxBounds, !recycleMap || !recycleMap[paint.url]);
1063
+ data = PaintImage.image(ui, attrName, paint, boxBounds, !recycleMap || !recycleMap[paint.url]);
1064
+ break;
1089
1065
  case 'linear':
1090
- return PaintGradient.linearGradient(paint, boxBounds);
1066
+ data = PaintGradient.linearGradient(paint, boxBounds);
1067
+ break;
1091
1068
  case 'radial':
1092
- return PaintGradient.radialGradient(paint, boxBounds);
1069
+ data = PaintGradient.radialGradient(paint, boxBounds);
1070
+ break;
1093
1071
  case 'angular':
1094
- return PaintGradient.conicGradient(paint, boxBounds);
1072
+ data = PaintGradient.conicGradient(paint, boxBounds);
1073
+ break;
1074
+ case 'solid':
1075
+ const { type, color, opacity } = paint;
1076
+ data = { type, style: ColorConvert.string(color, opacity) };
1077
+ break;
1095
1078
  default:
1096
- return paint.r !== undefined ? { type: 'solid', style: ColorConvert.string(paint) } : undefined;
1079
+ if (paint.r !== undefined)
1080
+ data = { type: 'solid', style: ColorConvert.string(paint) };
1097
1081
  }
1082
+ if (data) {
1083
+ if (typeof data.style === 'string' && hasTransparent$1(data.style))
1084
+ data.isTransparent = true;
1085
+ if (paint.blendMode)
1086
+ data.blendMode = paint.blendMode;
1087
+ }
1088
+ return data;
1098
1089
  }
1099
1090
 
1100
1091
  const PaintModule = {
@@ -1160,12 +1151,10 @@ function repeatMode(data, box, width, height, x, y, scaleX, scaleY, rotation, al
1160
1151
 
1161
1152
  const { get: get$2, translate } = MatrixHelper;
1162
1153
  const tempBox = new Bounds();
1163
- const tempPoint = {};
1164
1154
  const tempScaleData = {};
1155
+ const tempImage = {};
1165
1156
  function createData(leafPaint, image, paint, box) {
1166
- const { blendMode, changeful, sync } = paint;
1167
- if (blendMode)
1168
- leafPaint.blendMode = blendMode;
1157
+ const { changeful, sync } = paint;
1169
1158
  if (changeful)
1170
1159
  leafPaint.changeful = changeful;
1171
1160
  if (sync)
@@ -1173,38 +1162,38 @@ function createData(leafPaint, image, paint, box) {
1173
1162
  leafPaint.data = getPatternData(paint, box, image);
1174
1163
  }
1175
1164
  function getPatternData(paint, box, image) {
1176
- let { width, height } = image;
1177
1165
  if (paint.padding)
1178
1166
  box = tempBox.set(box).shrink(paint.padding);
1179
1167
  if (paint.mode === 'strench')
1180
1168
  paint.mode = 'stretch';
1169
+ let { width, height } = image;
1181
1170
  const { opacity, mode, align, offset, scale, size, rotation, repeat, filters } = paint;
1182
1171
  const sameBox = box.width === width && box.height === height;
1183
1172
  const data = { mode };
1184
1173
  const swapSize = align !== 'center' && (rotation || 0) % 180 === 90;
1185
- const swapWidth = swapSize ? height : width, swapHeight = swapSize ? width : height;
1186
- let x = 0, y = 0, scaleX, scaleY;
1174
+ BoundsHelper.set(tempImage, 0, 0, swapSize ? height : width, swapSize ? width : height);
1175
+ let scaleX, scaleY;
1187
1176
  if (!mode || mode === 'cover' || mode === 'fit') {
1188
1177
  if (!sameBox || rotation) {
1189
- const sw = box.width / swapWidth, sh = box.height / swapHeight;
1190
- scaleX = scaleY = mode === 'fit' ? Math.min(sw, sh) : Math.max(sw, sh);
1191
- x += (box.width - width * scaleX) / 2, y += (box.height - height * scaleY) / 2;
1178
+ scaleX = scaleY = BoundsHelper.getFitScale(box, tempImage, mode !== 'fit');
1179
+ BoundsHelper.put(box, image, align, scaleX, false, tempImage);
1180
+ BoundsHelper.scale(tempImage, scaleX, scaleY, true);
1192
1181
  }
1193
1182
  }
1194
- else if (scale || size) {
1195
- MathHelper.getScaleData(scale, size, image, tempScaleData);
1196
- scaleX = tempScaleData.scaleX;
1197
- scaleY = tempScaleData.scaleY;
1198
- }
1199
- if (align) {
1200
- const imageBounds = { x, y, width: swapWidth, height: swapHeight };
1201
- if (scaleX)
1202
- imageBounds.width *= scaleX, imageBounds.height *= scaleY;
1203
- AlignHelper.toPoint(align, imageBounds, box, tempPoint, true);
1204
- x += tempPoint.x, y += tempPoint.y;
1183
+ else {
1184
+ if (scale || size) {
1185
+ MathHelper.getScaleData(scale, size, image, tempScaleData);
1186
+ scaleX = tempScaleData.scaleX;
1187
+ scaleY = tempScaleData.scaleY;
1188
+ }
1189
+ if (align) {
1190
+ if (scaleX)
1191
+ BoundsHelper.scale(tempImage, scaleX, scaleY, true);
1192
+ AlignHelper.toPoint(align, tempImage, box, tempImage, true, true);
1193
+ }
1205
1194
  }
1206
1195
  if (offset)
1207
- x += offset.x, y += offset.y;
1196
+ PointHelper.move(tempImage, offset);
1208
1197
  switch (mode) {
1209
1198
  case 'stretch':
1210
1199
  if (!sameBox)
@@ -1212,12 +1201,12 @@ function getPatternData(paint, box, image) {
1212
1201
  break;
1213
1202
  case 'normal':
1214
1203
  case 'clip':
1215
- if (x || y || scaleX || rotation)
1216
- clipMode(data, box, x, y, scaleX, scaleY, rotation);
1204
+ if (tempImage.x || tempImage.y || scaleX || rotation)
1205
+ clipMode(data, box, tempImage.x, tempImage.y, scaleX, scaleY, rotation);
1217
1206
  break;
1218
1207
  case 'repeat':
1219
1208
  if (!sameBox || scaleX || rotation)
1220
- repeatMode(data, box, width, height, x, y, scaleX, scaleY, rotation, align);
1209
+ repeatMode(data, box, width, height, tempImage.x, tempImage.y, scaleX, scaleY, rotation, align);
1221
1210
  if (!repeat)
1222
1211
  data.repeat = 'repeat';
1223
1212
  break;
@@ -1225,7 +1214,7 @@ function getPatternData(paint, box, image) {
1225
1214
  case 'cover':
1226
1215
  default:
1227
1216
  if (scaleX)
1228
- fillOrFitMode(data, box, x, y, scaleX, scaleY, rotation);
1217
+ fillOrFitMode(data, box, tempImage.x, tempImage.y, scaleX, scaleY, rotation);
1229
1218
  }
1230
1219
  if (!data.transform) {
1231
1220
  if (box.x || box.y) {
@@ -1258,6 +1247,8 @@ function image(ui, attrName, paint, boxBounds, firstUse) {
1258
1247
  }
1259
1248
  else {
1260
1249
  leafPaint = { type: paint.type, image };
1250
+ if (image.hasAlphaPixel)
1251
+ leafPaint.isTransparent = true;
1261
1252
  cache = image.use > 1 ? { leafPaint, paint, boxBounds: box.set(boxBounds) } : null;
1262
1253
  }
1263
1254
  if (firstUse || image.loading)
@@ -1282,7 +1273,7 @@ function image(ui, attrName, paint, boxBounds, firstUse) {
1282
1273
  ignoreRender(ui, false);
1283
1274
  if (!ui.destroyed) {
1284
1275
  if (checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds)) {
1285
- if (image.hasOpacityPixel)
1276
+ if (image.hasAlphaPixel)
1286
1277
  ui.__layout.hitCanvasChanged = true;
1287
1278
  ui.forceUpdate('surface');
1288
1279
  }
@@ -1294,13 +1285,17 @@ function image(ui, attrName, paint, boxBounds, firstUse) {
1294
1285
  onLoadError(ui, event, error);
1295
1286
  leafPaint.loadId = null;
1296
1287
  });
1297
- if (ui.placeholderColor)
1298
- setTimeout(() => {
1299
- if (!(image.ready || image.isPlacehold)) {
1300
- image.isPlacehold = true;
1301
- ui.forceUpdate('surface');
1302
- }
1303
- }, 100);
1288
+ if (ui.placeholderColor) {
1289
+ if (!ui.placeholderDelay)
1290
+ image.isPlacehold = true;
1291
+ else
1292
+ setTimeout(() => {
1293
+ if (!image.ready) {
1294
+ image.isPlacehold = true;
1295
+ ui.forceUpdate('surface');
1296
+ }
1297
+ }, ui.placeholderDelay);
1298
+ }
1304
1299
  }
1305
1300
  return leafPaint;
1306
1301
  }
@@ -1487,7 +1482,7 @@ function checkImage(ui, canvas, paint, allowDraw) {
1487
1482
  }
1488
1483
  function drawImage(ui, canvas, paint, data) {
1489
1484
  canvas.save();
1490
- ui.windingRule ? canvas.clip(ui.windingRule) : canvas.clip();
1485
+ canvas.clipUI(ui);
1491
1486
  if (paint.blendMode)
1492
1487
  canvas.blendMode = paint.blendMode;
1493
1488
  if (data.opacity)
@@ -1538,32 +1533,33 @@ const PaintImageModule = {
1538
1533
  repeatMode
1539
1534
  };
1540
1535
 
1541
- const { toPoint: toPoint$2 } = AroundHelper;
1536
+ const { toPoint: toPoint$2 } = AroundHelper, { hasTransparent } = ColorConvert;
1542
1537
  const realFrom$2 = {};
1543
1538
  const realTo$2 = {};
1544
1539
  function linearGradient(paint, box) {
1545
- let { from, to, type, blendMode, opacity } = paint;
1540
+ let { from, to, type, opacity } = paint;
1546
1541
  toPoint$2(from || 'top', box, realFrom$2);
1547
1542
  toPoint$2(to || 'bottom', box, realTo$2);
1548
1543
  const style = Platform.canvas.createLinearGradient(realFrom$2.x, realFrom$2.y, realTo$2.x, realTo$2.y);
1549
- applyStops(style, paint.stops, opacity);
1550
1544
  const data = { type, style };
1551
- if (blendMode)
1552
- data.blendMode = blendMode;
1545
+ applyStops(data, style, paint.stops, opacity);
1553
1546
  return data;
1554
1547
  }
1555
- function applyStops(gradient, stops, opacity) {
1548
+ function applyStops(data, gradient, stops, opacity) {
1556
1549
  if (stops) {
1557
- let stop;
1550
+ let stop, color, offset, isTransparent;
1558
1551
  for (let i = 0, len = stops.length; i < len; i++) {
1559
1552
  stop = stops[i];
1560
- if (typeof stop === 'string') {
1561
- gradient.addColorStop(i / (len - 1), ColorConvert.string(stop, opacity));
1562
- }
1563
- else {
1564
- gradient.addColorStop(stop.offset, ColorConvert.string(stop.color, opacity));
1565
- }
1553
+ if (typeof stop === 'string')
1554
+ offset = i / (len - 1), color = ColorConvert.string(stop, opacity);
1555
+ else
1556
+ offset = stop.offset, color = ColorConvert.string(stop.color, opacity);
1557
+ gradient.addColorStop(offset, color);
1558
+ if (!isTransparent && hasTransparent(color))
1559
+ isTransparent = true;
1566
1560
  }
1561
+ if (isTransparent)
1562
+ data.isTransparent = true;
1567
1563
  }
1568
1564
  }
1569
1565
 
@@ -1573,17 +1569,15 @@ const { toPoint: toPoint$1 } = AroundHelper;
1573
1569
  const realFrom$1 = {};
1574
1570
  const realTo$1 = {};
1575
1571
  function radialGradient(paint, box) {
1576
- let { from, to, type, opacity, blendMode, stretch } = paint;
1572
+ let { from, to, type, opacity, stretch } = paint;
1577
1573
  toPoint$1(from || 'center', box, realFrom$1);
1578
1574
  toPoint$1(to || 'bottom', box, realTo$1);
1579
1575
  const style = Platform.canvas.createRadialGradient(realFrom$1.x, realFrom$1.y, 0, realFrom$1.x, realFrom$1.y, getDistance$1(realFrom$1, realTo$1));
1580
- applyStops(style, paint.stops, opacity);
1581
1576
  const data = { type, style };
1577
+ applyStops(data, style, paint.stops, opacity);
1582
1578
  const transform = getTransform(box, realFrom$1, realTo$1, stretch, true);
1583
1579
  if (transform)
1584
1580
  data.transform = transform;
1585
- if (blendMode)
1586
- data.blendMode = blendMode;
1587
1581
  return data;
1588
1582
  }
1589
1583
  function getTransform(box, from, to, stretch, rotate90) {
@@ -1609,17 +1603,15 @@ const { toPoint } = AroundHelper;
1609
1603
  const realFrom = {};
1610
1604
  const realTo = {};
1611
1605
  function conicGradient(paint, box) {
1612
- let { from, to, type, opacity, blendMode, stretch } = paint;
1606
+ let { from, to, type, opacity, stretch } = paint;
1613
1607
  toPoint(from || 'center', box, realFrom);
1614
1608
  toPoint(to || 'bottom', box, realTo);
1615
1609
  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));
1616
- applyStops(style, paint.stops, opacity);
1617
1610
  const data = { type, style };
1611
+ applyStops(data, style, paint.stops, opacity);
1618
1612
  const transform = getTransform(box, realFrom, realTo, stretch || 1, Platform.conicGradientRotate90);
1619
1613
  if (transform)
1620
1614
  data.transform = transform;
1621
- if (blendMode)
1622
- data.blendMode = blendMode;
1623
1615
  return data;
1624
1616
  }
1625
1617
 
@@ -1655,12 +1647,10 @@ function shadow(ui, current, shape) {
1655
1647
  }
1656
1648
  worldCanvas ? other.copyWorld(worldCanvas, nowWorld, nowWorld, 'destination-out') : other.copyWorld(shape.canvas, shapeBounds, bounds, 'destination-out');
1657
1649
  }
1658
- if (ui.__worldFlipped) {
1650
+ if (ui.__worldFlipped)
1659
1651
  current.copyWorldByReset(other, copyBounds, nowWorld, item.blendMode);
1660
- }
1661
- else {
1652
+ else
1662
1653
  current.copyWorldToInner(other, copyBounds, __layout.renderBounds, item.blendMode);
1663
- }
1664
1654
  if (end && index < end)
1665
1655
  other.clearWorld(copyBounds, true);
1666
1656
  });
@@ -1719,12 +1709,10 @@ function innerShadow(ui, current, shape) {
1719
1709
  copyBounds = bounds;
1720
1710
  }
1721
1711
  other.fillWorld(copyBounds, ColorConvert.string(item.color), 'source-in');
1722
- if (ui.__worldFlipped) {
1712
+ if (ui.__worldFlipped)
1723
1713
  current.copyWorldByReset(other, copyBounds, nowWorld, item.blendMode);
1724
- }
1725
- else {
1714
+ else
1726
1715
  current.copyWorldToInner(other, copyBounds, __layout.renderBounds, item.blendMode);
1727
- }
1728
1716
  if (end && index < end)
1729
1717
  other.clearWorld(copyBounds, true);
1730
1718
  });
@@ -1952,6 +1940,8 @@ function createRows(drawData, content, style) {
1952
1940
  lastCharType = null;
1953
1941
  startCharSize = charWidth = charSize = wordWidth = rowWidth = 0;
1954
1942
  word = { data: [] }, row = { words: [] };
1943
+ if (__letterSpacing)
1944
+ content = [...content];
1955
1945
  for (let i = 0, len = content.length; i < len; i++) {
1956
1946
  char = content[i];
1957
1947
  if (char === '\n') {