@codehz/draw-call 0.2.2 → 0.4.1
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/browser/index.cjs +2058 -0
- package/{canvas.d.cts → browser/index.d.cts} +52 -1
- package/{canvas.d.mts → browser/index.d.ts} +52 -1
- package/{render.cjs → browser/index.js} +240 -65
- package/examples/card.ts +2 -3
- package/examples/customdraw-basic.ts +2 -3
- package/examples/customdraw.ts +2 -3
- package/examples/richtext.ts +2 -3
- package/examples/transform.ts +2 -3
- package/node/index.cjs +2054 -0
- package/node/index.d.cts +452 -0
- package/node/index.d.mts +452 -0
- package/{render.mjs → node/index.mjs} +235 -39
- package/package.json +19 -24
- package/index.cjs +0 -241
- package/index.d.cts +0 -54
- package/index.d.mts +0 -54
- package/index.mjs +0 -226
- package/node.cjs +0 -50
- package/node.d.cts +0 -12
- package/node.d.mts +0 -12
- package/node.mjs +0 -50
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DOMMatrix, Path2D, createCanvas as createCanvas$1 } from "@napi-rs/canvas";
|
|
2
2
|
|
|
3
|
-
//#region
|
|
4
|
-
|
|
3
|
+
//#region src/compat/index.ts
|
|
4
|
+
function createRawCanvas(width, height) {
|
|
5
|
+
return createCanvas$1(width, height);
|
|
6
|
+
}
|
|
5
7
|
|
|
6
8
|
//#endregion
|
|
7
9
|
//#region src/types/base.ts
|
|
@@ -1111,17 +1113,6 @@ function renderBox(ctx, node) {
|
|
|
1111
1113
|
if (element.opacity !== void 0 && element.opacity < 1) ctx.globalAlpha = 1;
|
|
1112
1114
|
}
|
|
1113
1115
|
|
|
1114
|
-
//#endregion
|
|
1115
|
-
//#region src/compat/DOMMatrix.ts
|
|
1116
|
-
const DOMMatrixCompat = (() => {
|
|
1117
|
-
if (typeof DOMMatrix !== "undefined") return DOMMatrix;
|
|
1118
|
-
try {
|
|
1119
|
-
return __require("@napi-rs/canvas").DOMMatrix;
|
|
1120
|
-
} catch {
|
|
1121
|
-
throw new Error("DOMMatrix is not available. In Node.js, install @napi-rs/canvas.");
|
|
1122
|
-
}
|
|
1123
|
-
})();
|
|
1124
|
-
|
|
1125
1116
|
//#endregion
|
|
1126
1117
|
//#region src/render/components/ProxiedCanvasContext.ts
|
|
1127
1118
|
/**
|
|
@@ -1158,7 +1149,7 @@ var ProxiedCanvasContext = class {
|
|
|
1158
1149
|
constructor(ctx, baseTransform) {
|
|
1159
1150
|
this.ctx = ctx;
|
|
1160
1151
|
this.baseTransform = baseTransform;
|
|
1161
|
-
this.relativeTransform = new
|
|
1152
|
+
this.relativeTransform = new DOMMatrix();
|
|
1162
1153
|
}
|
|
1163
1154
|
/**
|
|
1164
1155
|
* save() - 保存当前状态并增加计数
|
|
@@ -1181,8 +1172,8 @@ var ProxiedCanvasContext = class {
|
|
|
1181
1172
|
*/
|
|
1182
1173
|
setTransform(...args) {
|
|
1183
1174
|
let matrix;
|
|
1184
|
-
if (args.length === 1 && args[0] instanceof
|
|
1185
|
-
else if (args.length === 6) matrix = new
|
|
1175
|
+
if (args.length === 1 && args[0] instanceof DOMMatrix) matrix = args[0];
|
|
1176
|
+
else if (args.length === 6) matrix = new DOMMatrix([
|
|
1186
1177
|
args[0],
|
|
1187
1178
|
args[1],
|
|
1188
1179
|
args[2],
|
|
@@ -1405,17 +1396,6 @@ function renderRichText(ctx, node) {
|
|
|
1405
1396
|
}
|
|
1406
1397
|
}
|
|
1407
1398
|
|
|
1408
|
-
//#endregion
|
|
1409
|
-
//#region src/compat/Path2D.ts
|
|
1410
|
-
const Path2DCompat = (() => {
|
|
1411
|
-
if (typeof Path2D !== "undefined") return Path2D;
|
|
1412
|
-
try {
|
|
1413
|
-
return __require("@napi-rs/canvas").Path2D;
|
|
1414
|
-
} catch {
|
|
1415
|
-
throw new Error("Path2D is not available. In Node.js, install @napi-rs/canvas.");
|
|
1416
|
-
}
|
|
1417
|
-
})();
|
|
1418
|
-
|
|
1419
1399
|
//#endregion
|
|
1420
1400
|
//#region src/render/components/svg.ts
|
|
1421
1401
|
function isGradientDescriptor(color) {
|
|
@@ -1453,7 +1433,7 @@ function resolveColor(ctx, color, x, y, width, height) {
|
|
|
1453
1433
|
}
|
|
1454
1434
|
function applyTransform(base, transform) {
|
|
1455
1435
|
if (!transform) return base;
|
|
1456
|
-
let result = new
|
|
1436
|
+
let result = new DOMMatrix([
|
|
1457
1437
|
base.a,
|
|
1458
1438
|
base.b,
|
|
1459
1439
|
base.c,
|
|
@@ -1463,7 +1443,7 @@ function applyTransform(base, transform) {
|
|
|
1463
1443
|
]);
|
|
1464
1444
|
if (transform.matrix) {
|
|
1465
1445
|
const [a, b, c, d, e, f] = transform.matrix;
|
|
1466
|
-
result = result.multiply(new
|
|
1446
|
+
result = result.multiply(new DOMMatrix([
|
|
1467
1447
|
a,
|
|
1468
1448
|
b,
|
|
1469
1449
|
c,
|
|
@@ -1552,7 +1532,7 @@ function renderSvgPolygon(ctx, polygon, bounds) {
|
|
|
1552
1532
|
applyFillAndStroke(ctx, polygon, bounds);
|
|
1553
1533
|
}
|
|
1554
1534
|
function renderSvgPath(ctx, path, bounds) {
|
|
1555
|
-
const path2d = new
|
|
1535
|
+
const path2d = new Path2D(path.d);
|
|
1556
1536
|
if (path.fill && path.fill !== "none") {
|
|
1557
1537
|
ctx.fillStyle = resolveColor(ctx, path.fill, bounds.x, bounds.y, bounds.width, bounds.height);
|
|
1558
1538
|
ctx.fill(path2d);
|
|
@@ -1634,7 +1614,7 @@ function calculateViewBoxTransform(x, y, width, height, viewBox, preserveAspectR
|
|
|
1634
1614
|
const scaleY = height / vbHeight;
|
|
1635
1615
|
const align = preserveAspectRatio?.align ?? "xMidYMid";
|
|
1636
1616
|
const meetOrSlice = preserveAspectRatio?.meetOrSlice ?? "meet";
|
|
1637
|
-
if (align === "none") return new
|
|
1617
|
+
if (align === "none") return new DOMMatrix().translate(x, y).scale(scaleX, scaleY).translate(-vbX, -vbY);
|
|
1638
1618
|
const scale = meetOrSlice === "meet" ? Math.min(scaleX, scaleY) : Math.max(scaleX, scaleY);
|
|
1639
1619
|
const scaledWidth = vbWidth * scale;
|
|
1640
1620
|
const scaledHeight = vbHeight * scale;
|
|
@@ -1644,7 +1624,7 @@ function calculateViewBoxTransform(x, y, width, height, viewBox, preserveAspectR
|
|
|
1644
1624
|
else if (align.includes("xMax")) translateX += width - scaledWidth;
|
|
1645
1625
|
if (align.includes("YMid")) translateY += (height - scaledHeight) / 2;
|
|
1646
1626
|
else if (align.includes("YMax")) translateY += height - scaledHeight;
|
|
1647
|
-
return new
|
|
1627
|
+
return new DOMMatrix().translate(translateX, translateY).scale(scale, scale).translate(-vbX, -vbY);
|
|
1648
1628
|
}
|
|
1649
1629
|
function applyShadow(ctx, shadow) {
|
|
1650
1630
|
ctx.shadowOffsetX = shadow.offsetX ?? 0;
|
|
@@ -1733,13 +1713,13 @@ function renderText(ctx, node) {
|
|
|
1733
1713
|
* - 简易对象: { translate, rotate, scale, skewX, skewY }
|
|
1734
1714
|
*/
|
|
1735
1715
|
function parseTransformValue(transform) {
|
|
1736
|
-
if (transform === void 0) return new
|
|
1737
|
-
if (Array.isArray(transform)) return new
|
|
1716
|
+
if (transform === void 0) return new DOMMatrix();
|
|
1717
|
+
if (Array.isArray(transform)) return new DOMMatrix(transform);
|
|
1738
1718
|
const hasDOMMatrixInit = "a" in transform || "b" in transform || "c" in transform || "d" in transform || "e" in transform || "f" in transform;
|
|
1739
1719
|
const hasSimpleTransform = "translate" in transform || "rotate" in transform || "scale" in transform || "skewX" in transform || "skewY" in transform;
|
|
1740
1720
|
if (hasDOMMatrixInit && !hasSimpleTransform) {
|
|
1741
1721
|
const init = transform;
|
|
1742
|
-
return new
|
|
1722
|
+
return new DOMMatrix([
|
|
1743
1723
|
init.a ?? 1,
|
|
1744
1724
|
init.b ?? 0,
|
|
1745
1725
|
init.c ?? 0,
|
|
@@ -1749,7 +1729,7 @@ function parseTransformValue(transform) {
|
|
|
1749
1729
|
]);
|
|
1750
1730
|
}
|
|
1751
1731
|
const simpleObj = transform;
|
|
1752
|
-
let result = new
|
|
1732
|
+
let result = new DOMMatrix();
|
|
1753
1733
|
if (simpleObj.translate) result = result.translate(simpleObj.translate[0], simpleObj.translate[1]);
|
|
1754
1734
|
if (simpleObj.rotate !== void 0) if (typeof simpleObj.rotate === "number") result = result.rotate(simpleObj.rotate);
|
|
1755
1735
|
else {
|
|
@@ -1791,7 +1771,7 @@ function renderTransform(ctx, node) {
|
|
|
1791
1771
|
const ox = childNode.layout.x + relativeOx;
|
|
1792
1772
|
const oy = childNode.layout.y + relativeOy;
|
|
1793
1773
|
const targetMatrix = parseTransformValue(element.transform);
|
|
1794
|
-
const finalMatrix = new
|
|
1774
|
+
const finalMatrix = new DOMMatrix().translate(ox, oy).multiply(targetMatrix).translate(-ox, -oy);
|
|
1795
1775
|
ctx.save();
|
|
1796
1776
|
const composedTransform = ctx.getTransform().multiply(finalMatrix);
|
|
1797
1777
|
ctx.setTransform(composedTransform);
|
|
@@ -1840,4 +1820,220 @@ function renderNode(ctx, node) {
|
|
|
1840
1820
|
}
|
|
1841
1821
|
|
|
1842
1822
|
//#endregion
|
|
1843
|
-
|
|
1823
|
+
//#region src/canvas.ts
|
|
1824
|
+
/**
|
|
1825
|
+
* 创建适用于浏览器环境的 Canvas
|
|
1826
|
+
*
|
|
1827
|
+
* 在浏览器环境中使用,支持传入已有的 canvas 实例
|
|
1828
|
+
*/
|
|
1829
|
+
function createCanvas(options) {
|
|
1830
|
+
const { width, height, pixelRatio = 1 } = options;
|
|
1831
|
+
const canvas = options.canvas ?? createRawCanvas(width * pixelRatio, height * pixelRatio);
|
|
1832
|
+
const ctx = canvas.getContext("2d");
|
|
1833
|
+
if (!ctx) throw new Error("Failed to get 2d context");
|
|
1834
|
+
if (options.imageSmoothingEnabled !== void 0) ctx.imageSmoothingEnabled = options.imageSmoothingEnabled;
|
|
1835
|
+
if (options.imageSmoothingQuality !== void 0) ctx.imageSmoothingQuality = options.imageSmoothingQuality;
|
|
1836
|
+
if (pixelRatio !== 1) ctx.scale(pixelRatio, pixelRatio);
|
|
1837
|
+
const measureCtx = createCanvasMeasureContext(ctx);
|
|
1838
|
+
return {
|
|
1839
|
+
width,
|
|
1840
|
+
height,
|
|
1841
|
+
pixelRatio,
|
|
1842
|
+
canvas,
|
|
1843
|
+
render(element) {
|
|
1844
|
+
const layoutTree = computeLayout(element, measureCtx, {
|
|
1845
|
+
minWidth: 0,
|
|
1846
|
+
maxWidth: width,
|
|
1847
|
+
minHeight: 0,
|
|
1848
|
+
maxHeight: height
|
|
1849
|
+
});
|
|
1850
|
+
renderNode(ctx, layoutTree);
|
|
1851
|
+
return layoutTree;
|
|
1852
|
+
},
|
|
1853
|
+
clear() {
|
|
1854
|
+
ctx.clearRect(0, 0, width, height);
|
|
1855
|
+
},
|
|
1856
|
+
getContext() {
|
|
1857
|
+
return ctx;
|
|
1858
|
+
},
|
|
1859
|
+
toDataURL(type, quality) {
|
|
1860
|
+
if ("toDataURL" in canvas && typeof canvas.toDataURL === "function") return canvas.toDataURL(type, quality);
|
|
1861
|
+
throw new Error("toDataURL not supported");
|
|
1862
|
+
},
|
|
1863
|
+
toBuffer(type = "image/png") {
|
|
1864
|
+
if ("toBuffer" in canvas && typeof canvas.toBuffer === "function") return canvas.toBuffer(type);
|
|
1865
|
+
throw new Error("toBuffer not supported in this environment");
|
|
1866
|
+
}
|
|
1867
|
+
};
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1870
|
+
//#endregion
|
|
1871
|
+
//#region src/components/Box.ts
|
|
1872
|
+
function Box(props) {
|
|
1873
|
+
return {
|
|
1874
|
+
type: "box",
|
|
1875
|
+
...props
|
|
1876
|
+
};
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1879
|
+
//#endregion
|
|
1880
|
+
//#region src/components/CustomDraw.ts
|
|
1881
|
+
function CustomDraw(props) {
|
|
1882
|
+
return {
|
|
1883
|
+
type: "customdraw",
|
|
1884
|
+
...props
|
|
1885
|
+
};
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
//#endregion
|
|
1889
|
+
//#region src/components/Image.ts
|
|
1890
|
+
function Image(props) {
|
|
1891
|
+
return {
|
|
1892
|
+
type: "image",
|
|
1893
|
+
...props
|
|
1894
|
+
};
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
//#endregion
|
|
1898
|
+
//#region src/components/RichText.ts
|
|
1899
|
+
function RichText(props) {
|
|
1900
|
+
return {
|
|
1901
|
+
type: "richtext",
|
|
1902
|
+
...props
|
|
1903
|
+
};
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
//#endregion
|
|
1907
|
+
//#region src/components/Stack.ts
|
|
1908
|
+
function Stack(props) {
|
|
1909
|
+
return {
|
|
1910
|
+
type: "stack",
|
|
1911
|
+
...props
|
|
1912
|
+
};
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
//#endregion
|
|
1916
|
+
//#region src/components/Svg.ts
|
|
1917
|
+
function Svg(props) {
|
|
1918
|
+
return {
|
|
1919
|
+
type: "svg",
|
|
1920
|
+
...props
|
|
1921
|
+
};
|
|
1922
|
+
}
|
|
1923
|
+
const svg = {
|
|
1924
|
+
rect: (props) => ({
|
|
1925
|
+
type: "rect",
|
|
1926
|
+
...props
|
|
1927
|
+
}),
|
|
1928
|
+
circle: (props) => ({
|
|
1929
|
+
type: "circle",
|
|
1930
|
+
...props
|
|
1931
|
+
}),
|
|
1932
|
+
ellipse: (props) => ({
|
|
1933
|
+
type: "ellipse",
|
|
1934
|
+
...props
|
|
1935
|
+
}),
|
|
1936
|
+
line: (props) => ({
|
|
1937
|
+
type: "line",
|
|
1938
|
+
...props
|
|
1939
|
+
}),
|
|
1940
|
+
polyline: (props) => ({
|
|
1941
|
+
type: "polyline",
|
|
1942
|
+
...props
|
|
1943
|
+
}),
|
|
1944
|
+
polygon: (props) => ({
|
|
1945
|
+
type: "polygon",
|
|
1946
|
+
...props
|
|
1947
|
+
}),
|
|
1948
|
+
path: (props) => ({
|
|
1949
|
+
type: "path",
|
|
1950
|
+
...props
|
|
1951
|
+
}),
|
|
1952
|
+
text: (props) => ({
|
|
1953
|
+
type: "text",
|
|
1954
|
+
...props
|
|
1955
|
+
}),
|
|
1956
|
+
g: (props) => ({
|
|
1957
|
+
type: "g",
|
|
1958
|
+
...props
|
|
1959
|
+
})
|
|
1960
|
+
};
|
|
1961
|
+
|
|
1962
|
+
//#endregion
|
|
1963
|
+
//#region src/components/Text.ts
|
|
1964
|
+
function Text(props) {
|
|
1965
|
+
return {
|
|
1966
|
+
type: "text",
|
|
1967
|
+
...props
|
|
1968
|
+
};
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
//#endregion
|
|
1972
|
+
//#region src/components/Transform.ts
|
|
1973
|
+
function Transform(props) {
|
|
1974
|
+
return {
|
|
1975
|
+
type: "transform",
|
|
1976
|
+
...props
|
|
1977
|
+
};
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
//#endregion
|
|
1981
|
+
//#region src/layout/utils/print.ts
|
|
1982
|
+
/**
|
|
1983
|
+
* 获取元素类型的显示名称
|
|
1984
|
+
*/
|
|
1985
|
+
function getElementType(element) {
|
|
1986
|
+
switch (element.type) {
|
|
1987
|
+
case "box": return "Box";
|
|
1988
|
+
case "text": return `Text "${element.content.slice(0, 20)}${element.content.length > 20 ? "..." : ""}"`;
|
|
1989
|
+
case "stack": return "Stack";
|
|
1990
|
+
case "image": return "Image";
|
|
1991
|
+
case "svg": return "Svg";
|
|
1992
|
+
default: return element.type;
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
/**
|
|
1996
|
+
* 递归打印布局树
|
|
1997
|
+
*/
|
|
1998
|
+
function printLayoutToString(node, prefix = "", isLast = true, depth = 0) {
|
|
1999
|
+
const lines = [];
|
|
2000
|
+
const connector = isLast ? "└─ " : "├─ ";
|
|
2001
|
+
const type = getElementType(node.element);
|
|
2002
|
+
const { x, y, width, height } = node.layout;
|
|
2003
|
+
const childCount = node.children.length;
|
|
2004
|
+
lines.push(`${prefix}${connector}${type} @(${Math.round(x)},${Math.round(y)}) size:${Math.round(width)}x${Math.round(height)}`);
|
|
2005
|
+
if (node.element.type === "text" && node.lines) {
|
|
2006
|
+
const contentPrefix = prefix + (isLast ? " " : "│ ");
|
|
2007
|
+
for (let i = 0; i < node.lines.length; i++) {
|
|
2008
|
+
const lineText = node.lines[i];
|
|
2009
|
+
const isLastLine = i === node.lines.length - 1 && childCount === 0;
|
|
2010
|
+
lines.push(`${contentPrefix}${isLastLine ? "└─ " : "├─ "}${JSON.stringify(lineText)}`);
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
2014
|
+
const child = node.children[i];
|
|
2015
|
+
const isChildLast = i === node.children.length - 1;
|
|
2016
|
+
const childPrefix = prefix + (isLast ? " " : "│ ");
|
|
2017
|
+
lines.push(...printLayoutToString(child, childPrefix, isChildLast, depth + 1));
|
|
2018
|
+
}
|
|
2019
|
+
return lines;
|
|
2020
|
+
}
|
|
2021
|
+
/**
|
|
2022
|
+
* 打印 LayoutNode 树结构到控制台
|
|
2023
|
+
*/
|
|
2024
|
+
function printLayout(node) {
|
|
2025
|
+
const lines = printLayoutToString(node, "", true);
|
|
2026
|
+
console.log(lines.join("\n"));
|
|
2027
|
+
}
|
|
2028
|
+
/**
|
|
2029
|
+
* 将 LayoutNode 转换为美观的字符串
|
|
2030
|
+
* @param node LayoutNode 根节点
|
|
2031
|
+
* @param indent 缩进字符串,默认为两个空格
|
|
2032
|
+
* @returns 格式化的字符串
|
|
2033
|
+
*/
|
|
2034
|
+
function layoutToString(node, _indent = " ") {
|
|
2035
|
+
return printLayoutToString(node, "", true).join("\n");
|
|
2036
|
+
}
|
|
2037
|
+
|
|
2038
|
+
//#endregion
|
|
2039
|
+
export { Box, CustomDraw, Image, RichText, Stack, Svg, Text, Transform, computeLayout, createCanvas, createCanvasMeasureContext, layoutToString, linearGradient, printLayout, radialGradient, svg };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codehz/draw-call",
|
|
3
|
-
"module": "./index.mjs",
|
|
3
|
+
"module": "./node/index.mjs",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -25,33 +25,28 @@
|
|
|
25
25
|
"optionalDependencies": {
|
|
26
26
|
"@napi-rs/canvas": "^0.1.88"
|
|
27
27
|
},
|
|
28
|
-
"version": "0.
|
|
29
|
-
"main": "./index.cjs",
|
|
30
|
-
"types": "./index.d.cts",
|
|
28
|
+
"version": "0.4.1",
|
|
29
|
+
"main": "./node/index.cjs",
|
|
30
|
+
"types": "./node/index.d.cts",
|
|
31
31
|
"exports": {
|
|
32
32
|
".": {
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"default": "./index.cjs"
|
|
33
|
+
"types": {
|
|
34
|
+
"import": "./node/index.d.mts",
|
|
35
|
+
"require": "./node/index.d.cts"
|
|
37
36
|
},
|
|
38
|
-
"
|
|
39
|
-
"types":
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"require": {
|
|
46
|
-
"types": "./node.d.cts",
|
|
47
|
-
"import": "./node.cjs",
|
|
48
|
-
"default": "./node.cjs"
|
|
37
|
+
"browser": {
|
|
38
|
+
"types": {
|
|
39
|
+
"import": "./browser/index.d.mts",
|
|
40
|
+
"require": "./browser/index.d.cts"
|
|
41
|
+
},
|
|
42
|
+
"import": "./browser/index.mjs",
|
|
43
|
+
"require": "./browser/index.cjs"
|
|
49
44
|
},
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
45
|
+
"bun": {
|
|
46
|
+
"import": "./node/index.mjs"
|
|
47
|
+
},
|
|
48
|
+
"import": "./node/index.mjs",
|
|
49
|
+
"require": "./node/index.cjs"
|
|
55
50
|
}
|
|
56
51
|
}
|
|
57
52
|
}
|
package/index.cjs
DELETED
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
const require_render = require('./render.cjs');
|
|
2
|
-
|
|
3
|
-
//#region src/canvas.ts
|
|
4
|
-
/**
|
|
5
|
-
* 创建适用于浏览器环境的 Canvas
|
|
6
|
-
*
|
|
7
|
-
* 在浏览器环境中使用,支持传入已有的 canvas 实例
|
|
8
|
-
*/
|
|
9
|
-
function createCanvas(options) {
|
|
10
|
-
const { width, height, pixelRatio = 1 } = options;
|
|
11
|
-
let canvas;
|
|
12
|
-
if (options.canvas) canvas = options.canvas;
|
|
13
|
-
else {
|
|
14
|
-
const el = document.createElement("canvas");
|
|
15
|
-
el.width = width * pixelRatio;
|
|
16
|
-
el.height = height * pixelRatio;
|
|
17
|
-
canvas = el;
|
|
18
|
-
}
|
|
19
|
-
const ctx = canvas.getContext("2d");
|
|
20
|
-
if (!ctx) throw new Error("Failed to get 2d context");
|
|
21
|
-
if (options.imageSmoothingEnabled !== void 0) ctx.imageSmoothingEnabled = options.imageSmoothingEnabled;
|
|
22
|
-
if (options.imageSmoothingQuality !== void 0) ctx.imageSmoothingQuality = options.imageSmoothingQuality;
|
|
23
|
-
if (pixelRatio !== 1) ctx.scale(pixelRatio, pixelRatio);
|
|
24
|
-
const measureCtx = require_render.createCanvasMeasureContext(ctx);
|
|
25
|
-
return {
|
|
26
|
-
width,
|
|
27
|
-
height,
|
|
28
|
-
pixelRatio,
|
|
29
|
-
canvas,
|
|
30
|
-
render(element) {
|
|
31
|
-
const layoutTree = require_render.computeLayout(element, measureCtx, {
|
|
32
|
-
minWidth: 0,
|
|
33
|
-
maxWidth: width,
|
|
34
|
-
minHeight: 0,
|
|
35
|
-
maxHeight: height
|
|
36
|
-
});
|
|
37
|
-
require_render.renderNode(ctx, layoutTree);
|
|
38
|
-
return layoutTree;
|
|
39
|
-
},
|
|
40
|
-
clear() {
|
|
41
|
-
ctx.clearRect(0, 0, width, height);
|
|
42
|
-
},
|
|
43
|
-
getContext() {
|
|
44
|
-
return ctx;
|
|
45
|
-
},
|
|
46
|
-
toDataURL(type, quality) {
|
|
47
|
-
if ("toDataURL" in canvas && typeof canvas.toDataURL === "function") return canvas.toDataURL(type, quality);
|
|
48
|
-
throw new Error("toDataURL not supported");
|
|
49
|
-
},
|
|
50
|
-
toBuffer(type = "image/png") {
|
|
51
|
-
if ("toBuffer" in canvas && typeof canvas.toBuffer === "function") return canvas.toBuffer(type);
|
|
52
|
-
throw new Error("toBuffer not supported in this environment");
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
//#endregion
|
|
58
|
-
//#region src/components/Box.ts
|
|
59
|
-
function Box(props) {
|
|
60
|
-
return {
|
|
61
|
-
type: "box",
|
|
62
|
-
...props
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
//#endregion
|
|
67
|
-
//#region src/components/CustomDraw.ts
|
|
68
|
-
function CustomDraw(props) {
|
|
69
|
-
return {
|
|
70
|
-
type: "customdraw",
|
|
71
|
-
...props
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
//#endregion
|
|
76
|
-
//#region src/components/Image.ts
|
|
77
|
-
function Image(props) {
|
|
78
|
-
return {
|
|
79
|
-
type: "image",
|
|
80
|
-
...props
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
//#endregion
|
|
85
|
-
//#region src/components/RichText.ts
|
|
86
|
-
function RichText(props) {
|
|
87
|
-
return {
|
|
88
|
-
type: "richtext",
|
|
89
|
-
...props
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
//#endregion
|
|
94
|
-
//#region src/components/Stack.ts
|
|
95
|
-
function Stack(props) {
|
|
96
|
-
return {
|
|
97
|
-
type: "stack",
|
|
98
|
-
...props
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
//#endregion
|
|
103
|
-
//#region src/components/Svg.ts
|
|
104
|
-
function Svg(props) {
|
|
105
|
-
return {
|
|
106
|
-
type: "svg",
|
|
107
|
-
...props
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
const svg = {
|
|
111
|
-
rect: (props) => ({
|
|
112
|
-
type: "rect",
|
|
113
|
-
...props
|
|
114
|
-
}),
|
|
115
|
-
circle: (props) => ({
|
|
116
|
-
type: "circle",
|
|
117
|
-
...props
|
|
118
|
-
}),
|
|
119
|
-
ellipse: (props) => ({
|
|
120
|
-
type: "ellipse",
|
|
121
|
-
...props
|
|
122
|
-
}),
|
|
123
|
-
line: (props) => ({
|
|
124
|
-
type: "line",
|
|
125
|
-
...props
|
|
126
|
-
}),
|
|
127
|
-
polyline: (props) => ({
|
|
128
|
-
type: "polyline",
|
|
129
|
-
...props
|
|
130
|
-
}),
|
|
131
|
-
polygon: (props) => ({
|
|
132
|
-
type: "polygon",
|
|
133
|
-
...props
|
|
134
|
-
}),
|
|
135
|
-
path: (props) => ({
|
|
136
|
-
type: "path",
|
|
137
|
-
...props
|
|
138
|
-
}),
|
|
139
|
-
text: (props) => ({
|
|
140
|
-
type: "text",
|
|
141
|
-
...props
|
|
142
|
-
}),
|
|
143
|
-
g: (props) => ({
|
|
144
|
-
type: "g",
|
|
145
|
-
...props
|
|
146
|
-
})
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
//#endregion
|
|
150
|
-
//#region src/components/Text.ts
|
|
151
|
-
function Text(props) {
|
|
152
|
-
return {
|
|
153
|
-
type: "text",
|
|
154
|
-
...props
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
//#endregion
|
|
159
|
-
//#region src/components/Transform.ts
|
|
160
|
-
function Transform(props) {
|
|
161
|
-
return {
|
|
162
|
-
type: "transform",
|
|
163
|
-
...props
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
//#endregion
|
|
168
|
-
//#region src/layout/utils/print.ts
|
|
169
|
-
/**
|
|
170
|
-
* 获取元素类型的显示名称
|
|
171
|
-
*/
|
|
172
|
-
function getElementType(element) {
|
|
173
|
-
switch (element.type) {
|
|
174
|
-
case "box": return "Box";
|
|
175
|
-
case "text": return `Text "${element.content.slice(0, 20)}${element.content.length > 20 ? "..." : ""}"`;
|
|
176
|
-
case "stack": return "Stack";
|
|
177
|
-
case "image": return "Image";
|
|
178
|
-
case "svg": return "Svg";
|
|
179
|
-
default: return element.type;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
/**
|
|
183
|
-
* 递归打印布局树
|
|
184
|
-
*/
|
|
185
|
-
function printLayoutToString(node, prefix = "", isLast = true, depth = 0) {
|
|
186
|
-
const lines = [];
|
|
187
|
-
const connector = isLast ? "└─ " : "├─ ";
|
|
188
|
-
const type = getElementType(node.element);
|
|
189
|
-
const { x, y, width, height } = node.layout;
|
|
190
|
-
const childCount = node.children.length;
|
|
191
|
-
lines.push(`${prefix}${connector}${type} @(${Math.round(x)},${Math.round(y)}) size:${Math.round(width)}x${Math.round(height)}`);
|
|
192
|
-
if (node.element.type === "text" && node.lines) {
|
|
193
|
-
const contentPrefix = prefix + (isLast ? " " : "│ ");
|
|
194
|
-
for (let i = 0; i < node.lines.length; i++) {
|
|
195
|
-
const lineText = node.lines[i];
|
|
196
|
-
const isLastLine = i === node.lines.length - 1 && childCount === 0;
|
|
197
|
-
lines.push(`${contentPrefix}${isLastLine ? "└─ " : "├─ "}${JSON.stringify(lineText)}`);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
for (let i = 0; i < node.children.length; i++) {
|
|
201
|
-
const child = node.children[i];
|
|
202
|
-
const isChildLast = i === node.children.length - 1;
|
|
203
|
-
const childPrefix = prefix + (isLast ? " " : "│ ");
|
|
204
|
-
lines.push(...printLayoutToString(child, childPrefix, isChildLast, depth + 1));
|
|
205
|
-
}
|
|
206
|
-
return lines;
|
|
207
|
-
}
|
|
208
|
-
/**
|
|
209
|
-
* 打印 LayoutNode 树结构到控制台
|
|
210
|
-
*/
|
|
211
|
-
function printLayout(node) {
|
|
212
|
-
const lines = printLayoutToString(node, "", true);
|
|
213
|
-
console.log(lines.join("\n"));
|
|
214
|
-
}
|
|
215
|
-
/**
|
|
216
|
-
* 将 LayoutNode 转换为美观的字符串
|
|
217
|
-
* @param node LayoutNode 根节点
|
|
218
|
-
* @param indent 缩进字符串,默认为两个空格
|
|
219
|
-
* @returns 格式化的字符串
|
|
220
|
-
*/
|
|
221
|
-
function layoutToString(node, _indent = " ") {
|
|
222
|
-
return printLayoutToString(node, "", true).join("\n");
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
//#endregion
|
|
226
|
-
exports.Box = Box;
|
|
227
|
-
exports.CustomDraw = CustomDraw;
|
|
228
|
-
exports.Image = Image;
|
|
229
|
-
exports.RichText = RichText;
|
|
230
|
-
exports.Stack = Stack;
|
|
231
|
-
exports.Svg = Svg;
|
|
232
|
-
exports.Text = Text;
|
|
233
|
-
exports.Transform = Transform;
|
|
234
|
-
exports.computeLayout = require_render.computeLayout;
|
|
235
|
-
exports.createCanvas = createCanvas;
|
|
236
|
-
exports.createCanvasMeasureContext = require_render.createCanvasMeasureContext;
|
|
237
|
-
exports.layoutToString = layoutToString;
|
|
238
|
-
exports.linearGradient = require_render.linearGradient;
|
|
239
|
-
exports.printLayout = printLayout;
|
|
240
|
-
exports.radialGradient = require_render.radialGradient;
|
|
241
|
-
exports.svg = svg;
|