Skip to content

Commit 199f906

Browse files
committed
feat(layout): 更新布局构建逻辑以动态调整节点高度
- 在 SNLayoutBuilder 中添加对 Line 和 Measure 节点高度的动态计算,确保根据子节点的实际内容调整行高。 - 更新 Measure 元素的默认高度设置,确保其包含必要的冗余空间以适应乐谱元素。 - 在节点添加后立即更新父节点高度,提升布局的准确性和视觉效果。 该变更增强了布局构建的灵活性和可维护性,确保元素在视觉上更加协调。
1 parent 189fe15 commit 199f906

File tree

3 files changed

+119
-8
lines changed

3 files changed

+119
-8
lines changed

packages/simple-notation/src/layout/builder.ts

Lines changed: 113 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,9 @@ export class SNLayoutBuilder {
422422

423423
// 子节点构建完成后,计算 Element 的布局信息
424424
this.finalizeNodeLayout(element);
425+
426+
// 子节点添加后,立即更新父节点(Line)的高度
427+
this.calculateNodeHeight(parentNode);
425428
}
426429
}
427430

@@ -557,6 +560,9 @@ export class SNLayoutBuilder {
557560
layoutElement.layout.width,
558561
);
559562
}
563+
564+
// 子节点添加后,立即更新父节点(Measure Element)的高度
565+
this.calculateNodeHeight(parentNode);
560566
}
561567
}
562568

@@ -657,8 +663,15 @@ export class SNLayoutBuilder {
657663
lyricBaseOffset +
658664
verseNumber * lyricLineHeight;
659665
}
666+
667+
// 歌词元素添加后,立即更新父节点(Measure Element)的高度
668+
this.calculateNodeHeight(parentLayoutElement);
660669
}
661670
}
671+
672+
// 所有歌词元素添加完成后,更新父节点(Measure Element)的高度
673+
// 确保父节点高度包含所有歌词
674+
this.calculateNodeHeight(parentLayoutElement);
662675
}
663676

