@codehz/draw-call 0.4.5 → 0.5.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 +185 -104
- package/browser/index.d.cts +11 -9
- package/browser/index.d.ts +11 -9
- package/browser/index.js +185 -104
- package/examples/demo.ts +0 -5
- package/node/index.cjs +185 -104
- package/node/index.d.cts +11 -9
- package/node/index.d.mts +11 -9
- package/node/index.mjs +185 -104
- package/package.json +1 -1
package/browser/index.cjs
CHANGED
|
@@ -82,6 +82,19 @@ function normalizeBorderRadius(value) {
|
|
|
82
82
|
|
|
83
83
|
//#endregion
|
|
84
84
|
//#region src/layout/components/box.ts
|
|
85
|
+
/**
|
|
86
|
+
* 安全获取元素的 margin
|
|
87
|
+
* Transform 元素没有 margin,返回默认 spacing
|
|
88
|
+
*/
|
|
89
|
+
function getElementMargin$2(element) {
|
|
90
|
+
if (element.type === "transform") return {
|
|
91
|
+
top: 0,
|
|
92
|
+
right: 0,
|
|
93
|
+
bottom: 0,
|
|
94
|
+
left: 0
|
|
95
|
+
};
|
|
96
|
+
return normalizeSpacing(element.margin);
|
|
97
|
+
}
|
|
85
98
|
function calcEffectiveSize(element, padding, availableWidth) {
|
|
86
99
|
return {
|
|
87
100
|
width: typeof element.width === "number" ? element.width - padding.left - padding.right : availableWidth > 0 ? availableWidth : 0,
|
|
@@ -91,7 +104,7 @@ function calcEffectiveSize(element, padding, availableWidth) {
|
|
|
91
104
|
function collectChildSizes(children, ctx, availableWidth, padding, measureChild) {
|
|
92
105
|
const childSizes = [];
|
|
93
106
|
for (const child of children) {
|
|
94
|
-
const childMargin =
|
|
107
|
+
const childMargin = getElementMargin$2(child);
|
|
95
108
|
const childSize = measureChild(child, ctx, availableWidth - padding.left - padding.right - childMargin.left - childMargin.right);
|
|
96
109
|
childSizes.push({
|
|
97
110
|
width: childSize.width,
|
|
@@ -160,7 +173,7 @@ function measureBoxSize(element, ctx, availableWidth, measureChild) {
|
|
|
160
173
|
contentHeight = wrapped.height;
|
|
161
174
|
} else for (let i = 0; i < children.length; i++) {
|
|
162
175
|
const child = children[i];
|
|
163
|
-
const childMargin =
|
|
176
|
+
const childMargin = getElementMargin$2(child);
|
|
164
177
|
const childSize = measureChild(child, ctx, availableWidth - padding.left - padding.right - childMargin.left - childMargin.right);
|
|
165
178
|
if (isRow) {
|
|
166
179
|
contentWidth += childSize.width + childMargin.left + childMargin.right;
|
|
@@ -394,6 +407,19 @@ function wrapRichText(ctx, spans, maxWidth, lineHeightScale = 1.2, elementStyle
|
|
|
394
407
|
//#endregion
|
|
395
408
|
//#region src/layout/components/stack.ts
|
|
396
409
|
/**
|
|
410
|
+
* 安全获取元素的 margin
|
|
411
|
+
* Transform 元素没有 margin,返回默认 spacing
|
|
412
|
+
*/
|
|
413
|
+
function getElementMargin$1(element) {
|
|
414
|
+
if (element.type === "transform") return {
|
|
415
|
+
top: 0,
|
|
416
|
+
right: 0,
|
|
417
|
+
bottom: 0,
|
|
418
|
+
left: 0
|
|
419
|
+
};
|
|
420
|
+
return normalizeSpacing(element.margin);
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
397
423
|
* 测量 Stack 元素的固有尺寸
|
|
398
424
|
*/
|
|
399
425
|
function measureStackSize(element, ctx, availableWidth, measureChild) {
|
|
@@ -402,7 +428,7 @@ function measureStackSize(element, ctx, availableWidth, measureChild) {
|
|
|
402
428
|
let contentHeight = 0;
|
|
403
429
|
const children = element.children ?? [];
|
|
404
430
|
for (const child of children) {
|
|
405
|
-
const childMargin =
|
|
431
|
+
const childMargin = getElementMargin$1(child);
|
|
406
432
|
const childSize = measureChild(child, ctx, availableWidth - padding.left - padding.right - childMargin.left - childMargin.right);
|
|
407
433
|
contentWidth = Math.max(contentWidth, childSize.width + childMargin.left + childMargin.right);
|
|
408
434
|
contentHeight = Math.max(contentHeight, childSize.height + childMargin.top + childMargin.bottom);
|
|
@@ -641,18 +667,79 @@ function sizeNeedsParent(size) {
|
|
|
641
667
|
|
|
642
668
|
//#endregion
|
|
643
669
|
//#region src/layout/engine.ts
|
|
670
|
+
/**
|
|
671
|
+
* 类型守卫:检查 Element 是否为 LayoutElement(非 Transform)
|
|
672
|
+
* 由于 Transform 元素在 computeLayoutImpl 开始时被处理,
|
|
673
|
+
* 此时只应处理 LayoutElement
|
|
674
|
+
*/
|
|
675
|
+
function assertLayoutElement(element) {
|
|
676
|
+
if (element.type === "transform") throw new Error("Transform elements should be handled at entry point");
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* 安全获取元素的 margin
|
|
680
|
+
* Transform 元素没有 margin,返回默认 spacing
|
|
681
|
+
*/
|
|
682
|
+
function getElementMargin(element) {
|
|
683
|
+
if (element.type === "transform") return {
|
|
684
|
+
top: 0,
|
|
685
|
+
right: 0,
|
|
686
|
+
bottom: 0,
|
|
687
|
+
left: 0
|
|
688
|
+
};
|
|
689
|
+
return normalizeSpacing(element.margin);
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* 安全获取元素的布局属性(width, height, flex等)
|
|
693
|
+
* Transform 元素这些属性为 undefined
|
|
694
|
+
*/
|
|
695
|
+
function getElementLayoutProps(element) {
|
|
696
|
+
if (element.type === "transform") return {
|
|
697
|
+
width: void 0,
|
|
698
|
+
height: void 0,
|
|
699
|
+
flex: void 0,
|
|
700
|
+
minWidth: void 0,
|
|
701
|
+
maxWidth: void 0,
|
|
702
|
+
minHeight: void 0,
|
|
703
|
+
maxHeight: void 0,
|
|
704
|
+
alignSelf: void 0
|
|
705
|
+
};
|
|
706
|
+
const le = element;
|
|
707
|
+
return {
|
|
708
|
+
width: le.width,
|
|
709
|
+
height: le.height,
|
|
710
|
+
flex: le.flex,
|
|
711
|
+
minWidth: le.minWidth,
|
|
712
|
+
maxWidth: le.maxWidth,
|
|
713
|
+
minHeight: le.minHeight,
|
|
714
|
+
maxHeight: le.maxHeight,
|
|
715
|
+
alignSelf: le.alignSelf
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* 布局计算主函数
|
|
720
|
+
* 内部使用 Element 类型以支持 Transform,外部通过 LayoutElement 约束类型
|
|
721
|
+
*/
|
|
644
722
|
function computeLayout(element, ctx, constraints, x = 0, y = 0) {
|
|
645
|
-
|
|
646
|
-
|
|
723
|
+
return computeLayoutImpl(element, ctx, constraints, x, y);
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* 内部实现函数,处理所有元素类型包括 Transform
|
|
727
|
+
*/
|
|
728
|
+
function computeLayoutImpl(element, ctx, constraints, x = 0, y = 0) {
|
|
729
|
+
if (element.type === "transform") return computeLayoutImpl(element.children, ctx, constraints, x, y);
|
|
730
|
+
assertLayoutElement(element);
|
|
731
|
+
const layoutElement = element;
|
|
732
|
+
const margin = normalizeSpacing(layoutElement.margin);
|
|
733
|
+
const padding = normalizeSpacing("padding" in layoutElement ? layoutElement.padding : void 0);
|
|
647
734
|
const availableWidth = constraints.maxWidth - margin.left - margin.right;
|
|
648
735
|
const availableHeight = constraints.maxHeight - margin.top - margin.bottom;
|
|
649
|
-
const intrinsic = measureIntrinsicSize(
|
|
650
|
-
let width = constraints.minWidth === constraints.maxWidth && constraints.minWidth > 0 ? constraints.maxWidth - margin.left - margin.right : resolveSize(
|
|
651
|
-
let height = constraints.minHeight === constraints.maxHeight && constraints.minHeight > 0 ? constraints.maxHeight - margin.top - margin.bottom : resolveSize(
|
|
652
|
-
if (
|
|
653
|
-
if (
|
|
654
|
-
if (
|
|
655
|
-
if (
|
|
736
|
+
const intrinsic = measureIntrinsicSize(layoutElement, ctx, availableWidth);
|
|
737
|
+
let width = constraints.minWidth === constraints.maxWidth && constraints.minWidth > 0 ? constraints.maxWidth - margin.left - margin.right : resolveSize(layoutElement.width, availableWidth, intrinsic.width);
|
|
738
|
+
let height = constraints.minHeight === constraints.maxHeight && constraints.minHeight > 0 ? constraints.maxHeight - margin.top - margin.bottom : resolveSize(layoutElement.height, availableHeight, intrinsic.height);
|
|
739
|
+
if (layoutElement.minWidth !== void 0) width = Math.max(width, layoutElement.minWidth);
|
|
740
|
+
if (layoutElement.maxWidth !== void 0) width = Math.min(width, layoutElement.maxWidth);
|
|
741
|
+
if (layoutElement.minHeight !== void 0) height = Math.max(height, layoutElement.minHeight);
|
|
742
|
+
if (layoutElement.maxHeight !== void 0) height = Math.min(height, layoutElement.maxHeight);
|
|
656
743
|
const actualX = x + margin.left;
|
|
657
744
|
const actualY = y + margin.top;
|
|
658
745
|
const contentX = actualX + padding.left;
|
|
@@ -660,7 +747,7 @@ function computeLayout(element, ctx, constraints, x = 0, y = 0) {
|
|
|
660
747
|
const contentWidth = width - padding.left - padding.right;
|
|
661
748
|
const contentHeight = height - padding.top - padding.bottom;
|
|
662
749
|
const node = {
|
|
663
|
-
element,
|
|
750
|
+
element: layoutElement,
|
|
664
751
|
layout: {
|
|
665
752
|
x: actualX,
|
|
666
753
|
y: actualY,
|
|
@@ -673,14 +760,14 @@ function computeLayout(element, ctx, constraints, x = 0, y = 0) {
|
|
|
673
760
|
},
|
|
674
761
|
children: []
|
|
675
762
|
};
|
|
676
|
-
if (
|
|
677
|
-
const font =
|
|
678
|
-
if (
|
|
679
|
-
let { lines, offsets } = wrapText(ctx,
|
|
680
|
-
if (
|
|
681
|
-
lines = lines.slice(0,
|
|
682
|
-
offsets = offsets.slice(0,
|
|
683
|
-
if (
|
|
763
|
+
if (layoutElement.type === "text") {
|
|
764
|
+
const font = layoutElement.font ?? {};
|
|
765
|
+
if (layoutElement.wrap && contentWidth > 0) {
|
|
766
|
+
let { lines, offsets } = wrapText(ctx, layoutElement.content, contentWidth, font);
|
|
767
|
+
if (layoutElement.maxLines && lines.length > layoutElement.maxLines) {
|
|
768
|
+
lines = lines.slice(0, layoutElement.maxLines);
|
|
769
|
+
offsets = offsets.slice(0, layoutElement.maxLines);
|
|
770
|
+
if (layoutElement.ellipsis && lines.length > 0) {
|
|
684
771
|
const lastIdx = lines.length - 1;
|
|
685
772
|
const truncated = truncateText(ctx, lines[lastIdx], contentWidth, font);
|
|
686
773
|
lines[lastIdx] = truncated.text;
|
|
@@ -690,17 +777,17 @@ function computeLayout(element, ctx, constraints, x = 0, y = 0) {
|
|
|
690
777
|
node.lines = lines;
|
|
691
778
|
node.lineOffsets = offsets;
|
|
692
779
|
} else {
|
|
693
|
-
const { text, offset } = truncateText(ctx,
|
|
780
|
+
const { text, offset } = truncateText(ctx, layoutElement.content, contentWidth > 0 && layoutElement.ellipsis ? contentWidth : Infinity, font);
|
|
694
781
|
node.lines = [text];
|
|
695
782
|
node.lineOffsets = [offset];
|
|
696
783
|
}
|
|
697
784
|
}
|
|
698
|
-
if (
|
|
699
|
-
const lineHeight =
|
|
700
|
-
let lines = wrapRichText(ctx,
|
|
701
|
-
if (
|
|
702
|
-
lines = lines.slice(0,
|
|
703
|
-
if (
|
|
785
|
+
if (layoutElement.type === "richtext") {
|
|
786
|
+
const lineHeight = layoutElement.lineHeight ?? 1.2;
|
|
787
|
+
let lines = wrapRichText(ctx, layoutElement.spans, contentWidth, lineHeight);
|
|
788
|
+
if (layoutElement.maxLines && lines.length > layoutElement.maxLines) {
|
|
789
|
+
lines = lines.slice(0, layoutElement.maxLines);
|
|
790
|
+
if (layoutElement.ellipsis && lines.length > 0) {
|
|
704
791
|
const lastLine = lines[lines.length - 1];
|
|
705
792
|
if (lastLine.segments.length > 0) {
|
|
706
793
|
const lastSeg = lastLine.segments[lastLine.segments.length - 1];
|
|
@@ -712,19 +799,19 @@ function computeLayout(element, ctx, constraints, x = 0, y = 0) {
|
|
|
712
799
|
}
|
|
713
800
|
node.richLines = lines;
|
|
714
801
|
}
|
|
715
|
-
if (
|
|
716
|
-
const children =
|
|
717
|
-
if (
|
|
718
|
-
const stackAlign =
|
|
719
|
-
const stackJustify =
|
|
802
|
+
if (layoutElement.type === "box" || layoutElement.type === "stack") {
|
|
803
|
+
const children = layoutElement.children ?? [];
|
|
804
|
+
if (layoutElement.type === "stack") {
|
|
805
|
+
const stackAlign = layoutElement.align ?? "start";
|
|
806
|
+
const stackJustify = layoutElement.justify ?? "start";
|
|
720
807
|
for (const child of children) {
|
|
721
|
-
const childNode =
|
|
808
|
+
const childNode = computeLayoutImpl(child, ctx, {
|
|
722
809
|
minWidth: 0,
|
|
723
810
|
maxWidth: contentWidth,
|
|
724
811
|
minHeight: 0,
|
|
725
812
|
maxHeight: contentHeight
|
|
726
813
|
}, contentX, contentY);
|
|
727
|
-
const childMargin =
|
|
814
|
+
const childMargin = getElementMargin(child);
|
|
728
815
|
const childOuterWidth = childNode.layout.width + childMargin.left + childMargin.right;
|
|
729
816
|
const childOuterHeight = childNode.layout.height + childMargin.top + childMargin.bottom;
|
|
730
817
|
let offsetX = 0;
|
|
@@ -737,19 +824,21 @@ function computeLayout(element, ctx, constraints, x = 0, y = 0) {
|
|
|
737
824
|
node.children.push(childNode);
|
|
738
825
|
}
|
|
739
826
|
} else {
|
|
740
|
-
const
|
|
741
|
-
const
|
|
742
|
-
const
|
|
743
|
-
const
|
|
744
|
-
const
|
|
827
|
+
const boxElement = layoutElement;
|
|
828
|
+
const direction = boxElement.direction ?? "row";
|
|
829
|
+
const justify = boxElement.justify ?? "start";
|
|
830
|
+
const align = boxElement.align ?? "stretch";
|
|
831
|
+
const gap = boxElement.gap ?? 0;
|
|
832
|
+
const wrap = boxElement.wrap ?? false;
|
|
745
833
|
const isRow = direction === "row" || direction === "row-reverse";
|
|
746
834
|
const isReverse = direction === "row-reverse" || direction === "column-reverse";
|
|
747
835
|
const getContentMainSize = () => isRow ? contentWidth : contentHeight;
|
|
748
836
|
const getContentCrossSize = () => isRow ? contentHeight : contentWidth;
|
|
749
837
|
const childInfos = [];
|
|
750
838
|
for (const child of children) {
|
|
751
|
-
const childMargin =
|
|
752
|
-
const
|
|
839
|
+
const childMargin = getElementMargin(child);
|
|
840
|
+
const childProps = getElementLayoutProps(child);
|
|
841
|
+
const childFlex = childProps.flex ?? 0;
|
|
753
842
|
if (childFlex > 0) childInfos.push({
|
|
754
843
|
element: child,
|
|
755
844
|
width: 0,
|
|
@@ -759,10 +848,10 @@ function computeLayout(element, ctx, constraints, x = 0, y = 0) {
|
|
|
759
848
|
});
|
|
760
849
|
else {
|
|
761
850
|
const size = measureIntrinsicSize(child, ctx, contentWidth - childMargin.left - childMargin.right);
|
|
762
|
-
const shouldStretchWidth = !isRow &&
|
|
763
|
-
const shouldStretchHeight = isRow &&
|
|
764
|
-
let w = sizeNeedsParent(
|
|
765
|
-
let h = sizeNeedsParent(
|
|
851
|
+
const shouldStretchWidth = !isRow && childProps.width === void 0 && align === "stretch";
|
|
852
|
+
const shouldStretchHeight = isRow && childProps.height === void 0 && align === "stretch";
|
|
853
|
+
let w = sizeNeedsParent(childProps.width) ? resolveSize(childProps.width, contentWidth - childMargin.left - childMargin.right, size.width) : resolveSize(childProps.width, 0, size.width);
|
|
854
|
+
let h = sizeNeedsParent(childProps.height) ? resolveSize(childProps.height, contentHeight - childMargin.top - childMargin.bottom, size.height) : resolveSize(childProps.height, 0, size.height);
|
|
766
855
|
if (shouldStretchWidth && !wrap) w = contentWidth - childMargin.left - childMargin.right;
|
|
767
856
|
if (shouldStretchHeight && !wrap) h = contentHeight - childMargin.top - childMargin.bottom;
|
|
768
857
|
childInfos.push({
|
|
@@ -803,14 +892,15 @@ function computeLayout(element, ctx, constraints, x = 0, y = 0) {
|
|
|
803
892
|
const availableForFlex = Math.max(0, mainAxisSize - totalFixed - totalGap);
|
|
804
893
|
for (const info of lineInfos) if (info.flex > 0) {
|
|
805
894
|
const flexSize = totalFlex > 0 ? availableForFlex * info.flex / totalFlex : 0;
|
|
895
|
+
const childProps = getElementLayoutProps(info.element);
|
|
806
896
|
if (isRow) {
|
|
807
897
|
info.width = flexSize;
|
|
808
898
|
const size = measureIntrinsicSize(info.element, ctx, flexSize);
|
|
809
|
-
info.height = sizeNeedsParent(
|
|
899
|
+
info.height = sizeNeedsParent(childProps.height) ? resolveSize(childProps.height, contentHeight - info.margin.top - info.margin.bottom, size.height) : resolveSize(childProps.height, 0, size.height);
|
|
810
900
|
} else {
|
|
811
901
|
info.height = flexSize;
|
|
812
902
|
const size = measureIntrinsicSize(info.element, ctx, contentWidth - info.margin.left - info.margin.right);
|
|
813
|
-
info.width = sizeNeedsParent(
|
|
903
|
+
info.width = sizeNeedsParent(childProps.width) ? resolveSize(childProps.width, contentWidth - info.margin.left - info.margin.right, size.width) : resolveSize(childProps.width, 0, size.width);
|
|
814
904
|
}
|
|
815
905
|
}
|
|
816
906
|
}
|
|
@@ -861,17 +951,18 @@ function computeLayout(element, ctx, constraints, x = 0, y = 0) {
|
|
|
861
951
|
const orderedInfos = isReverse ? [...lineInfos].reverse() : lineInfos;
|
|
862
952
|
for (let i = 0; i < orderedInfos.length; i++) {
|
|
863
953
|
const info = orderedInfos[i];
|
|
954
|
+
const childProps = getElementLayoutProps(info.element);
|
|
864
955
|
const crossAxisSize = wrap ? lineCrossSize : getContentCrossSize();
|
|
865
956
|
const childCrossSize = isRow ? info.height + info.margin.top + info.margin.bottom : info.width + info.margin.left + info.margin.right;
|
|
866
957
|
let itemCrossOffset = 0;
|
|
867
|
-
const effectiveAlign =
|
|
958
|
+
const effectiveAlign = childProps.alignSelf ?? align;
|
|
868
959
|
if (effectiveAlign === "start") itemCrossOffset = 0;
|
|
869
960
|
else if (effectiveAlign === "end") itemCrossOffset = crossAxisSize - childCrossSize;
|
|
870
961
|
else if (effectiveAlign === "center") itemCrossOffset = (crossAxisSize - childCrossSize) / 2;
|
|
871
962
|
else if (effectiveAlign === "stretch") {
|
|
872
963
|
itemCrossOffset = 0;
|
|
873
|
-
if (isRow &&
|
|
874
|
-
else if (!isRow &&
|
|
964
|
+
if (isRow && childProps.height === void 0) info.height = crossAxisSize - info.margin.top - info.margin.bottom;
|
|
965
|
+
else if (!isRow && childProps.width === void 0) info.width = crossAxisSize - info.margin.left - info.margin.right;
|
|
875
966
|
}
|
|
876
967
|
const childX = isRow ? contentX + mainOffset + info.margin.left : contentX + crossOffset + itemCrossOffset + info.margin.left;
|
|
877
968
|
const childY = isRow ? contentY + crossOffset + itemCrossOffset + info.margin.top : contentY + mainOffset + info.margin.top;
|
|
@@ -880,26 +971,29 @@ function computeLayout(element, ctx, constraints, x = 0, y = 0) {
|
|
|
880
971
|
let minHeight = 0;
|
|
881
972
|
let maxHeight = info.height;
|
|
882
973
|
let shouldStretchCross = false;
|
|
883
|
-
if (info.flex > 0)
|
|
884
|
-
|
|
885
|
-
if (
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
974
|
+
if (info.flex > 0) {
|
|
975
|
+
const childProps = getElementLayoutProps(info.element);
|
|
976
|
+
if (isRow) {
|
|
977
|
+
minWidth = maxWidth = info.width;
|
|
978
|
+
if (childProps.height === void 0 && align === "stretch") {
|
|
979
|
+
minHeight = info.height;
|
|
980
|
+
maxHeight = boxElement.height !== void 0 ? info.height : Infinity;
|
|
981
|
+
shouldStretchCross = true;
|
|
982
|
+
}
|
|
983
|
+
} else {
|
|
984
|
+
minHeight = maxHeight = info.height;
|
|
985
|
+
if (childProps.width === void 0 && align === "stretch") {
|
|
986
|
+
minWidth = info.width;
|
|
987
|
+
maxWidth = boxElement.width !== void 0 ? info.width : Infinity;
|
|
988
|
+
shouldStretchCross = true;
|
|
989
|
+
}
|
|
889
990
|
}
|
|
890
991
|
} else {
|
|
891
|
-
|
|
892
|
-
if (
|
|
893
|
-
|
|
894
|
-
maxWidth = element.width !== void 0 ? info.width : Infinity;
|
|
895
|
-
shouldStretchCross = true;
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
else {
|
|
899
|
-
if (!isRow && info.element.width === void 0 && align === "stretch") minWidth = maxWidth = crossAxisSize - info.margin.left - info.margin.right;
|
|
900
|
-
if (isRow && info.element.height === void 0 && align === "stretch") minHeight = maxHeight = crossAxisSize - info.margin.top - info.margin.bottom;
|
|
992
|
+
const childProps = getElementLayoutProps(info.element);
|
|
993
|
+
if (!isRow && childProps.width === void 0 && align === "stretch") minWidth = maxWidth = crossAxisSize - info.margin.left - info.margin.right;
|
|
994
|
+
if (isRow && childProps.height === void 0 && align === "stretch") minHeight = maxHeight = crossAxisSize - info.margin.top - info.margin.bottom;
|
|
901
995
|
}
|
|
902
|
-
const childNode =
|
|
996
|
+
const childNode = computeLayoutImpl(info.element, ctx, {
|
|
903
997
|
minWidth,
|
|
904
998
|
maxWidth,
|
|
905
999
|
minHeight,
|
|
@@ -922,12 +1016,12 @@ function computeLayout(element, ctx, constraints, x = 0, y = 0) {
|
|
|
922
1016
|
crossOffset += lineCrossSize;
|
|
923
1017
|
if (lineIndex < lines.length - 1) crossOffset += gap;
|
|
924
1018
|
}
|
|
925
|
-
if (wrap &&
|
|
1019
|
+
if (wrap && boxElement.height === void 0 && isRow) {
|
|
926
1020
|
const actualContentHeight = crossOffset;
|
|
927
1021
|
const actualHeight = actualContentHeight + padding.top + padding.bottom;
|
|
928
1022
|
node.layout.height = actualHeight;
|
|
929
1023
|
node.layout.contentHeight = actualContentHeight;
|
|
930
|
-
} else if (wrap &&
|
|
1024
|
+
} else if (wrap && boxElement.width === void 0 && !isRow) {
|
|
931
1025
|
const actualContentWidth = crossOffset;
|
|
932
1026
|
const actualWidth = actualContentWidth + padding.left + padding.right;
|
|
933
1027
|
node.layout.width = actualWidth;
|
|
@@ -936,7 +1030,7 @@ function computeLayout(element, ctx, constraints, x = 0, y = 0) {
|
|
|
936
1030
|
if (!wrap) {
|
|
937
1031
|
let maxChildCrossSize = 0;
|
|
938
1032
|
for (const childNode of node.children) {
|
|
939
|
-
const childMargin =
|
|
1033
|
+
const childMargin = getElementMargin(childNode.element);
|
|
940
1034
|
if (isRow) {
|
|
941
1035
|
const childOuterHeight = childNode.layout.height + childMargin.top + childMargin.bottom;
|
|
942
1036
|
maxChildCrossSize = Math.max(maxChildCrossSize, childOuterHeight);
|
|
@@ -945,13 +1039,13 @@ function computeLayout(element, ctx, constraints, x = 0, y = 0) {
|
|
|
945
1039
|
maxChildCrossSize = Math.max(maxChildCrossSize, childOuterWidth);
|
|
946
1040
|
}
|
|
947
1041
|
}
|
|
948
|
-
if (isRow &&
|
|
1042
|
+
if (isRow && boxElement.height === void 0) {
|
|
949
1043
|
const actualHeight = maxChildCrossSize + padding.top + padding.bottom;
|
|
950
1044
|
if (actualHeight > node.layout.height) {
|
|
951
1045
|
node.layout.height = actualHeight;
|
|
952
1046
|
node.layout.contentHeight = maxChildCrossSize;
|
|
953
1047
|
}
|
|
954
|
-
} else if (!isRow &&
|
|
1048
|
+
} else if (!isRow && boxElement.width === void 0) {
|
|
955
1049
|
const actualWidth = maxChildCrossSize + padding.left + padding.right;
|
|
956
1050
|
if (actualWidth > node.layout.width) {
|
|
957
1051
|
node.layout.width = actualWidth;
|
|
@@ -961,48 +1055,24 @@ function computeLayout(element, ctx, constraints, x = 0, y = 0) {
|
|
|
961
1055
|
}
|
|
962
1056
|
if (isReverse) node.children.reverse();
|
|
963
1057
|
}
|
|
964
|
-
} else if (
|
|
965
|
-
const child =
|
|
1058
|
+
} else if (layoutElement.type === "customdraw") {
|
|
1059
|
+
const child = layoutElement.children;
|
|
966
1060
|
if (child) {
|
|
967
1061
|
const childMargin = normalizeSpacing(child.margin);
|
|
968
|
-
const childNode =
|
|
1062
|
+
const childNode = computeLayoutImpl(child, ctx, {
|
|
969
1063
|
minWidth: 0,
|
|
970
1064
|
maxWidth: contentWidth,
|
|
971
1065
|
minHeight: 0,
|
|
972
1066
|
maxHeight: contentHeight
|
|
973
1067
|
}, contentX, contentY);
|
|
974
1068
|
node.children.push(childNode);
|
|
975
|
-
if (
|
|
1069
|
+
if (layoutElement.width === void 0) {
|
|
976
1070
|
const childOuterWidth = childNode.layout.width + childMargin.left + childMargin.right;
|
|
977
1071
|
const actualWidth = childOuterWidth + padding.left + padding.right;
|
|
978
1072
|
node.layout.width = actualWidth;
|
|
979
1073
|
node.layout.contentWidth = childOuterWidth;
|
|
980
1074
|
}
|
|
981
|
-
if (
|
|
982
|
-
const childOuterHeight = childNode.layout.height + childMargin.top + childMargin.bottom;
|
|
983
|
-
const actualHeight = childOuterHeight + padding.top + padding.bottom;
|
|
984
|
-
node.layout.height = actualHeight;
|
|
985
|
-
node.layout.contentHeight = childOuterHeight;
|
|
986
|
-
}
|
|
987
|
-
}
|
|
988
|
-
} else if (element.type === "customdraw") {
|
|
989
|
-
const child = element.children;
|
|
990
|
-
if (child) {
|
|
991
|
-
const childMargin = normalizeSpacing(child.margin);
|
|
992
|
-
const childNode = computeLayout(child, ctx, {
|
|
993
|
-
minWidth: 0,
|
|
994
|
-
maxWidth: contentWidth,
|
|
995
|
-
minHeight: 0,
|
|
996
|
-
maxHeight: contentHeight
|
|
997
|
-
}, contentX, contentY);
|
|
998
|
-
node.children.push(childNode);
|
|
999
|
-
if (element.width === void 0) {
|
|
1000
|
-
const childOuterWidth = childNode.layout.width + childMargin.left + childMargin.right;
|
|
1001
|
-
const actualWidth = childOuterWidth + padding.left + padding.right;
|
|
1002
|
-
node.layout.width = actualWidth;
|
|
1003
|
-
node.layout.contentWidth = childOuterWidth;
|
|
1004
|
-
}
|
|
1005
|
-
if (element.height === void 0) {
|
|
1075
|
+
if (layoutElement.height === void 0) {
|
|
1006
1076
|
const childOuterHeight = childNode.layout.height + childMargin.top + childMargin.bottom;
|
|
1007
1077
|
const actualHeight = childOuterHeight + padding.top + padding.bottom;
|
|
1008
1078
|
node.layout.height = actualHeight;
|
|
@@ -1833,8 +1903,17 @@ function renderNode(ctx, node) {
|
|
|
1833
1903
|
function createCanvas(options) {
|
|
1834
1904
|
const { width, height, pixelRatio = 1 } = options;
|
|
1835
1905
|
const canvas = options.canvas ?? createRawCanvas(width * pixelRatio, height * pixelRatio);
|
|
1906
|
+
if (options.canvas) {
|
|
1907
|
+
canvas.width = width * pixelRatio;
|
|
1908
|
+
canvas.height = height * pixelRatio;
|
|
1909
|
+
if ("style" in canvas && options.updateStyles !== false) {
|
|
1910
|
+
canvas.style.width = `${width}px`;
|
|
1911
|
+
canvas.style.height = `${height}px`;
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1836
1914
|
const ctx = canvas.getContext("2d");
|
|
1837
1915
|
if (!ctx) throw new Error("Failed to get 2d context");
|
|
1916
|
+
ctx.resetTransform();
|
|
1838
1917
|
if (options.imageSmoothingEnabled !== void 0) ctx.imageSmoothingEnabled = options.imageSmoothingEnabled;
|
|
1839
1918
|
if (options.imageSmoothingQuality !== void 0) ctx.imageSmoothingQuality = options.imageSmoothingQuality;
|
|
1840
1919
|
if (pixelRatio !== 1) ctx.scale(pixelRatio, pixelRatio);
|
|
@@ -1857,7 +1936,7 @@ function createCanvas(options) {
|
|
|
1857
1936
|
if (canvas.width !== contentWidth * pixelRatio || canvas.height !== contentHeight * pixelRatio) {
|
|
1858
1937
|
canvas.width = contentWidth * pixelRatio;
|
|
1859
1938
|
canvas.height = contentHeight * pixelRatio;
|
|
1860
|
-
if ("style" in canvas) {
|
|
1939
|
+
if ("style" in canvas && options.updateStyles !== false) {
|
|
1861
1940
|
canvas.style.width = `${contentWidth}px`;
|
|
1862
1941
|
canvas.style.height = `${contentHeight}px`;
|
|
1863
1942
|
}
|
|
@@ -2003,10 +2082,12 @@ function getElementType(element) {
|
|
|
2003
2082
|
switch (element.type) {
|
|
2004
2083
|
case "box": return "Box";
|
|
2005
2084
|
case "text": return `Text "${element.content.slice(0, 20)}${element.content.length > 20 ? "..." : ""}"`;
|
|
2085
|
+
case "richtext": return "RichText";
|
|
2006
2086
|
case "stack": return "Stack";
|
|
2007
2087
|
case "image": return "Image";
|
|
2008
2088
|
case "svg": return "Svg";
|
|
2009
|
-
|
|
2089
|
+
case "transform": return "Transform";
|
|
2090
|
+
case "customdraw": return "CustomDraw";
|
|
2010
2091
|
}
|
|
2011
2092
|
}
|
|
2012
2093
|
/**
|
package/browser/index.d.cts
CHANGED
|
@@ -363,7 +363,8 @@ interface CustomDrawProps extends LayoutProps {
|
|
|
363
363
|
interface CustomDrawElement extends ElementBase, CustomDrawProps {
|
|
364
364
|
type: "customdraw";
|
|
365
365
|
}
|
|
366
|
-
type
|
|
366
|
+
type LayoutElement = BoxElement | TextElement | RichTextElement | ImageElement | SvgElement | StackElement | CustomDrawElement;
|
|
367
|
+
type Element = LayoutElement | TransformElement;
|
|
367
368
|
//#endregion
|
|
368
369
|
//#region node_modules/@napi-rs/canvas/index.d.ts
|
|
369
370
|
interface CanvasRenderingContext2D$1 extends CanvasCompositing, CanvasDrawPath, CanvasFillStrokeStyles, CanvasFilters, CanvasImageData, CanvasImageSmoothing, CanvasPath, CanvasPathDrawingStyles, CanvasRect, CanvasSettings, CanvasShadowStyles, CanvasState, CanvasText, CanvasTextDrawingStyles, CanvasTransform, CanvasPDFAnnotations {}
|
|
@@ -916,20 +917,17 @@ interface CanvasOptions<T extends HTMLCanvasElement | OffscreenCanvas | Canvas =
|
|
|
916
917
|
pixelRatio?: number;
|
|
917
918
|
/** 根据内容调整画布大小 */
|
|
918
919
|
fitContent?: boolean;
|
|
920
|
+
updateStyles?: boolean;
|
|
919
921
|
imageSmoothingEnabled?: boolean;
|
|
920
922
|
imageSmoothingQuality?: "low" | "medium" | "high";
|
|
921
923
|
canvas?: T;
|
|
922
924
|
}
|
|
923
|
-
interface
|
|
924
|
-
width: number;
|
|
925
|
-
height: number;
|
|
926
|
-
}
|
|
927
|
-
interface DrawCallCanvas<T extends HTMLCanvasElement | OffscreenCanvas | Canvas = HTMLCanvasElement | OffscreenCanvas | Canvas> {
|
|
925
|
+
interface DrawCallCanvas<T extends HTMLCanvasElement | OffscreenCanvas | Canvas = HTMLCanvasElement> {
|
|
928
926
|
readonly width: number;
|
|
929
927
|
readonly height: number;
|
|
930
928
|
readonly pixelRatio: number;
|
|
931
929
|
readonly canvas: T;
|
|
932
|
-
render(element:
|
|
930
|
+
render(element: LayoutElement): LayoutNode;
|
|
933
931
|
clear(): void;
|
|
934
932
|
getContext(): CanvasRenderingContext2D;
|
|
935
933
|
toDataURL(type?: string, quality?: number): string;
|
|
@@ -978,7 +976,11 @@ declare function Text(props: TextProps): TextElement;
|
|
|
978
976
|
declare function Transform(props: TransformProps): TransformElement;
|
|
979
977
|
//#endregion
|
|
980
978
|
//#region src/layout/engine.d.ts
|
|
981
|
-
|
|
979
|
+
/**
|
|
980
|
+
* 布局计算主函数
|
|
981
|
+
* 内部使用 Element 类型以支持 Transform,外部通过 LayoutElement 约束类型
|
|
982
|
+
*/
|
|
983
|
+
declare function computeLayout(element: LayoutElement, ctx: MeasureContext, constraints: LayoutConstraints, x?: number, y?: number): LayoutNode;
|
|
982
984
|
//#endregion
|
|
983
985
|
//#region src/layout/utils/print.d.ts
|
|
984
986
|
/**
|
|
@@ -993,4 +995,4 @@ declare function printLayout(node: LayoutNode): void;
|
|
|
993
995
|
*/
|
|
994
996
|
declare function layoutToString(node: LayoutNode, _indent?: string): string;
|
|
995
997
|
//#endregion
|
|
996
|
-
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
|
|
998
|
+
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 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 };
|
package/browser/index.d.ts
CHANGED
|
@@ -363,7 +363,8 @@ interface CustomDrawProps extends LayoutProps {
|
|
|
363
363
|
interface CustomDrawElement extends ElementBase, CustomDrawProps {
|
|
364
364
|
type: "customdraw";
|
|
365
365
|
}
|
|
366
|
-
type
|
|
366
|
+
type LayoutElement = BoxElement | TextElement | RichTextElement | ImageElement | SvgElement | StackElement | CustomDrawElement;
|
|
367
|
+
type Element = LayoutElement | TransformElement;
|
|
367
368
|
//#endregion
|
|
368
369
|
//#region node_modules/@napi-rs/canvas/index.d.ts
|
|
369
370
|
interface CanvasRenderingContext2D$1 extends CanvasCompositing, CanvasDrawPath, CanvasFillStrokeStyles, CanvasFilters, CanvasImageData, CanvasImageSmoothing, CanvasPath, CanvasPathDrawingStyles, CanvasRect, CanvasSettings, CanvasShadowStyles, CanvasState, CanvasText, CanvasTextDrawingStyles, CanvasTransform, CanvasPDFAnnotations {}
|
|
@@ -916,20 +917,17 @@ interface CanvasOptions<T extends HTMLCanvasElement | OffscreenCanvas | Canvas =
|
|
|
916
917
|
pixelRatio?: number;
|
|
917
918
|
/** 根据内容调整画布大小 */
|
|
918
919
|
fitContent?: boolean;
|
|
920
|
+
updateStyles?: boolean;
|
|
919
921
|
imageSmoothingEnabled?: boolean;
|
|
920
922
|
imageSmoothingQuality?: "low" | "medium" | "high";
|
|
921
923
|
canvas?: T;
|
|
922
924
|
}
|
|
923
|
-
interface
|
|
924
|
-
width: number;
|
|
925
|
-
height: number;
|
|
926
|
-
}
|
|
927
|
-
interface DrawCallCanvas<T extends HTMLCanvasElement | OffscreenCanvas | Canvas = HTMLCanvasElement | OffscreenCanvas | Canvas> {
|
|
925
|
+
interface DrawCallCanvas<T extends HTMLCanvasElement | OffscreenCanvas | Canvas = HTMLCanvasElement> {
|
|
928
926
|
readonly width: number;
|
|
929
927
|
readonly height: number;
|
|
930
928
|
readonly pixelRatio: number;
|
|
931
929
|
readonly canvas: T;
|
|
932
|
-
render(element:
|
|
930
|
+
render(element: LayoutElement): LayoutNode;
|
|
933
931
|
clear(): void;
|
|
934
932
|
getContext(): CanvasRenderingContext2D;
|
|
935
933
|
toDataURL(type?: string, quality?: number): string;
|
|
@@ -978,7 +976,11 @@ declare function Text(props: TextProps): TextElement;
|
|
|
978
976
|
declare function Transform(props: TransformProps): TransformElement;
|
|
979
977
|
//#endregion
|
|
980
978
|
//#region src/layout/engine.d.ts
|
|
981
|
-
|
|
979
|
+
/**
|
|
980
|
+
* 布局计算主函数
|
|
981
|
+
* 内部使用 Element 类型以支持 Transform,外部通过 LayoutElement 约束类型
|
|
982
|
+
*/
|
|
983
|
+
declare function computeLayout(element: LayoutElement, ctx: MeasureContext, constraints: LayoutConstraints, x?: number, y?: number): LayoutNode;
|
|
982
984
|
//#endregion
|
|
983
985
|
//#region src/layout/utils/print.d.ts
|
|
984
986
|
/**
|
|
@@ -993,4 +995,4 @@ declare function printLayout(node: LayoutNode): void;
|
|
|
993
995
|
*/
|
|
994
996
|
declare function layoutToString(node: LayoutNode, _indent?: string): string;
|
|
995
997
|
//#endregion
|
|
996
|
-
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
|
|
998
|
+
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 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 };
|