leafer-ui 1.9.8 → 1.9.10

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
@@ -876,7 +876,7 @@ class Picker {
876
876
  children: [ target ]
877
877
  } : target);
878
878
  const {list: list} = this.findList;
879
- const leaf = this.getBestMatchLeaf(list, options.bottomList, ignoreHittable);
879
+ const leaf = this.getBestMatchLeaf(list, options.bottomList, ignoreHittable, !!options.findList);
880
880
  const path = ignoreHittable ? this.getPath(leaf) : this.getHitablePath(leaf);
881
881
  this.clear();
882
882
  return through ? {
@@ -891,7 +891,7 @@ class Picker {
891
891
  hitPoint(hitPoint, hitRadius, options) {
892
892
  return !!this.getByPoint(hitPoint, hitRadius, options).target;
893
893
  }
894
- getBestMatchLeaf(list, bottomList, ignoreHittable) {
894
+ getBestMatchLeaf(list, bottomList, ignoreHittable, allowNull) {
895
895
  const findList = this.findList = new core.LeafList;
896
896
  if (list.length) {
897
897
  let find;
@@ -922,15 +922,27 @@ class Picker {
922
922
  if (findList.length) return findList.list[0];
923
923
  }
924
924
  }
925
+ if (allowNull) return null;
925
926
  return ignoreHittable ? list[0] : list.find(item => core.LeafHelper.worldHittable(item));
926
927
  }
927
928
  getPath(leaf) {
928
- const path = new core.LeafList;
929
+ const path = new core.LeafList, syncList = [], {target: target} = this;
929
930
  while (leaf) {
931
+ if (leaf.syncEventer) syncList.push(leaf.syncEventer);
930
932
  path.add(leaf);
931
933
  leaf = leaf.parent;
934
+ if (leaf === target) break;
935
+ }
936
+ if (syncList.length) {
937
+ syncList.forEach(item => {
938
+ while (item) {
939
+ if (item.__.hittable) path.add(item);
940
+ item = item.parent;
941
+ if (item === target) break;
942
+ }
943
+ });
932
944
  }
933
- if (this.target) path.add(this.target);
945
+ if (target) path.add(target);
934
946
  return path;
935
947
  }
936
948
  getHitablePath(leaf) {
@@ -1734,7 +1746,14 @@ const PaintModule = {
1734
1746
 
1735
1747
  let origin = {}, tempMatrix$1 = core.getMatrixData();
1736
1748
 
1737
- const {get: get$3, rotateOfOuter: rotateOfOuter$1, translate: translate$1, scaleOfOuter: scaleOfOuter$1, multiplyParent: multiplyParent, scale: scaleHelper, rotate: rotate, skew: skewHelper} = core.MatrixHelper;
1749
+ const {get: get$3, set: set, rotateOfOuter: rotateOfOuter$1, translate: translate$1, scaleOfOuter: scaleOfOuter$1, multiplyParent: multiplyParent, scale: scaleHelper, rotate: rotate, skew: skewHelper} = core.MatrixHelper;
1750
+
1751
+ function stretchMode(data, box, scaleX, scaleY) {
1752
+ const transform = get$3();
1753
+ translate$1(transform, box.x, box.y);
1754
+ if (scaleX) scaleHelper(transform, scaleX, scaleY);
1755
+ data.transform = transform;
1756
+ }
1738
1757
 
1739
1758
  function fillOrFitMode(data, box, x, y, scaleX, scaleY, rotation) {
1740
1759
  const transform = get$3();
@@ -1751,8 +1770,11 @@ function clipMode(data, box, x, y, scaleX, scaleY, rotation, skew, clipScaleX, c
1751
1770
  const transform = get$3();
1752
1771
  layout(transform, box, x, y, scaleX, scaleY, rotation, skew);
1753
1772
  if (clipScaleX) {
1754
- tempMatrix$1.a = clipScaleX, tempMatrix$1.d = clipScaleY;
1755
- multiplyParent(transform, tempMatrix$1);
1773
+ if (rotation || skew) {
1774
+ set(tempMatrix$1);
1775
+ scaleOfOuter$1(tempMatrix$1, box, clipScaleX, clipScaleY);
1776
+ multiplyParent(transform, tempMatrix$1);
1777
+ } else scaleOfOuter$1(transform, box, clipScaleX, clipScaleY);
1756
1778
  }
1757
1779
  data.transform = transform;
1758
1780
  }
@@ -1848,7 +1870,10 @@ function getPatternData(paint, box, image) {
1848
1870
  if (offset) core.PointHelper.move(tempImage, offset);
1849
1871
  switch (mode) {
1850
1872
  case "stretch":
1851
- if (!sameBox) width = box.width, height = box.height;
1873
+ if (!sameBox) {
1874
+ scaleX = box.width / width, scaleY = box.height / height;
1875
+ stretchMode(data, box, scaleX, scaleY);
1876
+ }
1852
1877
  break;
1853
1878
 
1854
1879
  case "normal":
@@ -1857,7 +1882,7 @@ function getPatternData(paint, box, image) {
1857
1882
  let clipScaleX, clipScaleY;
1858
1883
  if (clipSize) clipScaleX = box.width / clipSize.width, clipScaleY = box.height / clipSize.height;
1859
1884
  clipMode(data, box, tempImage.x, tempImage.y, scaleX, scaleY, rotation, skew, clipScaleX, clipScaleY);
1860
- if (clipScaleX) scaleX = scaleX ? scaleX * clipScaleX : scaleX, scaleY = scaleY ? scaleY * clipScaleY : clipScaleY;
1885
+ if (clipScaleX) scaleX = scaleX ? scaleX * clipScaleX : clipScaleX, scaleY = scaleY ? scaleY * clipScaleY : clipScaleY;
1861
1886
  }
1862
1887
  break;
1863
1888
 
@@ -1874,17 +1899,14 @@ function getPatternData(paint, box, image) {
1874
1899
  if (scaleX) fillOrFitMode(data, box, tempImage.x, tempImage.y, scaleX, scaleY, rotation);
1875
1900
  }
1876
1901
  if (!data.transform) {
1877
- if (box.x || box.y) {
1878
- data.transform = get$2();
1879
- translate(data.transform, box.x, box.y);
1880
- }
1902
+ if (box.x || box.y) translate(data.transform = get$2(), box.x, box.y);
1881
1903
  }
1882
- if (scaleX && mode !== "stretch") {
1904
+ data.width = width;
1905
+ data.height = height;
1906
+ if (scaleX) {
1883
1907
  data.scaleX = scaleX;
1884
1908
  data.scaleY = scaleY;
1885
1909
  }
1886
- data.width = width;
1887
- data.height = height;
1888
1910
  if (opacity) data.opacity = opacity;
1889
1911
  if (filters) data.filters = filters;
1890
1912
  if (repeat) data.repeat = core.isString(repeat) ? repeat === "x" ? "repeat-x" : "repeat-y" : "repeat";
@@ -2016,7 +2038,7 @@ function ignoreRender(ui, value) {
2016
2038
 
2017
2039
  const {get: get$1, scale: scale, copy: copy$1} = core.MatrixHelper;
2018
2040
 
2019
- const {floor: floor, ceil: ceil, max: max$1, abs: abs} = Math;
2041
+ const {floor: floor, ceil: ceil, max: max$1, abs: abs$1} = Math;
2020
2042
 
2021
2043
  function createPattern(ui, paint, pixelRatio) {
2022
2044
  let {scaleX: scaleX, scaleY: scaleY} = ui.getRenderScaleData(true, paint.scaleFixed);
@@ -2027,8 +2049,8 @@ function createPattern(ui, paint, pixelRatio) {
2027
2049
  scaleX *= pixelRatio;
2028
2050
  scaleY *= pixelRatio;
2029
2051
  if (sx) {
2030
- sx = abs(sx);
2031
- sy = abs(sy);
2052
+ sx = abs$1(sx);
2053
+ sy = abs$1(sy);
2032
2054
  imageMatrix = get$1();
2033
2055
  copy$1(imageMatrix, transform);
2034
2056
  scale(imageMatrix, 1 / sx, 1 / sy);
@@ -2203,6 +2225,7 @@ const PaintImageModule = {
2203
2225
  recycleImage: recycleImage,
2204
2226
  createData: createData,
2205
2227
  getPatternData: getPatternData,
2228
+ stretchMode: stretchMode,
2206
2229
  fillOrFitMode: fillOrFitMode,
2207
2230
  clipMode: clipMode,
2208
2231
  repeatMode: repeatMode
@@ -2313,7 +2336,7 @@ const PaintGradientModule = {
2313
2336
  getTransform: getTransform
2314
2337
  };
2315
2338
 
2316
- const {copy: copy, move: move, toOffsetOutBounds: toOffsetOutBounds$1} = core.BoundsHelper, {max: max} = Math;
2339
+ const {copy: copy, move: move, toOffsetOutBounds: toOffsetOutBounds$1} = core.BoundsHelper, {max: max, abs: abs} = Math;
2317
2340
 
2318
2341
  const tempBounds = {}, tempMatrix = new core.Matrix;
2319
2342
 
@@ -2333,8 +2356,8 @@ function shadow(ui, current, shape) {
2333
2356
  const sx = Math.abs(nowWorld.scaleX);
2334
2357
  if (sx > 1) otherScale = 1 / sx;
2335
2358
  }
2336
- other.setWorldShadow(offsetOutBounds$1.offsetX + item.x * scaleX * otherScale, offsetOutBounds$1.offsetY + item.y * scaleY * otherScale, item.blur * scaleX * otherScale, draw.ColorConvert.string(item.color));
2337
- transform = getShadowTransform(ui, other, shape, item, offsetOutBounds$1, otherScale);
2359
+ other.setWorldShadow(offsetOutBounds$1.offsetX + (item.x || 0) * scaleX * otherScale, offsetOutBounds$1.offsetY + (item.y || 0) * scaleY * otherScale, (item.blur || 0) * scaleX * otherScale, draw.ColorConvert.string(item.color));
2360
+ transform = draw.Effect.getShadowTransform(ui, other, shape, item, offsetOutBounds$1, otherScale);
2338
2361
  if (transform) other.setTransform(transform);
2339
2362
  drawWorldShadow(other, offsetOutBounds$1, shape);
2340
2363
  if (transform) other.resetTransform();
@@ -2357,7 +2380,7 @@ function shadow(ui, current, shape) {
2357
2380
  function getShadowRenderSpread(_ui, shadow) {
2358
2381
  let top = 0, right = 0, bottom = 0, left = 0, x, y, spread, blur;
2359
2382
  shadow.forEach(item => {
2360
- x = item.x || 0, y = item.y || 0, spread = item.spread || 0, blur = (item.blur || 0) * 1.5;
2383
+ x = item.x || 0, y = item.y || 0, blur = (item.blur || 0) * 1.5, spread = abs(item.spread || 0);
2361
2384
  top = max(top, spread + blur - y);
2362
2385
  right = max(right, spread + blur + x);
2363
2386
  bottom = max(bottom, spread + blur + y);
@@ -2410,8 +2433,8 @@ function innerShadow(ui, current, shape) {
2410
2433
  if (sx > 1) otherScale = 1 / sx;
2411
2434
  }
2412
2435
  other.save();
2413
- other.setWorldShadow(offsetOutBounds.offsetX + item.x * scaleX * otherScale, offsetOutBounds.offsetY + item.y * scaleY * otherScale, item.blur * scaleX * otherScale);
2414
- transform = getShadowTransform(ui, other, shape, item, offsetOutBounds, otherScale, true);
2436
+ other.setWorldShadow(offsetOutBounds.offsetX + (item.x || 0) * scaleX * otherScale, offsetOutBounds.offsetY + (item.y || 0) * scaleY * otherScale, (item.blur || 0) * scaleX * otherScale);
2437
+ transform = draw.Effect.getShadowTransform(ui, other, shape, item, offsetOutBounds, otherScale, true);
2415
2438
  if (transform) other.setTransform(transform);
2416
2439
  drawWorldShadow(other, offsetOutBounds, shape);
2417
2440
  other.restore();
@@ -3047,6 +3070,7 @@ const TextConvertModule = {
3047
3070
  };
3048
3071
 
3049
3072
  function string(color, opacity) {
3073
+ if (!color) return "#000";
3050
3074
  const doOpacity = core.isNumber(opacity) && opacity < 1;
3051
3075
  if (core.isString(color)) {
3052
3076
  if (doOpacity && draw.ColorConvert.object) color = draw.ColorConvert.object(color); else return color;
package/dist/web.esm.js CHANGED
@@ -8,7 +8,7 @@ import { InteractionHelper, InteractionBase, Cursor, HitCanvasManager } from "@l
8
8
 
9
9
  export * from "@leafer-ui/core";
10
10
 
11
- import { PaintImage, Paint, ColorConvert, PaintGradient, Export, Group, TextConvert, Effect } from "@leafer-ui/draw";
11
+ import { PaintImage, Paint, ColorConvert, PaintGradient, Export, Effect, Group, TextConvert } from "@leafer-ui/draw";
12
12
 
13
13
  const debug$2 = Debug.get("LeaferCanvas");
14
14
 
@@ -880,7 +880,7 @@ class Picker {
880
880
  children: [ target ]
881
881
  } : target);
882
882
  const {list: list} = this.findList;
883
- const leaf = this.getBestMatchLeaf(list, options.bottomList, ignoreHittable);
883
+ const leaf = this.getBestMatchLeaf(list, options.bottomList, ignoreHittable, !!options.findList);
884
884
  const path = ignoreHittable ? this.getPath(leaf) : this.getHitablePath(leaf);
885
885
  this.clear();
886
886
  return through ? {
@@ -895,7 +895,7 @@ class Picker {
895
895
  hitPoint(hitPoint, hitRadius, options) {
896
896
  return !!this.getByPoint(hitPoint, hitRadius, options).target;
897
897
  }
898
- getBestMatchLeaf(list, bottomList, ignoreHittable) {
898
+ getBestMatchLeaf(list, bottomList, ignoreHittable, allowNull) {
899
899
  const findList = this.findList = new LeafList;
900
900
  if (list.length) {
901
901
  let find;
@@ -926,15 +926,27 @@ class Picker {
926
926
  if (findList.length) return findList.list[0];
927
927
  }
928
928
  }
929
+ if (allowNull) return null;
929
930
  return ignoreHittable ? list[0] : list.find(item => LeafHelper.worldHittable(item));
930
931
  }
931
932
  getPath(leaf) {
932
- const path = new LeafList;
933
+ const path = new LeafList, syncList = [], {target: target} = this;
933
934
  while (leaf) {
935
+ if (leaf.syncEventer) syncList.push(leaf.syncEventer);
934
936
  path.add(leaf);
935
937
  leaf = leaf.parent;
938
+ if (leaf === target) break;
939
+ }
940
+ if (syncList.length) {
941
+ syncList.forEach(item => {
942
+ while (item) {
943
+ if (item.__.hittable) path.add(item);
944
+ item = item.parent;
945
+ if (item === target) break;
946
+ }
947
+ });
936
948
  }
937
- if (this.target) path.add(this.target);
949
+ if (target) path.add(target);
938
950
  return path;
939
951
  }
940
952
  getHitablePath(leaf) {
@@ -1738,7 +1750,14 @@ const PaintModule = {
1738
1750
 
1739
1751
  let origin = {}, tempMatrix$1 = getMatrixData();
1740
1752
 
1741
- const {get: get$3, rotateOfOuter: rotateOfOuter$1, translate: translate$1, scaleOfOuter: scaleOfOuter$1, multiplyParent: multiplyParent, scale: scaleHelper, rotate: rotate, skew: skewHelper} = MatrixHelper;
1753
+ const {get: get$3, set: set, rotateOfOuter: rotateOfOuter$1, translate: translate$1, scaleOfOuter: scaleOfOuter$1, multiplyParent: multiplyParent, scale: scaleHelper, rotate: rotate, skew: skewHelper} = MatrixHelper;
1754
+
1755
+ function stretchMode(data, box, scaleX, scaleY) {
1756
+ const transform = get$3();
1757
+ translate$1(transform, box.x, box.y);
1758
+ if (scaleX) scaleHelper(transform, scaleX, scaleY);
1759
+ data.transform = transform;
1760
+ }
1742
1761
 
1743
1762
  function fillOrFitMode(data, box, x, y, scaleX, scaleY, rotation) {
1744
1763
  const transform = get$3();
@@ -1755,8 +1774,11 @@ function clipMode(data, box, x, y, scaleX, scaleY, rotation, skew, clipScaleX, c
1755
1774
  const transform = get$3();
1756
1775
  layout(transform, box, x, y, scaleX, scaleY, rotation, skew);
1757
1776
  if (clipScaleX) {
1758
- tempMatrix$1.a = clipScaleX, tempMatrix$1.d = clipScaleY;
1759
- multiplyParent(transform, tempMatrix$1);
1777
+ if (rotation || skew) {
1778
+ set(tempMatrix$1);
1779
+ scaleOfOuter$1(tempMatrix$1, box, clipScaleX, clipScaleY);
1780
+ multiplyParent(transform, tempMatrix$1);
1781
+ } else scaleOfOuter$1(transform, box, clipScaleX, clipScaleY);
1760
1782
  }
1761
1783
  data.transform = transform;
1762
1784
  }
@@ -1852,7 +1874,10 @@ function getPatternData(paint, box, image) {
1852
1874
  if (offset) PointHelper.move(tempImage, offset);
1853
1875
  switch (mode) {
1854
1876
  case "stretch":
1855
- if (!sameBox) width = box.width, height = box.height;
1877
+ if (!sameBox) {
1878
+ scaleX = box.width / width, scaleY = box.height / height;
1879
+ stretchMode(data, box, scaleX, scaleY);
1880
+ }
1856
1881
  break;
1857
1882
 
1858
1883
  case "normal":
@@ -1861,7 +1886,7 @@ function getPatternData(paint, box, image) {
1861
1886
  let clipScaleX, clipScaleY;
1862
1887
  if (clipSize) clipScaleX = box.width / clipSize.width, clipScaleY = box.height / clipSize.height;
1863
1888
  clipMode(data, box, tempImage.x, tempImage.y, scaleX, scaleY, rotation, skew, clipScaleX, clipScaleY);
1864
- if (clipScaleX) scaleX = scaleX ? scaleX * clipScaleX : scaleX, scaleY = scaleY ? scaleY * clipScaleY : clipScaleY;
1889
+ if (clipScaleX) scaleX = scaleX ? scaleX * clipScaleX : clipScaleX, scaleY = scaleY ? scaleY * clipScaleY : clipScaleY;
1865
1890
  }
1866
1891
  break;
1867
1892
 
@@ -1878,17 +1903,14 @@ function getPatternData(paint, box, image) {
1878
1903
  if (scaleX) fillOrFitMode(data, box, tempImage.x, tempImage.y, scaleX, scaleY, rotation);
1879
1904
  }
1880
1905
  if (!data.transform) {
1881
- if (box.x || box.y) {
1882
- data.transform = get$2();
1883
- translate(data.transform, box.x, box.y);
1884
- }
1906
+ if (box.x || box.y) translate(data.transform = get$2(), box.x, box.y);
1885
1907
  }
1886
- if (scaleX && mode !== "stretch") {
1908
+ data.width = width;
1909
+ data.height = height;
1910
+ if (scaleX) {
1887
1911
  data.scaleX = scaleX;
1888
1912
  data.scaleY = scaleY;
1889
1913
  }
1890
- data.width = width;
1891
- data.height = height;
1892
1914
  if (opacity) data.opacity = opacity;
1893
1915
  if (filters) data.filters = filters;
1894
1916
  if (repeat) data.repeat = isString(repeat) ? repeat === "x" ? "repeat-x" : "repeat-y" : "repeat";
@@ -2020,7 +2042,7 @@ function ignoreRender(ui, value) {
2020
2042
 
2021
2043
  const {get: get$1, scale: scale, copy: copy$1} = MatrixHelper;
2022
2044
 
2023
- const {floor: floor, ceil: ceil, max: max$1, abs: abs} = Math;
2045
+ const {floor: floor, ceil: ceil, max: max$1, abs: abs$1} = Math;
2024
2046
 
2025
2047
  function createPattern(ui, paint, pixelRatio) {
2026
2048
  let {scaleX: scaleX, scaleY: scaleY} = ui.getRenderScaleData(true, paint.scaleFixed);
@@ -2031,8 +2053,8 @@ function createPattern(ui, paint, pixelRatio) {
2031
2053
  scaleX *= pixelRatio;
2032
2054
  scaleY *= pixelRatio;
2033
2055
  if (sx) {
2034
- sx = abs(sx);
2035
- sy = abs(sy);
2056
+ sx = abs$1(sx);
2057
+ sy = abs$1(sy);
2036
2058
  imageMatrix = get$1();
2037
2059
  copy$1(imageMatrix, transform);
2038
2060
  scale(imageMatrix, 1 / sx, 1 / sy);
@@ -2207,6 +2229,7 @@ const PaintImageModule = {
2207
2229
  recycleImage: recycleImage,
2208
2230
  createData: createData,
2209
2231
  getPatternData: getPatternData,
2232
+ stretchMode: stretchMode,
2210
2233
  fillOrFitMode: fillOrFitMode,
2211
2234
  clipMode: clipMode,
2212
2235
  repeatMode: repeatMode
@@ -2317,7 +2340,7 @@ const PaintGradientModule = {
2317
2340
  getTransform: getTransform
2318
2341
  };
2319
2342
 
2320
- const {copy: copy, move: move, toOffsetOutBounds: toOffsetOutBounds$1} = BoundsHelper, {max: max} = Math;
2343
+ const {copy: copy, move: move, toOffsetOutBounds: toOffsetOutBounds$1} = BoundsHelper, {max: max, abs: abs} = Math;
2321
2344
 
2322
2345
  const tempBounds = {}, tempMatrix = new Matrix;
2323
2346
 
@@ -2337,8 +2360,8 @@ function shadow(ui, current, shape) {
2337
2360
  const sx = Math.abs(nowWorld.scaleX);
2338
2361
  if (sx > 1) otherScale = 1 / sx;
2339
2362
  }
2340
- other.setWorldShadow(offsetOutBounds$1.offsetX + item.x * scaleX * otherScale, offsetOutBounds$1.offsetY + item.y * scaleY * otherScale, item.blur * scaleX * otherScale, ColorConvert.string(item.color));
2341
- transform = getShadowTransform(ui, other, shape, item, offsetOutBounds$1, otherScale);
2363
+ other.setWorldShadow(offsetOutBounds$1.offsetX + (item.x || 0) * scaleX * otherScale, offsetOutBounds$1.offsetY + (item.y || 0) * scaleY * otherScale, (item.blur || 0) * scaleX * otherScale, ColorConvert.string(item.color));
2364
+ transform = Effect.getShadowTransform(ui, other, shape, item, offsetOutBounds$1, otherScale);
2342
2365
  if (transform) other.setTransform(transform);
2343
2366
  drawWorldShadow(other, offsetOutBounds$1, shape);
2344
2367
  if (transform) other.resetTransform();
@@ -2361,7 +2384,7 @@ function shadow(ui, current, shape) {
2361
2384
  function getShadowRenderSpread(_ui, shadow) {
2362
2385
  let top = 0, right = 0, bottom = 0, left = 0, x, y, spread, blur;
2363
2386
  shadow.forEach(item => {
2364
- x = item.x || 0, y = item.y || 0, spread = item.spread || 0, blur = (item.blur || 0) * 1.5;
2387
+ x = item.x || 0, y = item.y || 0, blur = (item.blur || 0) * 1.5, spread = abs(item.spread || 0);
2365
2388
  top = max(top, spread + blur - y);
2366
2389
  right = max(right, spread + blur + x);
2367
2390
  bottom = max(bottom, spread + blur + y);
@@ -2414,8 +2437,8 @@ function innerShadow(ui, current, shape) {
2414
2437
  if (sx > 1) otherScale = 1 / sx;
2415
2438
  }
2416
2439
  other.save();
2417
- other.setWorldShadow(offsetOutBounds.offsetX + item.x * scaleX * otherScale, offsetOutBounds.offsetY + item.y * scaleY * otherScale, item.blur * scaleX * otherScale);
2418
- transform = getShadowTransform(ui, other, shape, item, offsetOutBounds, otherScale, true);
2440
+ other.setWorldShadow(offsetOutBounds.offsetX + (item.x || 0) * scaleX * otherScale, offsetOutBounds.offsetY + (item.y || 0) * scaleY * otherScale, (item.blur || 0) * scaleX * otherScale);
2441
+ transform = Effect.getShadowTransform(ui, other, shape, item, offsetOutBounds, otherScale, true);
2419
2442
  if (transform) other.setTransform(transform);
2420
2443
  drawWorldShadow(other, offsetOutBounds, shape);
2421
2444
  other.restore();
@@ -3051,6 +3074,7 @@ const TextConvertModule = {
3051
3074
  };
3052
3075
 
3053
3076
  function string(color, opacity) {
3077
+ if (!color) return "#000";
3054
3078
  const doOpacity = isNumber(opacity) && opacity < 1;
3055
3079
  if (isString(color)) {
3056
3080
  if (doOpacity && ColorConvert.object) color = ColorConvert.object(color); else return color;