leafer-ui 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
@@ -342,17 +342,15 @@ class Watcher {
342
342
  this.target.emitEvent(new core.WatchEvent(core.WatchEvent.DATA, { updatedList: this.updatedList }));
343
343
  this.__updatedList = new core.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_(core.PropertyEvent.CHANGE, this.__onAttrChange, this),
354
- target.on_([core.ChildEvent.ADD, core.ChildEvent.REMOVE], this.__onChildEvent, this),
355
- 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
+ ])
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
  core.BranchHelper.updateBounds(target);
556
- }
557
- else {
553
+ else
558
554
  core.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_(core.LayoutEvent.REQUEST, this.layout, this),
585
- target.on_(core.LayoutEvent.AGAIN, this.layoutAgain, this),
586
- 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
+ ])
587
583
  ];
588
584
  }
589
585
  __removeListenEvents() {
@@ -814,12 +810,13 @@ class Renderer {
814
810
  this.target.emitEvent(new core.RenderEvent(type, this.times, bounds, options));
815
811
  }
816
812
  __listenEvents() {
817
- const { target } = this;
818
813
  this.__eventIds = [
819
- target.on_(core.RenderEvent.REQUEST, this.update, this),
820
- target.on_(core.LayoutEvent.END, this.__onLayoutEnd, this),
821
- target.on_(core.RenderEvent.AGAIN, this.renderAgain, this),
822
- 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
+ ])
823
820
  ];
824
821
  }