664677
/**
@@ -820,13 +833,80 @@ export class SNLayoutBuilder {
820833
}
821834

822835
case SNLayoutNodeType.LINE: {
823-
// Line节点高度按配置设置,不需要计算(已在转换器中设置)
824-
// 这里不做处理
836+
// Line节点高度需要根据实际内容动态调整
837+
// 根据子节点(measure)的实际高度来计算行高
838+
if (node instanceof SNLayoutLine) {
839+
// 默认行高(从配置中获取,如果没有则使用50)
840+
const defaultHeight =
841+
typeof node.layout.height === 'number' && node.layout.height > 0
842+
? node.layout.height
843+
: 50;
844+
845+
// 计算实际需要的行高
846+
let requiredHeight = defaultHeight;
847+
848+
// 检查是否有子元素
849+
if (node.children && node.children.length > 0) {
850+
// 计算子节点的最大高度
851+
let maxChildHeight = 0;
852+
let hasMetadata = false;
853+
854+
// 遍历所有子元素,计算最大高度
855+
for (const child of node.children) {
856+
if (!child.layout) continue;
857+
858+
const childData = child.data as any;
859+
const childType = childData?.type as string | undefined;
860+
861+
// 检查是否是调号等元信息元素
862+
if (
863+
childType === 'metadata-music-info' ||
864+
childType === 'metadata-contributors'
865+
) {
866+
hasMetadata = true;
867+
const childHeight =
868+
typeof child.layout.height === 'number'
869+
? child.layout.height
870+
: 0;
871+
maxChildHeight = Math.max(maxChildHeight, childHeight);
872+
}
873+
874+
// 检查是否是 measure 元素
875+
if (childType === 'measure') {
876+
const childHeight =
877+
typeof child.layout.height === 'number'
878+
? child.layout.height
879+
: 0;
880+
maxChildHeight = Math.max(maxChildHeight, childHeight);
881+
}
882+
}
883+
884+
// 如果有子节点,使用子节点的最大高度
885+
if (maxChildHeight > 0) {
886+
requiredHeight = Math.max(requiredHeight, maxChildHeight);
887+
}
888+
889+
// 如果有调号等元信息,确保行高足够
890+
if (hasMetadata && maxChildHeight === 0) {
891+
const metadataHeightIncrement = 5; // 调号等元信息需要的额外行高
892+
requiredHeight = Math.max(
893+
requiredHeight,
894+
defaultHeight + metadataHeightIncrement,
895+
);
896+
}
897+
}
898+
899+
// 更新行高
900+
node.layout.height = requiredHeight;
901+
}
825902
break;
826903
}
827904

828905
case SNLayoutNodeType.ELEMENT: {
829906
// Element:根据子节点计算高度,如果没有子节点则使用默认值
907+
const nodeData = node.data as any;
908+
const isMeasure = nodeData?.type === 'measure';
909+
830910
if (node.children && node.children.length > 0) {
831911
const childrenHeight = node.calculateChildrenHeight();
832912
const padding = node.layout.padding || {
@@ -837,15 +917,44 @@ export class SNLayoutBuilder {
837917
};
838918
// childrenHeight 返回的是 maxBottom,已经包含了 padding.top 的空间
839919
// 所以只需要加上 padding.bottom 即可
840-
node.layout.height = childrenHeight + padding.bottom;
920+
const calculatedHeight = childrenHeight + padding.bottom;
921+
922+
// 如果是 measure element,需要确保有足够的冗余高度来容纳上下加线和符干
923+
// 五线谱配置:staffTop = 6, staffHeight = 30
924+
// 五线谱范围:从 y=6 到 y=36
925+
// 符干长度:20px,上下加线可能延伸几个线间距(lineGap = 7.5)
926+
// 所以最小高度应该是:顶部冗余(6) + 五线谱高度(30) + 底部冗余(20+15) = 71px
927+
if (isMeasure) {
928+
const staffTop = 6; // 五线谱顶部偏移
929+
const staffHeight = 30; // 五线谱高度
930+
const stemLength = 20; // 符干长度
931+
const ledgerLineSpace = 15; // 上下加线的冗余空间(约2个线间距)
932+
const minMeasureHeight =
933+
staffTop + staffHeight + stemLength + ledgerLineSpace; // 71px
934+
935+
// 使用计算出的高度和最小高度的较大值
936+
node.layout.height = Math.max(calculatedHeight, minMeasureHeight);
937+
} else {
938+
node.layout.height = calculatedHeight;
939+
}
841940
} else {
842941
// 叶子元素:使用已有高度或默认值
843942
if (
844943
!node.layout.height ||
845944
typeof node.layout.height !== 'number' ||
846945
node.layout.height === 0
847946
) {
848-
node.layout.height = 20;
947+
// measure 的默认高度应该包含五线谱和冗余空间
948+
if (isMeasure) {
949+
const staffTop = 6;
950+
const staffHeight = 30;
951+
const stemLength = 20;
952+
const ledgerLineSpace = 15;
953+
node.layout.height =
954+
staffTop + staffHeight + stemLength + ledgerLineSpace; // 71px
955+
} else {
956+
node.layout.height = 20;
957+
}
849958
}
850959
}
851960
break;

packages/simple-notation/src/layout/trans/measure-transformer.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,16 @@ export function transformMeasureElement(
6262
const layoutElement = new SNLayoutElement(`layout-${element.id}`);
6363
layoutElement.data = element;
6464

65-
// 根据元素类型设置不同的宽度
65+
// 根据元素类型设置不同的宽度和高度
6666
let elementWidth = 20; // 默认宽度
67+
let elementHeight = 0; // 默认高度(自适应)
6768
if (element.type === 'note') {
6869
elementWidth = 30;
6970
} else if (element.type === 'rest') {
7071
elementWidth = 25;
7172
} else if (element.type === 'lyric') {
7273
elementWidth = 40;
74+
elementHeight = 14; // 歌词固定高度(对应字体大小 14px)
7375
} else if (element.type === 'tuplet') {
7476
elementWidth = 50; // 连音可能包含多个音符
7577
} else if (element.type === 'tie') {
@@ -81,7 +83,7 @@ export function transformMeasureElement(
8183
x: 0, // 初始位置,由布局计算填充
8284
y: 0, // 初始位置,由布局计算填充
8385
width: elementWidth,
84-
height: 0, // 自适应
86+
height: elementHeight, // 歌词有固定高度,其他元素自适应
8587
});
8688

8789
// 建立父子关系

packages/simple-notation/src/manager/config/debug-config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ export class DebugConfig extends BaseConfig<SNDebugConfig> {
1919
return {
2020
enableBackgroundBoxes: true,
2121
layers: {
22-
root: true,
23-
page: true,
22+
root: false,
23+
page: false,
2424
block: true,
2525
line: true,
2626
element: true,

0 commit comments

Comments
 (0)