@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
|
@@ -398,4 +398,55 @@ interface DrawCallCanvas {
|
|
|
398
398
|
*/
|
|
399
399
|
declare function createCanvas(options: CanvasOptions): DrawCallCanvas;
|
|
400
400
|
//#endregion
|
|
401
|
-
|
|
401
|
+
//#region src/components/Box.d.ts
|
|
402
|
+
declare function Box(props: BoxProps): BoxElement;
|
|
403
|
+
//#endregion
|
|
404
|
+
//#region src/components/CustomDraw.d.ts
|
|
405
|
+
declare function CustomDraw(props: CustomDrawProps): CustomDrawElement;
|
|
406
|
+
//#endregion
|
|
407
|
+
//#region src/components/Image.d.ts
|
|
408
|
+
declare function Image(props: ImageProps): ImageElement;
|
|
409
|
+
//#endregion
|
|
410
|
+
//#region src/components/RichText.d.ts
|
|
411
|
+
declare function RichText(props: RichTextProps): RichTextElement;
|
|
412
|
+
//#endregion
|
|
413
|
+
//#region src/components/Stack.d.ts
|
|
414
|
+
declare function Stack(props: StackProps): StackElement;
|
|
415
|
+
//#endregion
|
|
416
|
+
//#region src/components/Svg.d.ts
|
|
417
|
+
declare function Svg(props: SvgProps): SvgElement;
|
|
418
|
+
declare const svg: {
|
|
419
|
+
rect: (props: Omit<SvgRectChild, "type">) => SvgRectChild;
|
|
420
|
+
circle: (props: Omit<SvgCircleChild, "type">) => SvgCircleChild;
|
|
421
|
+
ellipse: (props: Omit<SvgEllipseChild, "type">) => SvgEllipseChild;
|
|
422
|
+
line: (props: Omit<SvgLineChild, "type">) => SvgLineChild;
|
|
423
|
+
polyline: (props: Omit<SvgPolylineChild, "type">) => SvgPolylineChild;
|
|
424
|
+
polygon: (props: Omit<SvgPolygonChild, "type">) => SvgPolygonChild;
|
|
425
|
+
path: (props: Omit<SvgPathChild, "type">) => SvgPathChild;
|
|
426
|
+
text: (props: Omit<SvgTextChild, "type">) => SvgTextChild;
|
|
427
|
+
g: (props: Omit<SvgGroupChild, "type">) => SvgGroupChild;
|
|
428
|
+
};
|
|
429
|
+
//#endregion
|
|
430
|
+
//#region src/components/Text.d.ts
|
|
431
|
+
declare function Text(props: TextProps): TextElement;
|
|
432
|
+
//#endregion
|
|
433
|
+
//#region src/components/Transform.d.ts
|
|
434
|
+
declare function Transform(props: TransformProps): TransformElement;
|
|
435
|
+
//#endregion
|
|
436
|
+
//#region src/layout/engine.d.ts
|
|
437
|
+
declare function computeLayout(element: Element, ctx: MeasureContext, constraints: LayoutConstraints, x?: number, y?: number): LayoutNode;
|
|
438
|
+
//#endregion
|
|
439
|
+
//#region src/layout/utils/print.d.ts
|
|
440
|
+
/**
|
|
441
|
+
* 打印 LayoutNode 树结构到控制台
|
|
442
|
+
*/
|
|
443
|
+
declare function printLayout(node: LayoutNode): void;
|
|
444
|
+
/**
|
|
445
|
+
* 将 LayoutNode 转换为美观的字符串
|
|
446
|
+
* @param node LayoutNode 根节点
|
|
447
|
+
* @param indent 缩进字符串,默认为两个空格
|
|
448
|
+
* @returns 格式化的字符串
|
|
449
|
+
*/
|
|
450
|
+
declare function layoutToString(node: LayoutNode, _indent?: string): string;
|
|
451
|
+
//#endregion
|
|
452
|
+
export { type AlignItems, type AlignSelf, type Border, type Bounds, Box, type BoxElement, type BoxProps, type CanvasOptions, type Color, type ColorStop, type ContainerLayoutProps, CustomDraw, type CustomDrawElement, type CustomDrawProps, type DrawCallCanvas, type Element, type FlexDirection, type FontProps, type GradientDescriptor, Image, type JustifyContent, type LayoutNode, type LayoutProps, type LayoutSize, type LinearGradientDescriptor, type MeasureContext, type ProxiedCanvasContextOptions, type RadialGradientDescriptor, RichText, type RichTextElement, type RichTextProps, type RichTextSpan, type Shadow, type Size, type Spacing, Stack, type StackAlign, type StackElement, type StackProps, type StrokeProps, Svg, type SvgAlign, type SvgChild, type SvgCircleChild, type SvgElement, type SvgEllipseChild, type SvgGroupChild, type SvgLineChild, type SvgPathChild, type SvgPolygonChild, type SvgPolylineChild, type SvgProps, type SvgRectChild, type SvgStyleProps, type SvgTextChild, type SvgTransformProps, Text, type TextElement, type TextProps, Transform, type TransformElement, type TransformProps, computeLayout, createCanvas, createCanvasMeasureContext, layoutToString, linearGradient, printLayout, radialGradient, svg };
|
|
@@ -398,4 +398,55 @@ interface DrawCallCanvas {
|
|
|
398
398
|
*/
|
|
399
399
|
declare function createCanvas(options: CanvasOptions): DrawCallCanvas;
|
|
400
400
|
//#endregion
|
|
401
|
-
|
|
401
|
+
//#region src/components/Box.d.ts
|
|
402
|
+
declare function Box(props: BoxProps): BoxElement;
|
|
403
|
+
//#endregion
|
|
404
|
+
//#region src/components/CustomDraw.d.ts
|
|
405
|
+
declare function CustomDraw(props: CustomDrawProps): CustomDrawElement;
|
|
406
|
+
//#endregion
|
|
407
|
+
//#region src/components/Image.d.ts
|
|
408
|
+
declare function Image(props: ImageProps): ImageElement;
|
|
409
|
+
//#endregion
|
|
410
|
+
//#region src/components/RichText.d.ts
|
|
411
|
+
declare function RichText(props: RichTextProps): RichTextElement;
|
|
412
|
+
//#endregion
|
|
413
|
+
//#region src/components/Stack.d.ts
|
|
414
|
+
declare function Stack(props: StackProps): StackElement;
|
|
415
|
+
//#endregion
|
|
416
|
+
//#region src/components/Svg.d.ts
|
|
417
|
+
declare function Svg(props: SvgProps): SvgElement;
|
|
418
|
+
declare const svg: {
|
|
419
|
+
rect: (props: Omit<SvgRectChild, "type">) => SvgRectChild;
|
|
420
|
+
circle: (props: Omit<SvgCircleChild, "type">) => SvgCircleChild;
|
|
421
|
+
ellipse: (props: Omit<SvgEllipseChild, "type">) => SvgEllipseChild;
|
|
422
|
+
line: (props: Omit<SvgLineChild, "type">) => SvgLineChild;
|
|
423
|
+
polyline: (props: Omit<SvgPolylineChild, "type">) => SvgPolylineChild;
|
|
424
|
+
polygon: (props: Omit<SvgPolygonChild, "type">) => SvgPolygonChild;
|
|
425
|
+
path: (props: Omit<SvgPathChild, "type">) => SvgPathChild;
|
|
426
|
+
text: (props: Omit<SvgTextChild, "type">) => SvgTextChild;
|
|
427
|
+
g: (props: Omit<SvgGroupChild, "type">) => SvgGroupChild;
|
|
428
|
+
};
|
|
429
|
+
//#endregion
|
|
430
|
+
//#region src/components/Text.d.ts
|
|
431
|
+
declare function Text(props: TextProps): TextElement;
|
|
432
|
+
//#endregion
|
|
433
|
+
//#region src/components/Transform.d.ts
|
|
434
|
+
declare function Transform(props: TransformProps): TransformElement;
|
|
435
|
+
//#endregion
|
|
436
|
+
//#region src/layout/engine.d.ts
|
|
437
|
+
declare function computeLayout(element: Element, ctx: MeasureContext, constraints: LayoutConstraints, x?: number, y?: number): LayoutNode;
|
|
438
|
+
//#endregion
|
|
439
|
+
//#region src/layout/utils/print.d.ts
|
|
440
|
+
/**
|
|
441
|
+
* 打印 LayoutNode 树结构到控制台
|
|
442
|
+
*/
|
|
443
|
+
declare function printLayout(node: LayoutNode): void;
|
|
444
|
+
/**
|
|
445
|
+
* 将 LayoutNode 转换为美观的字符串
|
|
446
|
+
* @param node LayoutNode 根节点
|
|
447
|
+
* @param indent 缩进字符串,默认为两个空格
|
|
448
|
+
* @returns 格式化的字符串
|
|
449
|
+
*/
|
|
450
|
+
declare function layoutToString(node: LayoutNode, _indent?: string): string;
|
|
451
|
+
//#endregion
|
|
452
|
+
export { type AlignItems, type AlignSelf, type Border, type Bounds, Box, type BoxElement, type BoxProps, type CanvasOptions, type Color, type ColorStop, type ContainerLayoutProps, CustomDraw, type CustomDrawElement, type CustomDrawProps, type DrawCallCanvas, type Element, type FlexDirection, type FontProps, type GradientDescriptor, Image, type JustifyContent, type LayoutNode, type LayoutProps, type LayoutSize, type LinearGradientDescriptor, type MeasureContext, type ProxiedCanvasContextOptions, type RadialGradientDescriptor, RichText, type RichTextElement, type RichTextProps, type RichTextSpan, type Shadow, type Size, type Spacing, Stack, type StackAlign, type StackElement, type StackProps, type StrokeProps, Svg, type SvgAlign, type SvgChild, type SvgCircleChild, type SvgElement, type SvgEllipseChild, type SvgGroupChild, type SvgLineChild, type SvgPathChild, type SvgPolygonChild, type SvgPolylineChild, type SvgProps, type SvgRectChild, type SvgStyleProps, type SvgTextChild, type SvgTransformProps, Text, type TextElement, type TextProps, Transform, type TransformElement, type TransformProps, computeLayout, createCanvas, createCanvasMeasureContext, layoutToString, linearGradient, printLayout, radialGradient, svg };
|
|
@@ -1,4 +1,14 @@
|
|
|
1
|
+
//#region src/compat/index.ts
|
|
2
|
+
const DOMMatrix = window.DOMMatrix;
|
|
3
|
+
const Path2D = window.Path2D;
|
|
4
|
+
function createRawCanvas(width, height) {
|
|
5
|
+
const canvas = document.createElement("canvas");
|
|
6
|
+
canvas.width = width;
|
|
7
|
+
canvas.height = height;
|
|
8
|
+
return canvas;
|
|
9
|
+
}
|
|
1
10
|
|
|
11
|
+
//#endregion
|
|
2
12
|
//#region src/types/base.ts
|
|
3
13
|
function linearGradient(angle, ...stops) {
|
|
4
14
|
return {
|
|
@@ -1106,17 +1116,6 @@ function renderBox(ctx, node) {
|
|
|
1106
1116
|
if (element.opacity !== void 0 && element.opacity < 1) ctx.globalAlpha = 1;
|
|
1107
1117
|
}
|
|
1108
1118
|
|
|
1109
|
-
//#endregion
|
|
1110
|
-
//#region src/compat/DOMMatrix.ts
|
|
1111
|
-
const DOMMatrixCompat = (() => {
|
|
1112
|
-
if (typeof DOMMatrix !== "undefined") return DOMMatrix;
|
|
1113
|
-
try {
|
|
1114
|
-
return require("@napi-rs/canvas").DOMMatrix;
|
|
1115
|
-
} catch {
|
|
1116
|
-
throw new Error("DOMMatrix is not available. In Node.js, install @napi-rs/canvas.");
|
|
1117
|
-
}
|
|
1118
|
-
})();
|
|
1119
|
-
|
|
1120
1119
|
//#endregion
|
|
1121
1120
|
//#region src/render/components/ProxiedCanvasContext.ts
|
|
1122
1121
|
/**
|
|
@@ -1153,7 +1152,7 @@ var ProxiedCanvasContext = class {
|
|
|
1153
1152
|
constructor(ctx, baseTransform) {
|
|
1154
1153
|
this.ctx = ctx;
|
|
1155
1154
|
this.baseTransform = baseTransform;
|
|
1156
|
-
this.relativeTransform = new
|
|
1155
|
+
this.relativeTransform = new DOMMatrix();
|
|
1157
1156
|
}
|
|
1158
1157
|
/**
|
|
1159
1158
|
* save() - 保存当前状态并增加计数
|
|
@@ -1176,8 +1175,8 @@ var ProxiedCanvasContext = class {
|
|
|
1176
1175
|
*/
|
|
1177
1176
|
setTransform(...args) {
|
|
1178
1177
|
let matrix;
|
|
1179
|
-
if (args.length === 1 && args[0] instanceof
|
|
1180
|
-
else if (args.length === 6) matrix = new
|
|
1178
|
+
if (args.length === 1 && args[0] instanceof DOMMatrix) matrix = args[0];
|
|
1179
|
+
else if (args.length === 6) matrix = new DOMMatrix([
|
|
1181
1180
|
args[0],
|
|
1182
1181
|
args[1],
|
|
1183
1182
|
args[2],
|
|
@@ -1400,17 +1399,6 @@ function renderRichText(ctx, node) {
|
|
|
1400
1399
|
}
|
|
1401
1400
|
}
|
|
1402
1401
|
|
|
1403
|
-
//#endregion
|
|
1404
|
-
//#region src/compat/Path2D.ts
|
|
1405
|
-
const Path2DCompat = (() => {
|
|
1406
|
-
if (typeof Path2D !== "undefined") return Path2D;
|
|
1407
|
-
try {
|
|
1408
|
-
return require("@napi-rs/canvas").Path2D;
|
|
1409
|
-
} catch {
|
|
1410
|
-
throw new Error("Path2D is not available. In Node.js, install @napi-rs/canvas.");
|
|
1411
|
-
}
|
|
1412
|
-
})();
|
|
1413
|
-
|
|
1414
1402
|
//#endregion
|
|
1415
1403
|
//#region src/render/components/svg.ts
|
|
1416
1404
|
function isGradientDescriptor(color) {
|
|
@@ -1448,7 +1436,7 @@ function resolveColor(ctx, color, x, y, width, height) {
|
|
|
1448
1436
|
}
|
|
1449
1437
|
function applyTransform(base, transform) {
|
|
1450
1438
|
if (!transform) return base;
|
|
1451
|
-
let result = new
|
|
1439
|
+
let result = new DOMMatrix([
|
|
1452
1440
|
base.a,
|
|
1453
1441
|
base.b,
|
|
1454
1442
|
base.c,
|
|
@@ -1458,7 +1446,7 @@ function applyTransform(base, transform) {
|
|
|
1458
1446
|
]);
|
|
1459
1447
|
if (transform.matrix) {
|
|
1460
1448
|
const [a, b, c, d, e, f] = transform.matrix;
|
|
1461
|
-
result = result.multiply(new
|
|
1449
|
+
result = result.multiply(new DOMMatrix([
|
|
1462
1450
|
a,
|
|
1463
1451
|
b,
|
|
1464
1452
|
c,
|
|
@@ -1547,7 +1535,7 @@ function renderSvgPolygon(ctx, polygon, bounds) {
|
|
|
1547
1535
|
applyFillAndStroke(ctx, polygon, bounds);
|
|
1548
1536
|
}
|
|
1549
1537
|
function renderSvgPath(ctx, path, bounds) {
|
|
1550
|
-
const path2d = new
|
|
1538
|
+
const path2d = new Path2D(path.d);
|
|
1551
1539
|
if (path.fill && path.fill !== "none") {
|
|
1552
1540
|
ctx.fillStyle = resolveColor(ctx, path.fill, bounds.x, bounds.y, bounds.width, bounds.height);
|
|
1553
1541
|
ctx.fill(path2d);
|
|
@@ -1629,7 +1617,7 @@ function calculateViewBoxTransform(x, y, width, height, viewBox, preserveAspectR
|
|
|
1629
1617
|
const scaleY = height / vbHeight;
|
|
1630
1618
|
const align = preserveAspectRatio?.align ?? "xMidYMid";
|
|
1631
1619
|
const meetOrSlice = preserveAspectRatio?.meetOrSlice ?? "meet";
|
|
1632
|
-
if (align === "none") return new
|
|
1620
|
+
if (align === "none") return new DOMMatrix().translate(x, y).scale(scaleX, scaleY).translate(-vbX, -vbY);
|
|
1633
1621
|
const scale = meetOrSlice === "meet" ? Math.min(scaleX, scaleY) : Math.max(scaleX, scaleY);
|
|
1634
1622
|
const scaledWidth = vbWidth * scale;
|
|
1635
1623
|
const scaledHeight = vbHeight * scale;
|
|
@@ -1639,7 +1627,7 @@ function calculateViewBoxTransform(x, y, width, height, viewBox, preserveAspectR
|
|
|
1639
1627
|
else if (align.includes("xMax")) translateX += width - scaledWidth;
|
|
1640
1628
|
if (align.includes("YMid")) translateY += (height - scaledHeight) / 2;
|
|
1641
1629
|
else if (align.includes("YMax")) translateY += height - scaledHeight;
|
|
1642
|
-
return new
|
|
1630
|
+
return new DOMMatrix().translate(translateX, translateY).scale(scale, scale).translate(-vbX, -vbY);
|
|
1643
1631
|
}
|
|
1644
1632
|
function applyShadow(ctx, shadow) {
|
|
1645
1633
|
ctx.shadowOffsetX = shadow.offsetX ?? 0;
|
|
@@ -1728,13 +1716,13 @@ function renderText(ctx, node) {
|
|
|
1728
1716
|
* - 简易对象: { translate, rotate, scale, skewX, skewY }
|
|
1729
1717
|
*/
|
|
1730
1718
|
function parseTransformValue(transform) {
|
|
1731
|
-
if (transform === void 0) return new
|
|
1732
|
-
if (Array.isArray(transform)) return new
|
|
1719
|
+
if (transform === void 0) return new DOMMatrix();
|
|
1720
|
+
if (Array.isArray(transform)) return new DOMMatrix(transform);
|
|
1733
1721
|
const hasDOMMatrixInit = "a" in transform || "b" in transform || "c" in transform || "d" in transform || "e" in transform || "f" in transform;
|
|
1734
1722
|
const hasSimpleTransform = "translate" in transform || "rotate" in transform || "scale" in transform || "skewX" in transform || "skewY" in transform;
|
|
1735
1723
|
if (hasDOMMatrixInit && !hasSimpleTransform) {
|
|
1736
1724
|
const init = transform;
|
|
1737
|
-
return new
|
|
1725
|
+
return new DOMMatrix([
|
|
1738
1726
|
init.a ?? 1,
|
|
1739
1727
|
init.b ?? 0,
|
|
1740
1728
|
init.c ?? 0,
|
|
@@ -1744,7 +1732,7 @@ function parseTransformValue(transform) {
|
|
|
1744
1732
|
]);
|
|
1745
1733
|
}
|
|
1746
1734
|
const simpleObj = transform;
|
|
1747
|
-
let result = new
|
|
1735
|
+
let result = new DOMMatrix();
|
|
1748
1736
|
if (simpleObj.translate) result = result.translate(simpleObj.translate[0], simpleObj.translate[1]);
|
|
1749
1737
|
if (simpleObj.rotate !== void 0) if (typeof simpleObj.rotate === "number") result = result.rotate(simpleObj.rotate);
|
|
1750
1738
|
else {
|
|
@@ -1786,7 +1774,7 @@ function renderTransform(ctx, node) {
|
|
|
1786
1774
|
const ox = childNode.layout.x + relativeOx;
|
|
1787
1775
|
const oy = childNode.layout.y + relativeOy;
|
|
1788
1776
|
const targetMatrix = parseTransformValue(element.transform);
|
|
1789
|
-
const finalMatrix = new
|
|
1777
|
+
const finalMatrix = new DOMMatrix().translate(ox, oy).multiply(targetMatrix).translate(-ox, -oy);
|
|
1790
1778
|
ctx.save();
|
|
1791
1779
|
const composedTransform = ctx.getTransform().multiply(finalMatrix);
|
|
1792
1780
|
ctx.setTransform(composedTransform);
|
|
@@ -1835,33 +1823,220 @@ function renderNode(ctx, node) {
|
|
|
1835
1823
|
}
|
|
1836
1824
|
|
|
1837
1825
|
//#endregion
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
}
|
|
1826
|
+
//#region src/canvas.ts
|
|
1827
|
+
/**
|
|
1828
|
+
* 创建适用于浏览器环境的 Canvas
|
|
1829
|
+
*
|
|
1830
|
+
* 在浏览器环境中使用,支持传入已有的 canvas 实例
|
|
1831
|
+
*/
|
|
1832
|
+
function createCanvas(options) {
|
|
1833
|
+
const { width, height, pixelRatio = 1 } = options;
|
|
1834
|
+
const canvas = options.canvas ?? createRawCanvas(width * pixelRatio, height * pixelRatio);
|
|
1835
|
+
const ctx = canvas.getContext("2d");
|
|
1836
|
+
if (!ctx) throw new Error("Failed to get 2d context");
|
|
1837
|
+
if (options.imageSmoothingEnabled !== void 0) ctx.imageSmoothingEnabled = options.imageSmoothingEnabled;
|
|
1838
|
+
if (options.imageSmoothingQuality !== void 0) ctx.imageSmoothingQuality = options.imageSmoothingQuality;
|
|
1839
|
+
if (pixelRatio !== 1) ctx.scale(pixelRatio, pixelRatio);
|
|
1840
|
+
const measureCtx = createCanvasMeasureContext(ctx);
|
|
1841
|
+
return {
|
|
1842
|
+
width,
|
|
1843
|
+
height,
|
|
1844
|
+
pixelRatio,
|
|
1845
|
+
canvas,
|
|
1846
|
+
render(element) {
|
|
1847
|
+
const layoutTree = computeLayout(element, measureCtx, {
|
|
1848
|
+
minWidth: 0,
|
|
1849
|
+
maxWidth: width,
|
|
1850
|
+
minHeight: 0,
|
|
1851
|
+
maxHeight: height
|
|
1852
|
+
});
|
|
1853
|
+
renderNode(ctx, layoutTree);
|
|
1854
|
+
return layoutTree;
|
|
1855
|
+
},
|
|
1856
|
+
clear() {
|
|
1857
|
+
ctx.clearRect(0, 0, width, height);
|
|
1858
|
+
},
|
|
1859
|
+
getContext() {
|
|
1860
|
+
return ctx;
|
|
1861
|
+
},
|
|
1862
|
+
toDataURL(type, quality) {
|
|
1863
|
+
if ("toDataURL" in canvas && typeof canvas.toDataURL === "function") return canvas.toDataURL(type, quality);
|
|
1864
|
+
throw new Error("toDataURL not supported");
|
|
1865
|
+
},
|
|
1866
|
+
toBuffer(type = "image/png") {
|
|
1867
|
+
if ("toBuffer" in canvas && typeof canvas.toBuffer === "function") return canvas.toBuffer(type);
|
|
1868
|
+
throw new Error("toBuffer not supported in this environment");
|
|
1869
|
+
}
|
|
1870
|
+
};
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
//#endregion
|
|
1874
|
+
//#region src/components/Box.ts
|
|
1875
|
+
function Box(props) {
|
|
1876
|
+
return {
|
|
1877
|
+
type: "box",
|
|
1878
|
+
...props
|
|
1879
|
+
};
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
//#endregion
|
|
1883
|
+
//#region src/components/CustomDraw.ts
|
|
1884
|
+
function CustomDraw(props) {
|
|
1885
|
+
return {
|
|
1886
|
+
type: "customdraw",
|
|
1887
|
+
...props
|
|
1888
|
+
};
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
//#endregion
|
|
1892
|
+
//#region src/components/Image.ts
|
|
1893
|
+
function Image(props) {
|
|
1894
|
+
return {
|
|
1895
|
+
type: "image",
|
|
1896
|
+
...props
|
|
1897
|
+
};
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
//#endregion
|
|
1901
|
+
//#region src/components/RichText.ts
|
|
1902
|
+
function RichText(props) {
|
|
1903
|
+
return {
|
|
1904
|
+
type: "richtext",
|
|
1905
|
+
...props
|
|
1906
|
+
};
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
//#endregion
|
|
1910
|
+
//#region src/components/Stack.ts
|
|
1911
|
+
function Stack(props) {
|
|
1912
|
+
return {
|
|
1913
|
+
type: "stack",
|
|
1914
|
+
...props
|
|
1915
|
+
};
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
//#endregion
|
|
1919
|
+
//#region src/components/Svg.ts
|
|
1920
|
+
function Svg(props) {
|
|
1921
|
+
return {
|
|
1922
|
+
type: "svg",
|
|
1923
|
+
...props
|
|
1924
|
+
};
|
|
1925
|
+
}
|
|
1926
|
+
const svg = {
|
|
1927
|
+
rect: (props) => ({
|
|
1928
|
+
type: "rect",
|
|
1929
|
+
...props
|
|
1930
|
+
}),
|
|
1931
|
+
circle: (props) => ({
|
|
1932
|
+
type: "circle",
|
|
1933
|
+
...props
|
|
1934
|
+
}),
|
|
1935
|
+
ellipse: (props) => ({
|
|
1936
|
+
type: "ellipse",
|
|
1937
|
+
...props
|
|
1938
|
+
}),
|
|
1939
|
+
line: (props) => ({
|
|
1940
|
+
type: "line",
|
|
1941
|
+
...props
|
|
1942
|
+
}),
|
|
1943
|
+
polyline: (props) => ({
|
|
1944
|
+
type: "polyline",
|
|
1945
|
+
...props
|
|
1946
|
+
}),
|
|
1947
|
+
polygon: (props) => ({
|
|
1948
|
+
type: "polygon",
|
|
1949
|
+
...props
|
|
1950
|
+
}),
|
|
1951
|
+
path: (props) => ({
|
|
1952
|
+
type: "path",
|
|
1953
|
+
...props
|
|
1954
|
+
}),
|
|
1955
|
+
text: (props) => ({
|
|
1956
|
+
type: "text",
|
|
1957
|
+
...props
|
|
1958
|
+
}),
|
|
1959
|
+
g: (props) => ({
|
|
1960
|
+
type: "g",
|
|
1961
|
+
...props
|
|
1962
|
+
})
|
|
1963
|
+
};
|
|
1964
|
+
|
|
1965
|
+
//#endregion
|
|
1966
|
+
//#region src/components/Text.ts
|
|
1967
|
+
function Text(props) {
|
|
1968
|
+
return {
|
|
1969
|
+
type: "text",
|
|
1970
|
+
...props
|
|
1971
|
+
};
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
//#endregion
|
|
1975
|
+
//#region src/components/Transform.ts
|
|
1976
|
+
function Transform(props) {
|
|
1977
|
+
return {
|
|
1978
|
+
type: "transform",
|
|
1979
|
+
...props
|
|
1980
|
+
};
|
|
1981
|
+
}
|
|
1982
|
+
|
|
1983
|
+
//#endregion
|
|
1984
|
+
//#region src/layout/utils/print.ts
|
|
1985
|
+
/**
|
|
1986
|
+
* 获取元素类型的显示名称
|
|
1987
|
+
*/
|
|
1988
|
+
function getElementType(element) {
|
|
1989
|
+
switch (element.type) {
|
|
1990
|
+
case "box": return "Box";
|
|
1991
|
+
case "text": return `Text "${element.content.slice(0, 20)}${element.content.length > 20 ? "..." : ""}"`;
|
|
1992
|
+
case "stack": return "Stack";
|
|
1993
|
+
case "image": return "Image";
|
|
1994
|
+
case "svg": return "Svg";
|
|
1995
|
+
default: return element.type;
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
/**
|
|
1999
|
+
* 递归打印布局树
|
|
2000
|
+
*/
|
|
2001
|
+
function printLayoutToString(node, prefix = "", isLast = true, depth = 0) {
|
|
2002
|
+
const lines = [];
|
|
2003
|
+
const connector = isLast ? "└─ " : "├─ ";
|
|
2004
|
+
const type = getElementType(node.element);
|
|
2005
|
+
const { x, y, width, height } = node.layout;
|
|
2006
|
+
const childCount = node.children.length;
|
|
2007
|
+
lines.push(`${prefix}${connector}${type} @(${Math.round(x)},${Math.round(y)}) size:${Math.round(width)}x${Math.round(height)}`);
|
|
2008
|
+
if (node.element.type === "text" && node.lines) {
|
|
2009
|
+
const contentPrefix = prefix + (isLast ? " " : "│ ");
|
|
2010
|
+
for (let i = 0; i < node.lines.length; i++) {
|
|
2011
|
+
const lineText = node.lines[i];
|
|
2012
|
+
const isLastLine = i === node.lines.length - 1 && childCount === 0;
|
|
2013
|
+
lines.push(`${contentPrefix}${isLastLine ? "└─ " : "├─ "}${JSON.stringify(lineText)}`);
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
2017
|
+
const child = node.children[i];
|
|
2018
|
+
const isChildLast = i === node.children.length - 1;
|
|
2019
|
+
const childPrefix = prefix + (isLast ? " " : "│ ");
|
|
2020
|
+
lines.push(...printLayoutToString(child, childPrefix, isChildLast, depth + 1));
|
|
2021
|
+
}
|
|
2022
|
+
return lines;
|
|
2023
|
+
}
|
|
2024
|
+
/**
|
|
2025
|
+
* 打印 LayoutNode 树结构到控制台
|
|
2026
|
+
*/
|
|
2027
|
+
function printLayout(node) {
|
|
2028
|
+
const lines = printLayoutToString(node, "", true);
|
|
2029
|
+
console.log(lines.join("\n"));
|
|
2030
|
+
}
|
|
2031
|
+
/**
|
|
2032
|
+
* 将 LayoutNode 转换为美观的字符串
|
|
2033
|
+
* @param node LayoutNode 根节点
|
|
2034
|
+
* @param indent 缩进字符串,默认为两个空格
|
|
2035
|
+
* @returns 格式化的字符串
|
|
2036
|
+
*/
|
|
2037
|
+
function layoutToString(node, _indent = " ") {
|
|
2038
|
+
return printLayoutToString(node, "", true).join("\n");
|
|
2039
|
+
}
|
|
2040
|
+
|
|
2041
|
+
//#endregion
|
|
2042
|
+
export { Box, CustomDraw, Image, RichText, Stack, Svg, Text, Transform, computeLayout, createCanvas, createCanvasMeasureContext, layoutToString, linearGradient, printLayout, radialGradient, svg };
|
package/examples/card.ts
CHANGED
|
@@ -2,14 +2,13 @@
|
|
|
2
2
|
* 示例:使用 draw-call 绘制一个卡片
|
|
3
3
|
* 运行: bun examples/card.ts
|
|
4
4
|
*/
|
|
5
|
-
import { Box, linearGradient, printLayout, Svg, svg, Text } from "@codehz/draw-call";
|
|
6
|
-
import { createNodeCanvas } from "@codehz/draw-call/node";
|
|
5
|
+
import { Box, createCanvas, linearGradient, printLayout, Svg, svg, Text } from "@codehz/draw-call";
|
|
7
6
|
import { GlobalFonts } from "@napi-rs/canvas";
|
|
8
7
|
import { fileURLToPath } from "bun";
|
|
9
8
|
|
|
10
9
|
GlobalFonts.registerFromPath(fileURLToPath(import.meta.resolve("@fontpkg/unifont/unifont-15.0.01.ttf")), "unifont");
|
|
11
10
|
|
|
12
|
-
const canvas =
|
|
11
|
+
const canvas = createCanvas({
|
|
13
12
|
width: 400,
|
|
14
13
|
height: 320,
|
|
15
14
|
pixelRatio: 2,
|
|
@@ -3,14 +3,13 @@
|
|
|
3
3
|
* 展示如何使用 CustomDraw 进行简单的自定义绘制
|
|
4
4
|
* 运行: bun examples/customdraw-basic.ts
|
|
5
5
|
*/
|
|
6
|
-
import { Box, CustomDraw, printLayout, Text } from "@codehz/draw-call";
|
|
7
|
-
import { createNodeCanvas } from "@codehz/draw-call/node";
|
|
6
|
+
import { Box, createCanvas, CustomDraw, printLayout, Text } from "@codehz/draw-call";
|
|
8
7
|
import { GlobalFonts } from "@napi-rs/canvas";
|
|
9
8
|
import { fileURLToPath } from "bun";
|
|
10
9
|
|
|
11
10
|
GlobalFonts.registerFromPath(fileURLToPath(import.meta.resolve("@fontpkg/unifont/unifont-15.0.01.ttf")), "unifont");
|
|
12
11
|
|
|
13
|
-
const canvas =
|
|
12
|
+
const canvas = createCanvas({
|
|
14
13
|
width: 600,
|
|
15
14
|
height: 1000,
|
|
16
15
|
pixelRatio: 2,
|
package/examples/customdraw.ts
CHANGED
|
@@ -3,14 +3,13 @@
|
|
|
3
3
|
* 展示直接调用 Canvas API、Transform 管理和子元素渲染功能
|
|
4
4
|
* 运行: bun examples/customdraw.ts
|
|
5
5
|
*/
|
|
6
|
-
import { Box, CustomDraw, printLayout, Text } from "@codehz/draw-call";
|
|
7
|
-
import { createNodeCanvas } from "@codehz/draw-call/node";
|
|
6
|
+
import { Box, createCanvas, CustomDraw, printLayout, Text } from "@codehz/draw-call";
|
|
8
7
|
import { GlobalFonts } from "@napi-rs/canvas";
|
|
9
8
|
import { fileURLToPath } from "bun";
|
|
10
9
|
|
|
11
10
|
GlobalFonts.registerFromPath(fileURLToPath(import.meta.resolve("@fontpkg/unifont/unifont-15.0.01.ttf")), "unifont");
|
|
12
11
|
|
|
13
|
-
const canvas =
|
|
12
|
+
const canvas = createCanvas({
|
|
14
13
|
width: 800,
|
|
15
14
|
height: 1000,
|
|
16
15
|
pixelRatio: 2,
|
package/examples/richtext.ts
CHANGED
|
@@ -2,14 +2,13 @@
|
|
|
2
2
|
* 示例:使用 draw-call 的 RichText 组件
|
|
3
3
|
* 运行: bun examples/richtext.ts
|
|
4
4
|
*/
|
|
5
|
-
import { Box, RichText, Text } from "@codehz/draw-call";
|
|
6
|
-
import { createNodeCanvas } from "@codehz/draw-call/node";
|
|
5
|
+
import { Box, createCanvas, RichText, Text } from "@codehz/draw-call";
|
|
7
6
|
import { GlobalFonts } from "@napi-rs/canvas";
|
|
8
7
|
import { fileURLToPath } from "bun";
|
|
9
8
|
|
|
10
9
|
GlobalFonts.registerFromPath(fileURLToPath(import.meta.resolve("@fontpkg/unifont/unifont-15.0.01.ttf")), "unifont");
|
|
11
10
|
|
|
12
|
-
const canvas =
|
|
11
|
+
const canvas = createCanvas({
|
|
13
12
|
width: 400,
|
|
14
13
|
height: 820,
|
|
15
14
|
pixelRatio: 2,
|
package/examples/transform.ts
CHANGED
|
@@ -3,14 +3,13 @@
|
|
|
3
3
|
* 展示移动(translate)、旋转(rotate)、放大(scale)等变换效果
|
|
4
4
|
* 运行: bun examples/transform.ts
|
|
5
5
|
*/
|
|
6
|
-
import { Box, Text, Transform
|
|
7
|
-
import { createNodeCanvas } from "@codehz/draw-call/node";
|
|
6
|
+
import { Box, createCanvas, printLayout, Text, Transform } from "@codehz/draw-call";
|
|
8
7
|
import { GlobalFonts } from "@napi-rs/canvas";
|
|
9
8
|
import { fileURLToPath } from "bun";
|
|
10
9
|
|
|
11
10
|
GlobalFonts.registerFromPath(fileURLToPath(import.meta.resolve("@fontpkg/unifont/unifont-15.0.01.ttf")), "unifont");
|
|
12
11
|
|
|
13
|
-
const canvas =
|
|
12
|
+
const canvas = createCanvas({
|
|
14
13
|
width: 800,
|
|
15
14
|
height: 1000,
|
|
16
15
|
pixelRatio: 2,
|