825
822
  __removeListenEvents() {
@@ -1371,32 +1368,34 @@ function fillPathOrText(ui, canvas) {
1371
1368
  ui.__.__font ? fillText(ui, canvas) : (ui.__.windingRule ? canvas.fill(ui.__.windingRule) : canvas.fill());
1372
1369
  }
1373
1370
 
1371
+ const Paint = {};
1372
+
1374
1373
  function strokeText(stroke, ui, canvas) {
1375
- const { strokeAlign } = ui.__;
1376
- const isStrokes = typeof stroke !== 'string';
1377
- switch (strokeAlign) {
1374
+ switch (ui.__.strokeAlign) {
1378
1375
  case 'center':
1379
- canvas.setStroke(isStrokes ? undefined : stroke, ui.__.strokeWidth, ui.__);
1380
- isStrokes ? drawStrokesStyle(stroke, true, ui, canvas) : drawTextStroke(ui, canvas);
1376
+ drawCenter$1(stroke, 1, ui, canvas);
1381
1377
  break;
1382
1378
  case 'inside':
1383
- drawAlignStroke('inside', stroke, isStrokes, ui, canvas);
1379
+ drawAlign(stroke, 'inside', ui, canvas);
1384
1380
  break;
1385
1381
  case 'outside':
1386
- drawAlignStroke('outside', stroke, isStrokes, ui, canvas);
1382
+ ui.__.__fillAfterStroke ? drawCenter$1(stroke, 2, ui, canvas) : drawAlign(stroke, 'outside', ui, canvas);
1387
1383
  break;
1388
1384
  }
1389
1385
  }
1390
- function drawAlignStroke(align, stroke, isStrokes, ui, canvas) {
1391
- const { __strokeWidth, __font } = ui.__;
1386
+ function drawCenter$1(stroke, strokeWidthScale, ui, canvas) {
1387
+ const data = ui.__;
1388
+ canvas.setStroke(!data.__isStrokes && stroke, data.strokeWidth * strokeWidthScale, data);
1389
+ data.__isStrokes ? drawStrokesStyle(stroke, true, ui, canvas) : drawTextStroke(ui, canvas);
1390
+ }
1391
+ function drawAlign(stroke, align, ui, canvas) {
1392
1392
  const out = canvas.getSameCanvas(true, true);
1393
- out.setStroke(isStrokes ? undefined : stroke, __strokeWidth * 2, ui.__);
1394
- out.font = __font;
1395
- isStrokes ? drawStrokesStyle(stroke, true, ui, out) : drawTextStroke(ui, out);
1393
+ out.font = ui.__.__font;
1394
+ drawCenter$1(stroke, 2, ui, out);
1396
1395
  out.blendMode = align === 'outside' ? 'destination-out' : 'destination-in';
1397
1396
  fillText(ui, out);
1398
1397
  out.blendMode = 'normal';
1399
- if (ui.__worldFlipped)
1398
+ if (ui.__worldFlipped || core$1.Platform.fullImageShadow)
1400
1399
  canvas.copyWorldByReset(out, ui.__nowWorld);
1401
1400
  else
1402
1401
  canvas.copyWorldToInner(out, ui.__nowWorld, ui.__layout.renderBounds);
@@ -1438,90 +1437,60 @@ function drawStrokesStyle(strokes, isText, ui, canvas) {
1438
1437
  }
1439
1438
 
1440
1439
  function stroke(stroke, ui, canvas) {
1441
- const options = ui.__;
1442
- const { __strokeWidth, strokeAlign, __font } = options;
1443
- if (!__strokeWidth)
1440
+ const data = ui.__;
1441
+ if (!data.__strokeWidth)
1444
1442
  return;
1445
- if (__font) {
1443
+ if (data.__font) {
1446
1444
  strokeText(stroke, ui, canvas);
1447
1445
  }
1448
1446
  else {
1449
- switch (strokeAlign) {
1447
+ switch (data.strokeAlign) {
1450
1448
  case 'center':
1451
- canvas.setStroke(stroke, __strokeWidth, options);
1452
- canvas.stroke();
1453
- if (options.__useArrow)
1454
- strokeArrow(ui, canvas);
1449
+ drawCenter(stroke, 1, ui, canvas);
1455
1450
  break;
1456
1451
  case 'inside':
1457
- canvas.save();
1458
- canvas.setStroke(stroke, __strokeWidth * 2, options);
1459
- options.windingRule ? canvas.clip(options.windingRule) : canvas.clip();
1460
- canvas.stroke();
1461
- canvas.restore();
1452
+ drawInside(stroke, ui, canvas);
1462
1453
  break;
1463
1454
  case 'outside':
1464
- const out = canvas.getSameCanvas(true, true);
1465
- out.setStroke(stroke, __strokeWidth * 2, options);
1466
- ui.__drawRenderPath(out);
1467
- out.stroke();
1468
- options.windingRule ? out.clip(options.windingRule) : out.clip();
1469
- out.clearWorld(ui.__layout.renderBounds);
1470
- if (ui.__worldFlipped)
1471
- canvas.copyWorldByReset(out, ui.__nowWorld);
1472
- else
1473
- canvas.copyWorldToInner(out, ui.__nowWorld, ui.__layout.renderBounds);
1474
- out.recycle(ui.__nowWorld);
1455
+ drawOutside(stroke, ui, canvas);
1475
1456
  break;
1476
1457
  }
1477
1458
  }
1478
1459
  }
1479
1460
  function strokes(strokes, ui, canvas) {
1480
- const options = ui.__;
1481
- const { __strokeWidth, strokeAlign, __font } = options;
1482
- if (!__strokeWidth)
1483
- return;
1484
- if (__font) {
1485
- strokeText(strokes, ui, canvas);
1461
+ stroke(strokes, ui, canvas);
1462
+ }
1463
+ function drawCenter(stroke, strokeWidthScale, ui, canvas) {
1464
+ const data = ui.__;
1465
+ canvas.setStroke(!data.__isStrokes && stroke, data.__strokeWidth * strokeWidthScale, data);
1466
+ data.__isStrokes ? drawStrokesStyle(stroke, false, ui, canvas) : canvas.stroke();
1467
+ if (data.__useArrow)
1468
+ Paint.strokeArrow(stroke, ui, canvas);
1469
+ }
1470
+ function drawInside(stroke, ui, canvas) {
1471
+ const data = ui.__;
1472
+ canvas.save();
1473
+ data.windingRule ? canvas.clip(data.windingRule) : canvas.clip();
1474
+ drawCenter(stroke, 2, ui, canvas);
1475
+ canvas.restore();
1476
+ }
1477
+ function drawOutside(stroke, ui, canvas) {
1478
+ const data = ui.__;
1479
+ if (data.__fillAfterStroke) {
1480
+ drawCenter(stroke, 2, ui, canvas);
1486
1481
  }
1487
1482
  else {
1488
- switch (strokeAlign) {
1489
- case 'center':
1490
- canvas.setStroke(undefined, __strokeWidth, options);
1491
- drawStrokesStyle(strokes, false, ui, canvas);
1492
- if (options.__useArrow)
1493
- strokeArrow(ui, canvas);
1494
- break;
1495
- case 'inside':
1496
- canvas.save();
1497
- canvas.setStroke(undefined, __strokeWidth * 2, options);
1498
- options.windingRule ? canvas.clip(options.windingRule) : canvas.clip();
1499
- drawStrokesStyle(strokes, false, ui, canvas);
1500
- canvas.restore();
1501
- break;
1502
- case 'outside':
1503
- const { renderBounds } = ui.__layout;
1504
- const out = canvas.getSameCanvas(true, true);
1505
- ui.__drawRenderPath(out);
1506
- out.setStroke(undefined, __strokeWidth * 2, options);
1507
- drawStrokesStyle(strokes, false, ui, out);
1508
- options.windingRule ? out.clip(options.windingRule) : out.clip();
1509
- out.clearWorld(renderBounds);
1510
- if (ui.__worldFlipped)
1511
- canvas.copyWorldByReset(out, ui.__nowWorld);
1512
- else
1513
- canvas.copyWorldToInner(out, ui.__nowWorld, renderBounds);
1514
- out.recycle(ui.__nowWorld);
1515
- break;
1516
- }
1517
- }
1518
- }
1519
- function strokeArrow(ui, canvas) {
1520
- if (ui.__.dashPattern) {
1521
- canvas.beginPath();
1522
- ui.__drawPathByData(canvas, ui.__.__pathForArrow);
1523
- canvas.dashPattern = null;
1524
- canvas.stroke();
1483
+ const { renderBounds } = ui.__layout;
1484
+ const out = canvas.getSameCanvas(true, true);
1485
+ ui.__drawRenderPath(out);
1486
+ drawCenter(stroke, 2, ui, out);
1487
+ data.windingRule ? out.clip(data.windingRule) : out.clip();
1488
+ out.clearWorld(renderBounds);
1489
+ if (ui.__worldFlipped || core$1.Platform.fullImageShadow)
1490
+ canvas.copyWorldByReset(out, ui.__nowWorld);
1491
+ else
1492
+ canvas.copyWorldToInner(out, ui.__nowWorld, renderBounds);
1493
+ out.recycle(ui.__nowWorld);
1525
1494
  }
1526
1495
  }
1527
1496
 
@@ -1568,9 +1537,10 @@ function shape(ui, current, options) {
1568
1537
  }
1569
1538
 
1570
1539
  let recycleMap;
1540
+ const { stintSet } = core.DataHelper, { hasTransparent: hasTransparent$1 } = draw.ColorConvert;
1571
1541
  function compute(attrName, ui) {
1572
1542
  const data = ui.__, leafPaints = [];
1573
- let paints = data.__input[attrName], hasOpacityPixel;
1543
+ let paints = data.__input[attrName], isAlphaPixel, isTransparent;
1574
1544
  if (!(paints instanceof Array))
1575
1545
  paints = [paints];
1576
1546
  recycleMap = draw.PaintImage.recycleImage(attrName, data);
@@ -1580,29 +1550,55 @@ function compute(attrName, ui) {
1580
1550
  leafPaints.push(item);
1581
1551
  }
1582
1552
  data['_' + attrName] = leafPaints.length ? leafPaints : undefined;
1583
- if (leafPaints.length && leafPaints[0].image)
1584
- hasOpacityPixel = leafPaints[0].image.hasOpacityPixel;
1585
- attrName === 'fill' ? data.__pixelFill = hasOpacityPixel : data.__pixelStroke = hasOpacityPixel;
1553
+ if (leafPaints.length) {
1554
+ if (leafPaints.every(item => item.isTransparent)) {
1555
+ if (leafPaints.some(item => item.image))
1556
+ isAlphaPixel = true;
1557
+ isTransparent = true;
1558
+ }
1559
+ }
1560
+ if (attrName === 'fill') {
1561
+ stintSet(data, '__isAlphaPixelFill', isAlphaPixel);
1562
+ stintSet(data, '__isTransparentFill', isTransparent);
1563
+ }
1564
+ else {
1565
+ stintSet(data, '__isAlphaPixelStroke', isAlphaPixel);
1566
+ stintSet(data, '__isTransparentStroke', isTransparent);
1567
+ }
1586
1568
  }
1587
1569
  function getLeafPaint(attrName, paint, ui) {
1588
1570
  if (typeof paint !== 'object' || paint.visible === false || paint.opacity === 0)
1589
1571
  return undefined;
1572
+ let data;
1590
1573
  const { boxBounds } = ui.__layout;
1591
1574
  switch (paint.type) {
1592
- case 'solid':
1593
- let { type, blendMode, color, opacity } = paint;
1594
- return { type, blendMode, style: draw.ColorConvert.string(color, opacity) };
1595
1575
  case 'image':
1596
- return draw.PaintImage.image(ui, attrName, paint, boxBounds, !recycleMap || !recycleMap[paint.url]);
1576
+ data = draw.PaintImage.image(ui, attrName, paint, boxBounds, !recycleMap || !recycleMap[paint.url]);
1577
+ break;
1597
1578
  case 'linear':
1598
- return draw.PaintGradient.linearGradient(paint, boxBounds);
1579
+ data = draw.PaintGradient.linearGradient(paint, boxBounds);
1580
+ break;
1599
1581
  case 'radial':
1600
- return draw.PaintGradient.radialGradient(paint, boxBounds);
1582
+ data = draw.PaintGradient.radialGradient(paint, boxBounds);
1583
+ break;
1601
1584
  case 'angular':
1602
- return draw.PaintGradient.conicGradient(paint, boxBounds);
1585
+ data = draw.PaintGradient.conicGradient(paint, boxBounds);
1586
+ break;
1587
+ case 'solid':
1588
+ const { type, blendMode, color, opacity } = paint;
1589
+ data = { type, blendMode, style: draw.ColorConvert.string(color, opacity) };
1590
+ break;
1603
1591
  default:
1604
- return paint.r !== undefined ? { type: 'solid', style: draw.ColorConvert.string(paint) } : undefined;
1592
+ if (paint.r !== undefined)
1593
+ data = { type: 'solid', style: draw.ColorConvert.string(paint) };
1605
1594
  }
1595
+ if (data) {
1596
+ if (typeof data.style === 'string' && hasTransparent$1(data.style))
1597
+ data.isTransparent = true;
1598
+ if (paint.blendMode)
1599
+ data.blendMode = paint.blendMode;
1600
+ }
1601
+ return data;
1606
1602
  }
1607
1603
 
1608
1604
  const PaintModule = {
@@ -1668,12 +1664,10 @@ function repeatMode(data, box, width, height, x, y, scaleX, scaleY, rotation, al
1668
1664
 
1669
1665
  const { get: get$2, translate } = core.MatrixHelper;
1670
1666
  const tempBox = new core.Bounds();
1671
- const tempPoint = {};
1672
1667
  const tempScaleData = {};
1668
+ const tempImage = {};
1673
1669
  function createData(leafPaint, image, paint, box) {
1674
- const { blendMode, changeful, sync } = paint;
1675
- if (blendMode)
1676
- leafPaint.blendMode = blendMode;
1670
+ const { changeful, sync } = paint;
1677
1671
  if (changeful)
1678
1672
  leafPaint.changeful = changeful;
1679
1673
  if (sync)
@@ -1681,38 +1675,38 @@ function createData(leafPaint, image, paint, box) {
1681
1675
  leafPaint.data = getPatternData(paint, box, image);
1682
1676
  }
1683
1677
  function getPatternData(paint, box, image) {
1684
- let { width, height } = image;
1685
1678
  if (paint.padding)
1686
1679
  box = tempBox.set(box).shrink(paint.padding);
1687
1680
  if (paint.mode === 'strench')
1688
1681
  paint.mode = 'stretch';
1682
+ let { width, height } = image;
1689
1683
  const { opacity, mode, align, offset, scale, size, rotation, repeat, filters } = paint;
1690
1684
  const sameBox = box.width === width && box.height === height;
1691
1685
  const data = { mode };
1692
1686
  const swapSize = align !== 'center' && (rotation || 0) % 180 === 90;
1693
- const swapWidth = swapSize ? height : width, swapHeight = swapSize ? width : height;
1694
- let x = 0, y = 0, scaleX, scaleY;
1687
+ core.BoundsHelper.set(tempImage, 0, 0, swapSize ? height : width, swapSize ? width : height);
1688
+ let scaleX, scaleY;
1695
1689
  if (!mode || mode === 'cover' || mode === 'fit') {
1696
1690
  if (!sameBox || rotation) {
1697
- const sw = box.width / swapWidth, sh = box.height / swapHeight;
1698
- scaleX = scaleY = mode === 'fit' ? Math.min(sw, sh) : Math.max(sw, sh);
1699
- x += (box.width - width * scaleX) / 2, y += (box.height - height * scaleY) / 2;
1691
+ scaleX = scaleY = core.BoundsHelper.getFitScale(box, tempImage, mode !== 'fit');
1692
+ core.BoundsHelper.put(box, image, align, scaleX, false, tempImage);
1693
+ core.BoundsHelper.scale(tempImage, scaleX, scaleY, true);
1700
1694
  }
1701
1695
  }
1702
- else if (scale || size) {
1703
- core.MathHelper.getScaleData(scale, size, image, tempScaleData);
1704
- scaleX = tempScaleData.scaleX;
1705
- scaleY = tempScaleData.scaleY;
1706
- }
1707
- if (align) {
1708
- const imageBounds = { x, y, width: swapWidth, height: swapHeight };
1709
- if (scaleX)
1710
- imageBounds.width *= scaleX, imageBounds.height *= scaleY;
1711
- core.AlignHelper.toPoint(align, imageBounds, box, tempPoint, true);
1712
- x += tempPoint.x, y += tempPoint.y;
1696
+ else {
1697
+ if (scale || size) {
1698
+ core.MathHelper.getScaleData(scale, size, image, tempScaleData);
1699
+ scaleX = tempScaleData.scaleX;
1700
+ scaleY = tempScaleData.scaleY;
1701
+ }
1702
+ if (align) {
1703
+ if (scaleX)
1704
+ core.BoundsHelper.scale(tempImage, scaleX, scaleY, true);
1705
+ core.AlignHelper.toPoint(align, tempImage, box, tempImage, true, true);
1706
+ }
1713
1707
  }
1714
1708
  if (offset)
1715
- x += offset.x, y += offset.y;
1709
+ core.PointHelper.move(tempImage, offset);
1716
1710
  switch (mode) {
1717
1711
  case 'stretch':
1718
1712
  if (!sameBox)
@@ -1720,12 +1714,12 @@ function getPatternData(paint, box, image) {
1720
1714
  break;
1721
1715
  case 'normal':
1722
1716
  case 'clip':
1723
- if (x || y || scaleX || rotation)
1724
- clipMode(data, box, x, y, scaleX, scaleY, rotation);
1717
+ if (tempImage.x || tempImage.y || scaleX || rotation)
1718
+ clipMode(data, box, tempImage.x, tempImage.y, scaleX, scaleY, rotation);
1725
1719
  break;
1726
1720
  case 'repeat':
1727
1721
  if (!sameBox || scaleX || rotation)
1728
- repeatMode(data, box, width, height, x, y, scaleX, scaleY, rotation, align);
1722
+ repeatMode(data, box, width, height, tempImage.x, tempImage.y, scaleX, scaleY, rotation, align);
1729
1723
  if (!repeat)
1730
1724
  data.repeat = 'repeat';
1731
1725
  break;
@@ -1733,7 +1727,7 @@ function getPatternData(paint, box, image) {
1733
1727
  case 'cover':
1734
1728
  default:
1735
1729
  if (scaleX)
1736
- fillOrFitMode(data, box, x, y, scaleX, scaleY, rotation);
1730
+ fillOrFitMode(data, box, tempImage.x, tempImage.y, scaleX, scaleY, rotation);
1737
1731
  }
1738
1732
  if (!data.transform) {
1739
1733
  if (box.x || box.y) {
@@ -1766,6 +1760,8 @@ function image(ui, attrName, paint, boxBounds, firstUse) {
1766
1760
  }
1767
1761
  else {
1768
1762
  leafPaint = { type: paint.type, image };
1763
+ if (image.hasAlphaPixel)
1764
+ leafPaint.isTransparent = true;
1769
1765
  cache = image.use > 1 ? { leafPaint, paint, boxBounds: box.set(boxBounds) } : null;
1770
1766
  }
1771
1767
  if (firstUse || image.loading)
@@ -1790,7 +1786,7 @@ function image(ui, attrName, paint, boxBounds, firstUse) {
1790
1786
  ignoreRender(ui, false);
1791
1787
  if (!ui.destroyed) {
1792
1788
  if (checkSizeAndCreateData(ui, attrName, paint, image, leafPaint, boxBounds)) {
1793
- if (image.hasOpacityPixel)
1789
+ if (image.hasAlphaPixel)
1794
1790
  ui.__layout.hitCanvasChanged = true;
1795
1791
  ui.forceUpdate('surface');
1796
1792
  }
@@ -1802,13 +1798,17 @@ function image(ui, attrName, paint, boxBounds, firstUse) {
1802
1798
  onLoadError(ui, event, error);
1803
1799
  leafPaint.loadId = null;
1804
1800
  });
1805
- if (ui.placeholderColor)
1806
- setTimeout(() => {
1807
- if (!(image.ready || image.isPlacehold)) {
1808
- image.isPlacehold = true;
1809
- ui.forceUpdate('surface');
1810
- }
1811
- }, 100);
1801
+ if (ui.placeholderColor) {
1802
+ if (!ui.placeholderDelay)
1803
+ image.isPlacehold = true;
1804
+ else
1805
+ setTimeout(() => {
1806
+ if (!image.ready) {
1807
+ image.isPlacehold = true;
1808
+ ui.forceUpdate('surface');
1809
+ }
1810
+ }, ui.placeholderDelay);
1811
+ }
1812
1812
  }
1813
1813
  return leafPaint;
1814
1814
  }
@@ -2046,32 +2046,33 @@ const PaintImageModule = {
2046
2046
  repeatMode
2047
2047
  };
2048
2048
 
2049
- const { toPoint: toPoint$2 } = core.AroundHelper;
2049
+ const { toPoint: toPoint$2 } = core.AroundHelper, { hasTransparent } = draw.ColorConvert;
2050
2050
  const realFrom$2 = {};
2051
2051
  const realTo$2 = {};
2052
2052
  function linearGradient(paint, box) {
2053
- let { from, to, type, blendMode, opacity } = paint;
2053
+ let { from, to, type, opacity } = paint;
2054
2054
  toPoint$2(from || 'top', box, realFrom$2);
2055
2055
  toPoint$2(to || 'bottom', box, realTo$2);
2056
2056
  const style = core.Platform.canvas.createLinearGradient(realFrom$2.x, realFrom$2.y, realTo$2.x, realTo$2.y);
2057
- applyStops(style, paint.stops, opacity);
2058
2057
  const data = { type, style };
2059
- if (blendMode)
2060
- data.blendMode = blendMode;
2058
+ applyStops(data, style, paint.stops, opacity);
2061
2059
  return data;
2062
2060
  }
2063
- function applyStops(gradient, stops, opacity) {
2061
+ function applyStops(data, gradient, stops, opacity) {
2064
2062
  if (stops) {
2065
- let stop;
2063
+ let stop, color, offset, isTransparent;
2066
2064
  for (let i = 0, len = stops.length; i < len; i++) {
2067
2065
  stop = stops[i];
2068
- if (typeof stop === 'string') {
2069
- gradient.addColorStop(i / (len - 1), draw.ColorConvert.string(stop, opacity));
2070
- }
2071
- else {
2072
- gradient.addColorStop(stop.offset, draw.ColorConvert.string(stop.color, opacity));
2073
- }
2066
+ if (typeof stop === 'string')
2067
+ offset = i / (len - 1), color = draw.ColorConvert.string(stop, opacity);
2068
+ else
2069
+ offset = stop.offset, color = draw.ColorConvert.string(stop.color, opacity);
2070
+ gradient.addColorStop(offset, color);
2071
+ if (!isTransparent && hasTransparent(color))
2072
+ isTransparent = true;
2074
2073
  }
2074
+ if (isTransparent)
2075
+ data.isTransparent = true;
2075
2076
  }
2076
2077
  }
2077
2078
 
@@ -2081,17 +2082,15 @@ const { toPoint: toPoint$1 } = core.AroundHelper;
2081
2082
  const realFrom$1 = {};
2082
2083
  const realTo$1 = {};
2083
2084
  function radialGradient(paint, box) {
2084
- let { from, to, type, opacity, blendMode, stretch } = paint;
2085
+ let { from, to, type, opacity, stretch } = paint;
2085
2086
  toPoint$1(from || 'center', box, realFrom$1);
2086
2087
  toPoint$1(to || 'bottom', box, realTo$1);
2087
2088
  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));
2088
- applyStops(style, paint.stops, opacity);
2089
2089
  const data = { type, style };
2090
+ applyStops(data, style, paint.stops, opacity);
2090
2091
  const transform = getTransform(box, realFrom$1, realTo$1, stretch, true);
2091
2092
  if (transform)
2092
2093
  data.transform = transform;
2093
- if (blendMode)
2094
- data.blendMode = blendMode;
2095
2094
  return data;
2096
2095
  }
2097
2096
  function getTransform(box, from, to, stretch, rotate90) {
@@ -2117,17 +2116,15 @@ const { toPoint } = core.AroundHelper;
2117
2116
  const realFrom = {};
2118
2117
  const realTo = {};
2119
2118
  function conicGradient(paint, box) {
2120
- let { from, to, type, opacity, blendMode, stretch } = paint;
2119
+ let { from, to, type, opacity, stretch } = paint;
2121
2120
  toPoint(from || 'center', box, realFrom);
2122
2121
  toPoint(to || 'bottom', box, realTo);
2123
2122
  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));
2124
- applyStops(style, paint.stops, opacity);
2125
2123
  const data = { type, style };
2124
+ applyStops(data, style, paint.stops, opacity);
2126
2125
  const transform = getTransform(box, realFrom, realTo, stretch || 1, core.Platform.conicGradientRotate90);
2127
2126
  if (transform)
2128
2127
  data.transform = transform;
2129
- if (blendMode)
2130
- data.blendMode = blendMode;
2131
2128
  return data;
2132
2129
  }
2133
2130
 
@@ -2460,6 +2457,8 @@ function createRows(drawData, content, style) {
2460
2457
  lastCharType = null;
2461
2458
  startCharSize = charWidth = charSize = wordWidth = rowWidth = 0;
2462
2459
  word = { data: [] }, row = { words: [] };
2460
+ if (__letterSpacing)
2461
+ content = [...content];
2463
2462
  for (let i = 0, len = content.length; i < len; i++) {
2464
2463
  char = content[i];
2465
2464
  if (char === '\n') {