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