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