Skip to content

Commit eed9496

Browse files
committed
feat(renderer): 使用 Unicode 字符绘制变音记号以增强兼容性
- 更新 renderAccidental 函数,使用 Unicode 字符表示升降号、还原号、重升号和重降号,确保符合标准并兼容更多系统。 - 添加 Unicode 字符的注释,明确每个变音记号对应的字符编码。 - 优化文本属性设置,确保符号在 SVG 中的正确显示和对齐。 该变更提升了变音记号的渲染效果,确保在不同系统中的一致性和可读性。
1 parent d9353ba commit eed9496

File tree

1 file changed

+55
-131
lines changed
  • packages/simple-notation/src/render/renderer/svg/node

1 file changed

+55
-131
lines changed

packages/simple-notation/src/render/renderer/svg/node/element.ts

Lines changed: 55 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ function getClefFromNode(node: SNLayoutNode): SNVoiceMetaClef {
3535
/**
3636
* 绘制升降号符号
3737
*
38+
* 使用 Unicode 字符显示变音记号,确保符合标准并兼容更多系统:
39+
* - 升号(♯):U+266F
40+
* - 降号(♭):U+266D
41+
* - 还原号(♮):U+266E
42+
* - 重升号(𝄪):U+1D12A
43+
* - 重降号(𝄫):U+1D12B
44+
*
3845
* @param parent - 父 SVG 元素
3946
* @param accidental - 变音记号类型
4047
* @param x - 升降号的 x 坐标(通常位于符头左侧)
@@ -46,158 +53,75 @@ function renderAccidental(
4653
x: number,
4754
y: number,
4855
): void {
49-
const strokeWidth = 1.5;
50-
56+
// 使用 Unicode 字符显示,字体大小根据音符大小调整
57+
const baseFontSize = 12; // 基础字体大小
58+
const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
59+
60+
// 设置文本属性
61+
text.setAttribute('x', String(x));
62+
text.setAttribute('y', String(y));
63+
text.setAttribute('font-family', 'Arial, "DejaVu Sans", sans-serif'); // 使用常见字体以确保兼容性
64+
text.setAttribute('fill', '#000');
65+
text.setAttribute('text-anchor', 'middle');
66+
text.setAttribute('dominant-baseline', 'central'); // 垂直居中对齐
67+
text.setAttribute('style', 'font-weight: bold;'); // 所有符号都加粗
68+
69+
// 根据变音记号类型设置对应的 Unicode 字符和字体大小
5170
switch (accidental) {
5271
case SNAccidental.SHARP: {
53-
// 绘制升号(♯):两条垂直的平行线,中间有两条斜线
54-
// 升号宽度约为 4-5px,高度约为 8px
55-
const width = 4;
56-
const height = 8;
57-
const centerX = x - width / 2;
58-
const centerY = y;
59-
60-
// 两条垂直的平行线
61-
const line1 = document.createElementNS(
62-
'http://www.w3.org/2000/svg',
63-
'line',
64-
);
65-
line1.setAttribute('x1', String(centerX));
66-
line1.setAttribute('y1', String(centerY - height / 2));
67-
line1.setAttribute('x2', String(centerX));
68-
line1.setAttribute('y2', String(centerY + height / 2));
69-
line1.setAttribute('stroke', '#000');
70-
line1.setAttribute('stroke-width', String(strokeWidth));
71-
parent.appendChild(line1);
72-
73-
const line2 = document.createElementNS(
74-
'http://www.w3.org/2000/svg',
75-
'line',
76-
);
77-
line2.setAttribute('x1', String(centerX + width));
78-
line2.setAttribute('y1', String(centerY - height / 2));
79-
line2.setAttribute('x2', String(centerX + width));
80-
line2.setAttribute('y2', String(centerY + height / 2));
81-
line2.setAttribute('stroke', '#000');
82-
line2.setAttribute('stroke-width', String(strokeWidth));
83-
parent.appendChild(line2);
84-
85-
// 两条斜线(从左上到右下)
86-
const slash1 = document.createElementNS(
87-
'http://www.w3.org/2000/svg',
88-
'line',
89-
);
90-
slash1.setAttribute('x1', String(centerX - 1));
91-
slash1.setAttribute('y1', String(centerY - height / 2 + 1));
92-
slash1.setAttribute('x2', String(centerX + width + 1));
93-
slash1.setAttribute('y2', String(centerY - height / 2 + 3));
94-
slash1.setAttribute('stroke', '#000');
95-
slash1.setAttribute('stroke-width', String(strokeWidth));
96-
parent.appendChild(slash1);
97-
98-
const slash2 = document.createElementNS(
99-
'http://www.w3.org/2000/svg',
100-
'line',
101-
);
102-
slash2.setAttribute('x1', String(centerX - 1));
103-
slash2.setAttribute('y1', String(centerY + height / 2 - 3));
104-
slash2.setAttribute('x2', String(centerX + width + 1));
105-
slash2.setAttribute('y2', String(centerY + height / 2 - 1));
106-
slash2.setAttribute('stroke', '#000');
107-
slash2.setAttribute('stroke-width', String(strokeWidth));
108-
parent.appendChild(slash2);
72+
// 升号(♯):U+266F
73+
text.setAttribute('font-size', String(baseFontSize));
74+
text.textContent = '\u266F';
10975
break;
11076
}
11177

11278
case SNAccidental.FLAT: {
113-
// 绘制降号(♭):一个类似小写字母 b 的形状
114-
const width = 3;
115-
const height = 8;
116-
const centerX = x;
117-
const centerY = y;
118-
119-
// 使用路径绘制降号
120-
const path = document.createElementNS(
121-
'http://www.w3.org/2000/svg',
122-
'path',
123-
);
124-
// 降号路径:从顶部开始,向下画一条曲线,然后向右上方弯曲
125-
const pathData = `M ${centerX} ${centerY - height / 2}
126-
Q ${centerX - width} ${centerY - height / 4} ${centerX - width} ${centerY}
127-
Q ${centerX - width} ${centerY + height / 4} ${centerX} ${centerY + height / 2}`;
128-
path.setAttribute('d', pathData);
129-
path.setAttribute('fill', 'none');
130-
path.setAttribute('stroke', '#000');
131-
path.setAttribute('stroke-width', String(strokeWidth));
132-
path.setAttribute('stroke-linecap', 'round');
133-
parent.appendChild(path);
79+
// 降号(♭):U+266D
80+
text.setAttribute('font-size', String(baseFontSize));
81+
text.textContent = '\u266D';
13482
break;
13583
}
13684

13785
case SNAccidental.NATURAL: {
138-
// 绘制还原号(♮):类似一个倾斜的矩形,中间有一条斜线
139-
const width = 4;
140-
const height = 8;
141-
const centerX = x - width / 2;
142-
const centerY = y;
143-
144-
// 左侧垂直线
145-
const leftLine = document.createElementNS(
146-
'http://www.w3.org/2000/svg',
147-
'line',
148-
);
149-
leftLine.setAttribute('x1', String(centerX));
150-
leftLine.setAttribute('y1', String(centerY - height / 2 + 1));
151-
leftLine.setAttribute('x2', String(centerX));
152-
leftLine.setAttribute('y2', String(centerY + height / 2));
153-
leftLine.setAttribute('stroke', '#000');
154-
leftLine.setAttribute('stroke-width', String(strokeWidth));
155-
parent.appendChild(leftLine);
156-
157-
// 右侧垂直线
158-
const rightLine = document.createElementNS(
159-
'http://www.w3.org/2000/svg',
160-
'line',
161-
);
162-
rightLine.setAttribute('x1', String(centerX + width));
163-
rightLine.setAttribute('y1', String(centerY - height / 2));
164-
rightLine.setAttribute('x2', String(centerX + width));
165-
rightLine.setAttribute('y2', String(centerY + height / 2 - 1));
166-
rightLine.setAttribute('stroke', '#000');
167-
rightLine.setAttribute('stroke-width', String(strokeWidth));
168-
parent.appendChild(rightLine);
169-
170-
// 中间的斜线(从左上到右下)
171-
const middleLine = document.createElementNS(
172-
'http://www.w3.org/2000/svg',
173-
'line',
174-
);
175-
middleLine.setAttribute('x1', String(centerX));
176-
middleLine.setAttribute('y1', String(centerY - height / 4));
177-
middleLine.setAttribute('x2', String(centerX + width));
178-
middleLine.setAttribute('y2', String(centerY + height / 4));
179-
middleLine.setAttribute('stroke', '#000');
180-
middleLine.setAttribute('stroke-width', String(strokeWidth));
181-
parent.appendChild(middleLine);
86+
// 还原号(♮):U+266E
87+
text.setAttribute('font-size', String(baseFontSize));
88+
text.textContent = '\u266E';
18289
break;
18390
}
18491

18592
case SNAccidental.DOUBLE_SHARP: {
186-
// 绘制重升号(×):两个升号叠加,稍微错开
187-
const offset = 1.5;
188-
renderAccidental(parent, SNAccidental.SHARP, x - offset, y);
189-
renderAccidental(parent, SNAccidental.SHARP, x + offset, y);
93+
// 重升号(𝄪):U+1D12A
94+
const doubleSharpFontSize = baseFontSize * 2;
95+
text.setAttribute('font-size', String(doubleSharpFontSize));
96+
text.setAttribute('dy', '5');
97+
// 使用 String.fromCodePoint 来支持辅助平面字符
98+
try {
99+
text.textContent = String.fromCodePoint(0x1d12a);
100+
} catch {
101+
// 如果系统不支持,则使用两个升号叠加
102+
text.textContent = '\u266F\u266F';
103+
text.setAttribute('dx', '-2'); // 稍微调整位置
104+
}
190105
break;
191106
}
192107

193108
case SNAccidental.DOUBLE_FLAT: {
194-
// 绘制重降号(♭♭):两个降号并排
195-
const offset = 2;
196-
renderAccidental(parent, SNAccidental.FLAT, x - offset, y);
197-
renderAccidental(parent, SNAccidental.FLAT, x + offset, y);
109+
// 重降号(𝄫):U+1D12B
110+
const doubleFlatFontSize = baseFontSize;
111+
text.setAttribute('font-size', String(doubleFlatFontSize));
112+
// 使用 String.fromCodePoint 来支持辅助平面字符
113+
try {
114+
text.textContent = String.fromCodePoint(0x1d12b);
115+
} catch {
116+
// 如果系统不支持,则使用两个降号并排
117+
text.textContent = '\u266D\u266D';
118+
text.setAttribute('dx', '-2'); // 稍微调整位置
119+
}
198120
break;
199121
}
200122
}
123+
124+
parent.appendChild(text);
201125
}
202126

203127
/**

0 commit comments

Comments
 (0)