@codehz/draw-call 0.5.2 → 0.6.0

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.
@@ -7,7 +7,6 @@ function createRawCanvas(width, height) {
7
7
  canvas.height = height;
8
8
  return canvas;
9
9
  }
10
-
11
10
  //#endregion
12
11
  //#region src/types/base.ts
13
12
  function linearGradient(angle, ...stops) {
@@ -78,7 +77,6 @@ function normalizeBorderRadius(value) {
78
77
  ];
79
78
  return value;
80
79
  }
81
-
82
80
  //#endregion
83
81
  //#region src/layout/components/box.ts
84
82
  /**
@@ -191,7 +189,6 @@ function measureBoxSize(element, ctx, availableWidth, measureChild) {
191
189
  height: typeof element.height === "number" ? element.height : intrinsicHeight
192
190
  };
193
191
  }
194
-
195
192
  //#endregion
196
193
  //#region src/layout/components/customDraw.ts
197
194
  /**
@@ -208,7 +205,6 @@ function measureCustomDrawSize(element, ctx, availableWidth, measureChild) {
208
205
  height: 0
209
206
  };
210
207
  }
211
-
212
208
  //#endregion
213
209
  //#region src/layout/components/image.ts
214
210
  /**
@@ -234,7 +230,6 @@ function measureImageSize(element, _ctx, _availableWidth) {
234
230
  height: 0
235
231
  };
236
232
  }
237
-
238
233
  //#endregion
239
234
  //#region src/layout/components/richtext.ts
240
235
  /**
@@ -402,7 +397,6 @@ function wrapRichText(ctx, spans, maxWidth, lineHeightScale = 1.2, elementStyle
402
397
  }];
403
398
  return lines;
404
399
  }
405
-
406
400
  //#endregion
407
401
  //#region src/layout/components/stack.ts
408
402
  /**
@@ -439,7 +433,6 @@ function measureStackSize(element, ctx, availableWidth, measureChild) {
439
433
  height: typeof element.height === "number" ? element.height : intrinsicHeight
440
434
  };
441
435
  }
442
-
443
436
  //#endregion
444
437
  //#region src/layout/components/svg.ts
445
438
  /**
@@ -473,7 +466,6 @@ function measureSvgSize(element, _ctx, _availableWidth) {
473
466
  height: 0
474
467
  };
475
468
  }
476
-
477
469
  //#endregion
478
470
  //#region src/render/utils/font.ts
479
471
  /**
@@ -484,23 +476,42 @@ function measureSvgSize(element, _ctx, _availableWidth) {
484
476
  function buildFontString(font) {
485
477
  return `${font.style ?? "normal"} ${font.weight ?? "normal"} ${font.size ?? 16}px ${font.family ?? "sans-serif"}`;
486
478
  }
487
-
488
479
  //#endregion
489
480
  //#region src/layout/utils/measure.ts
481
+ const MEASURE_CACHE_LIMIT = 256;
490
482
  function createCanvasMeasureContext(ctx) {
483
+ const cache = /* @__PURE__ */ new Map();
484
+ let lastFontString = null;
491
485
  return { measureText(text, font) {
492
- ctx.font = buildFontString(font);
493
- ctx.textBaseline = "middle";
486
+ const fontString = buildFontString(font);
487
+ const key = fontString + "\0" + text;
488
+ const hit = cache.get(key);
489
+ if (hit !== void 0) {
490
+ cache.delete(key);
491
+ cache.set(key, hit);
492
+ return hit;
493
+ }
494
+ if (fontString !== lastFontString) {
495
+ ctx.font = fontString;
496
+ ctx.textBaseline = "middle";
497
+ lastFontString = fontString;
498
+ }
494
499
  const metrics = ctx.measureText(text);
495
500
  const height = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
496
501
  const fontSize = font.size || 16;
497
- return {
502
+ const result = {
498
503
  width: metrics.width,
499
504
  height: height || fontSize,
500
505
  offset: (metrics.actualBoundingBoxAscent - metrics.actualBoundingBoxDescent) / 2,
501
506
  ascent: metrics.actualBoundingBoxAscent,
502
507
  descent: metrics.actualBoundingBoxDescent
503
508
  };
509
+ if (cache.size >= MEASURE_CACHE_LIMIT) {
510
+ const oldest = cache.keys().next().value;
511
+ if (oldest !== void 0) cache.delete(oldest);
512
+ }
513
+ cache.set(key, result);
514
+ return result;
504
515
  } };
505
516
  }
506
517
  function wrapText(ctx, text, maxWidth, font) {
@@ -576,7 +587,6 @@ function truncateText(ctx, text, maxWidth, font, ellipsis = "...") {
576
587
  offset
577
588
  };
578
589
  }
579
-
580
590
  //#endregion
581
591
  //#region src/layout/components/text.ts
582
592
  /**
@@ -602,7 +612,6 @@ function measureTextSize(element, ctx, availableWidth) {
602
612
  height: Math.max(height, lineHeightPx)
603
613
  };
604
614
  }
605
-
606
615
  //#endregion
607
616
  //#region src/layout/components/transform.ts
608
617
  /**
@@ -613,7 +622,6 @@ function measureTextSize(element, ctx, availableWidth) {
613
622
  function measureTransformSize(element, ctx, availableWidth, measureIntrinsicSize) {
614
623
  return measureIntrinsicSize(element.children, ctx, availableWidth);
615
624
  }
616
-
617
625
  //#endregion
618
626
  //#region src/layout/components/index.ts
619
627
  /**
@@ -635,7 +643,6 @@ function measureIntrinsicSize(element, ctx, availableWidth) {
635
643
  };
636
644
  }
637
645
  }
638
-
639
646
  //#endregion
640
647
  //#region src/layout/utils/offset.ts
641
648
  /**
@@ -648,7 +655,6 @@ function applyOffset(node, dx, dy) {
648
655
  node.layout.contentY += dy;
649
656
  for (const child of node.children) applyOffset(child, dx, dy);
650
657
  }
651
-
652
658
  //#endregion
653
659
  //#region src/types/layout.ts
654
660
  function resolveSize(size, available, auto) {
@@ -663,7 +669,6 @@ function sizeNeedsParent(size) {
663
669
  if (typeof size === "string" && size.endsWith("%")) return true;
664
670
  return false;
665
671
  }
666
-
667
672
  //#endregion
668
673
  //#region src/layout/engine.ts
669
674
  /**
@@ -783,7 +788,14 @@ function computeLayoutImpl(element, ctx, constraints, x = 0, y = 0) {
783
788
  }
784
789
  if (layoutElement.type === "richtext") {
785
790
  const lineHeight = layoutElement.lineHeight ?? 1.2;
786
- let lines = wrapRichText(ctx, layoutElement.spans, contentWidth, lineHeight);
791
+ const elementStyle = {
792
+ font: layoutElement.font,
793
+ color: layoutElement.color,
794
+ background: layoutElement.background,
795
+ underline: layoutElement.underline,
796
+ strikethrough: layoutElement.strikethrough
797
+ };
798
+ let lines = wrapRichText(ctx, layoutElement.spans, contentWidth, lineHeight, elementStyle);
787
799
  if (layoutElement.maxLines && lines.length > layoutElement.maxLines) {
788
800
  lines = lines.slice(0, layoutElement.maxLines);
789
801
  if (layoutElement.ellipsis && lines.length > 0) {
@@ -1081,7 +1093,6 @@ function computeLayoutImpl(element, ctx, constraints, x = 0, y = 0) {
1081
1093
  }
1082
1094
  return node;
1083
1095
  }
1084
-
1085
1096
  //#endregion
1086
1097
  //#region src/render/utils/colors.ts
1087
1098
  function isGradientDescriptor$1(color) {
@@ -1117,7 +1128,6 @@ function resolveColor$1(ctx, color, x, y, width, height) {
1117
1128
  if (isGradientDescriptor$1(color)) return resolveGradient$1(ctx, color, x, y, width, height);
1118
1129
  return color;
1119
1130
  }
1120
-
1121
1131
  //#endregion
1122
1132
  //#region src/render/utils/shadows.ts
1123
1133
  function applyShadow$1(ctx, shadow) {
@@ -1139,7 +1149,6 @@ function clearShadow$1(ctx) {
1139
1149
  ctx.shadowBlur = 0;
1140
1150
  ctx.shadowColor = "transparent";
1141
1151
  }
1142
-
1143
1152
  //#endregion
1144
1153
  //#region src/render/utils/shapes.ts
1145
1154
  function roundRectPath(ctx, x, y, width, height, radius) {
@@ -1156,7 +1165,6 @@ function roundRectPath(ctx, x, y, width, height, radius) {
1156
1165
  ctx.quadraticCurveTo(x, y, x + tl, y);
1157
1166
  ctx.closePath();
1158
1167
  }
1159
-
1160
1168
  //#endregion
1161
1169
  //#region src/render/components/box.ts
1162
1170
  function renderBox(ctx, node) {
@@ -1185,125 +1193,90 @@ function renderBox(ctx, node) {
1185
1193
  }
1186
1194
  if (element.opacity !== void 0 && element.opacity < 1) ctx.globalAlpha = 1;
1187
1195
  }
1188
-
1189
1196
  //#endregion
1190
- //#region src/render/components/ProxiedCanvasContext.ts
1191
- /**
1192
- * ProxiedCanvasContext - Canvas 上下文代理类
1193
- *
1194
- * 该类提供对真实 CanvasRenderingContext2D 的代理,有以下功能:
1195
- * 1. 管理 save/restore 的平衡(计数器)
1196
- * 2. 追踪相对变换而不是绝对变换
1197
- * 3. 在析构时自动恢复所有未恢复的状态
1198
- * 4. 转发所有其他 Canvas API 调用
1199
- */
1200
- var ProxiedCanvasContext = class {
1201
- /**
1202
- * 真实的 Canvas 上下文
1203
- */
1204
- ctx;
1205
- /**
1206
- * 基础变换矩阵(初始化时设置,保持不变)
1207
- */
1197
+ //#region src/render/components/CustomDrawContext.ts
1198
+ function cloneMatrix(matrix) {
1199
+ return new DOMMatrix([
1200
+ matrix.a,
1201
+ matrix.b,
1202
+ matrix.c,
1203
+ matrix.d,
1204
+ matrix.e,
1205
+ matrix.f
1206
+ ]);
1207
+ }
1208
+ function toRelativeMatrix(transform) {
1209
+ if (transform === void 0) return new DOMMatrix();
1210
+ if (transform instanceof DOMMatrix) return cloneMatrix(transform);
1211
+ return new DOMMatrix(transform);
1212
+ }
1213
+ var ManagedCustomDrawContext = class {
1214
+ canvas;
1208
1215
  baseTransform;
1209
- /**
1210
- * 相对变换矩阵(用户通过 setTransform 设置)
1211
- */
1212
1216
  relativeTransform;
1213
- /**
1214
- * save/restore 计数器
1215
- */
1217
+ transformStack = [];
1216
1218
  saveCount = 0;
1217
- /**
1218
- * 构造函数
1219
- * @param ctx 真实的 CanvasRenderingContext2D
1220
- * @param baseTransform 初始的基础变换矩阵
1221
- */
1222
- constructor(ctx, baseTransform) {
1223
- this.ctx = ctx;
1224
- this.baseTransform = baseTransform;
1219
+ constructor(canvas, baseTransform) {
1220
+ this.canvas = canvas;
1221
+ this.baseTransform = cloneMatrix(baseTransform);
1225
1222
  this.relativeTransform = new DOMMatrix();
1226
1223
  }
1227
- /**
1228
- * save() - 保存当前状态并增加计数
1229
- */
1230
1224
  save() {
1231
1225
  this.saveCount++;
1232
- this.ctx.save();
1226
+ this.transformStack.push(cloneMatrix(this.relativeTransform));
1227
+ this.canvas.save();
1233
1228
  }
1234
- /**
1235
- * restore() - 恢复上一个状态并减少计数
1236
- */
1237
1229
  restore() {
1238
- if (this.saveCount > 0) {
1239
- this.saveCount--;
1240
- this.ctx.restore();
1241
- }
1230
+ if (this.saveCount === 0) return;
1231
+ this.saveCount--;
1232
+ this.relativeTransform = this.transformStack.pop() ?? new DOMMatrix();
1233
+ this.canvas.restore();
1234
+ this.applyRelativeTransform();
1242
1235
  }
1243
- /**
1244
- * setTransform() - 设置相对变换
1245
- */
1246
- setTransform(...args) {
1247
- let matrix;
1248
- if (args.length === 1 && args[0] instanceof DOMMatrix) matrix = args[0];
1249
- else if (args.length === 6) matrix = new DOMMatrix([
1250
- args[0],
1251
- args[1],
1252
- args[2],
1253
- args[3],
1254
- args[4],
1255
- args[5]
1256
- ]);
1257
- else return;
1258
- this.relativeTransform = matrix;
1259
- const actualTransform = this.baseTransform.multiply(matrix);
1260
- this.ctx.setTransform(actualTransform);
1261
- }
1262
- /**
1263
- * getTransform() - 返回相对变换(而不是绝对变换)
1264
- */
1265
1236
  getTransform() {
1266
- return this.relativeTransform;
1237
+ return cloneMatrix(this.relativeTransform);
1238
+ }
1239
+ setTransform(transform) {
1240
+ this.relativeTransform = toRelativeMatrix(transform);
1241
+ this.applyRelativeTransform();
1242
+ }
1243
+ resetTransform() {
1244
+ this.relativeTransform = new DOMMatrix();
1245
+ this.applyRelativeTransform();
1246
+ }
1247
+ translate(x, y) {
1248
+ this.relativeTransform = this.relativeTransform.translate(x, y);
1249
+ this.applyRelativeTransform();
1250
+ }
1251
+ rotate(angle) {
1252
+ this.relativeTransform = this.relativeTransform.rotate(angle * 180 / Math.PI);
1253
+ this.applyRelativeTransform();
1254
+ }
1255
+ scale(x, y) {
1256
+ this.relativeTransform = this.relativeTransform.scale(x, y ?? x);
1257
+ this.applyRelativeTransform();
1258
+ }
1259
+ transform(a, b, c, d, e, f) {
1260
+ this.relativeTransform = this.relativeTransform.multiply(new DOMMatrix([
1261
+ a,
1262
+ b,
1263
+ c,
1264
+ d,
1265
+ e,
1266
+ f
1267
+ ]));
1268
+ this.applyRelativeTransform();
1267
1269
  }
1268
- /**
1269
- * 析构函数级的清理 - 自动恢复所有未恢复的 save
1270
- */
1271
1270
  destroy() {
1272
- while (this.saveCount > 0) {
1273
- console.log("destroy restore", this.saveCount);
1274
- this.saveCount--;
1275
- this.ctx.restore();
1276
- }
1271
+ while (this.saveCount > 0) this.restore();
1272
+ }
1273
+ applyRelativeTransform() {
1274
+ this.canvas.setTransform(this.baseTransform.multiply(this.relativeTransform));
1277
1275
  }
1278
1276
  };
1279
- function createProxiedCanvasContext(ctx, baseTransform) {
1280
- const proxy = new ProxiedCanvasContext(ctx, baseTransform);
1281
- return new Proxy(proxy, {
1282
- get(target, prop, receiver) {
1283
- if (prop === "save" || prop === "restore" || prop === "setTransform" || prop === "getTransform" || prop === "destroy") return Reflect.get(target, prop, receiver).bind(proxy);
1284
- const ownValue = Reflect.get(target, prop, receiver);
1285
- if (ownValue !== void 0) return ownValue;
1286
- const contextValue = target.ctx[prop];
1287
- if (typeof contextValue === "function") return contextValue.bind(target.ctx);
1288
- return contextValue;
1289
- },
1290
- set(target, prop, value, _receiver) {
1291
- target.ctx[prop] = value;
1292
- return true;
1293
- },
1294
- has(target, prop) {
1295
- if (prop === "save" || prop === "restore" || prop === "setTransform" || prop === "getTransform" || prop === "destroy") return true;
1296
- return prop in target.ctx;
1297
- },
1298
- ownKeys(target) {
1299
- return Reflect.ownKeys(target.ctx);
1300
- },
1301
- getOwnPropertyDescriptor(target, prop) {
1302
- return Reflect.getOwnPropertyDescriptor(target.ctx, prop);
1303
- }
1304
- });
1277
+ function createCustomDrawContext(canvas, baseTransform) {
1278
+ return new ManagedCustomDrawContext(canvas, baseTransform);
1305
1279
  }
1306
-
1307
1280
  //#endregion
1308
1281
  //#region src/render/components/customDraw.ts
1309
1282
  /**
@@ -1314,7 +1287,7 @@ function renderCustomDraw(ctx, node) {
1314
1287
  const element = node.element;
1315
1288
  ctx.save();
1316
1289
  ctx.translate(node.layout.x, node.layout.y);
1317
- const proxyCtx = createProxiedCanvasContext(ctx, ctx.getTransform());
1290
+ const customCtx = createCustomDrawContext(ctx, ctx.getTransform());
1318
1291
  const inner = () => {
1319
1292
  if (node.children && node.children.length > 0) {
1320
1293
  ctx.save();
@@ -1323,15 +1296,14 @@ function renderCustomDraw(ctx, node) {
1323
1296
  ctx.restore();
1324
1297
  }
1325
1298
  };
1326
- element.draw(proxyCtx, {
1299
+ element.draw(customCtx, {
1327
1300
  inner,
1328
1301
  width: node.layout.contentWidth,
1329
1302
  height: node.layout.contentHeight
1330
1303
  });
1331
- proxyCtx.destroy();
1304
+ customCtx.destroy();
1332
1305
  ctx.restore();
1333
1306
  }
1334
-
1335
1307
  //#endregion
1336
1308
  //#region src/render/components/image.ts
1337
1309
  function renderImage(ctx, node) {
@@ -1417,7 +1389,6 @@ function renderImage(ctx, node) {
1417
1389
  }
1418
1390
  if (element.opacity !== void 0 && element.opacity < 1) ctx.globalAlpha = 1;
1419
1391
  }
1420
-
1421
1392
  //#endregion
1422
1393
  //#region src/render/components/richtext.ts
1423
1394
  function renderRichText(ctx, node) {
@@ -1468,7 +1439,6 @@ function renderRichText(ctx, node) {
1468
1439
  currentY += line.height;
1469
1440
  }
1470
1441
  }
1471
-
1472
1442
  //#endregion
1473
1443
  //#region src/render/components/svg.ts
1474
1444
  function isGradientDescriptor(color) {
@@ -1740,7 +1710,6 @@ function renderSvg(ctx, node) {
1740
1710
  for (const child of element.children) renderSvgChild(ctx, child, transform, bounds, baseTransform);
1741
1711
  ctx.restore();
1742
1712
  }
1743
-
1744
1713
  //#endregion
1745
1714
  //#region src/render/components/text.ts
1746
1715
  function renderText(ctx, node) {
@@ -1775,7 +1744,6 @@ function renderText(ctx, node) {
1775
1744
  }
1776
1745
  if (element.shadow) clearShadow$1(ctx);
1777
1746
  }
1778
-
1779
1747
  //#endregion
1780
1748
  //#region src/render/components/transform.ts
1781
1749
  /**
@@ -1851,7 +1819,6 @@ function renderTransform(ctx, node) {
1851
1819
  renderNode(ctx, childNode);
1852
1820
  ctx.restore();
1853
1821
  }
1854
-
1855
1822
  //#endregion
1856
1823
  //#region src/render/index.ts
1857
1824
  function renderNode(ctx, node) {
@@ -1891,7 +1858,6 @@ function renderNode(ctx, node) {
1891
1858
  break;
1892
1859
  }
1893
1860
  }
1894
-
1895
1861
  //#endregion
1896
1862
  //#region src/canvas.ts
1897
1863
  /**
@@ -1961,7 +1927,6 @@ function createCanvas(options) {
1961
1927
  }
1962
1928
  };
1963
1929
  }
1964
-
1965
1930
  //#endregion
1966
1931
  //#region src/components/Box.ts
1967
1932
  function Box(props) {
@@ -1970,7 +1935,6 @@ function Box(props) {
1970
1935
  ...props
1971
1936
  };
1972
1937
  }
1973
-
1974
1938
  //#endregion
1975
1939
  //#region src/components/CustomDraw.ts
1976
1940
  function CustomDraw(props) {
@@ -1979,7 +1943,6 @@ function CustomDraw(props) {
1979
1943
  ...props
1980
1944
  };
1981
1945
  }
1982
-
1983
1946
  //#endregion
1984
1947
  //#region src/components/Image.ts
1985
1948
  function Image(props) {
@@ -1988,7 +1951,6 @@ function Image(props) {
1988
1951
  ...props
1989
1952
  };
1990
1953
  }
1991
-
1992
1954
  //#endregion
1993
1955
  //#region src/components/RichText.ts
1994
1956
  function RichText(props) {
@@ -1997,7 +1959,6 @@ function RichText(props) {
1997
1959
  ...props
1998
1960
  };
1999
1961
  }
2000
-
2001
1962
  //#endregion
2002
1963
  //#region src/components/Stack.ts
2003
1964
  function Stack(props) {
@@ -2006,7 +1967,6 @@ function Stack(props) {
2006
1967
  ...props
2007
1968
  };
2008
1969
  }
2009
-
2010
1970
  //#endregion
2011
1971
  //#region src/components/Svg.ts
2012
1972
  function Svg(props) {
@@ -2053,7 +2013,6 @@ const svg = {
2053
2013
  ...props
2054
2014
  })
2055
2015
  };
2056
-
2057
2016
  //#endregion
2058
2017
  //#region src/components/Text.ts
2059
2018
  function Text(props) {
@@ -2062,7 +2021,6 @@ function Text(props) {
2062
2021
  ...props
2063
2022
  };
2064
2023
  }
2065
-
2066
2024
  //#endregion
2067
2025
  //#region src/components/Transform.ts
2068
2026
  function Transform(props) {
@@ -2071,7 +2029,6 @@ function Transform(props) {
2071
2029
  ...props
2072
2030
  };
2073
2031
  }
2074
-
2075
2032
  //#endregion
2076
2033
  //#region src/layout/utils/print.ts
2077
2034
  /**
@@ -2131,6 +2088,5 @@ function printLayout(node) {
2131
2088
  function layoutToString(node, _indent = " ") {
2132
2089
  return printLayoutToString(node, "", true).join("\n");
2133
2090
  }
2134
-
2135
2091
  //#endregion
2136
- export { Box, CustomDraw, Image, RichText, Stack, Svg, Text, Transform, computeLayout, createCanvas, createCanvasMeasureContext, layoutToString, linearGradient, printLayout, radialGradient, svg };
2092
+ export { Box, CustomDraw, Image, RichText, Stack, Svg, Text, Transform, computeLayout, createCanvas, createCanvasMeasureContext, layoutToString, linearGradient, printLayout, radialGradient, svg };