版本: 2.0
最后更新: 2026-01-30
状态: ✅ 已完成
// 导入渲染器
import { JianpuRenderer, createJianpuRenderer } from '@/jianpu-renderer';
// 1. 创建渲染器
const renderer = createJianpuRenderer('container-id', {
systemWidth: 800,
noteFontSize: 24,
debug: false,
});
// 2. 加载乐谱数据(来自OSMD解析)
await renderer.load(osmdInstance);
// 3. 渲染
renderer.render();
import { JianpuRenderer } from '@/jianpu-renderer';
// 方式1:使用容器ID
const renderer1 = new JianpuRenderer('my-container');
// 方式2:使用DOM元素
const container = document.getElementById('my-container');
const renderer2 = new JianpuRenderer(container);
// 方式3:使用工厂函数(推荐)
const renderer3 = createJianpuRenderer('my-container', {
systemWidth: 800,
drawLyrics: true,
});
// 加载OSMD实例
await renderer.load(osmd);
// 渲染到容器
renderer.render();
// 获取渲染统计
const stats = renderer.getStats();
console.log(`渲染耗时: ${stats.totalTime}ms`);
console.log(`音符数: ${stats.noteCount}`);
// 更新配置会自动重新渲染
renderer.updateConfig({
noteColor: '#0000ff',
noteFontSize: 28,
});
简谱渲染引擎 v2.0 新增了12个专业绘制器,支持丰富的乐谱特性。
| 特性分类 | 包含内容 | 绘制器 |
|---|---|---|
| 连线系统 | 延音线、圆滑线 | SlurTieDrawer |
| 连音符 | 三连音、五连音等 | TupletDrawer |
| 反复记号 | 反复线、跳房子、D.C./D.S. | RepeatDrawer |
| 力度记号 | pp~fff、渐强渐弱 | DynamicsDrawer |
| 演奏技法 | 顿音、重音、延长等 | ArticulationDrawer |
| 和弦 | 多音符垂直堆叠 | ChordDrawer |
| 装饰音 | 倚音、颤音、波音、回音 | OrnamentDrawer |
| 速度标记 | BPM、速度术语、rit./accel. | TempoDrawer |
| 八度记号 | 8va、8vb、15ma、15mb | OctaveShiftDrawer |
| 踏板 | Ped.、释放、换踩 | PedalDrawer |
| 字符谱 | 指法、弦号、技法 | TablatureDrawer |
| 打击乐 | 鼓组符号、音头类型 | PercussionDrawer |
当音符包含相应的修饰符时,渲染器会自动绘制:
// 音符数据中包含演奏技法
const note: JianpuNote = {
id: 'note-1',
pitch: 1,
duration: 1,
x: 100,
y: 60,
modifiers: {
articulations: ['staccato', 'accent'], // 自动绘制顿音和重音
ornaments: ['trill'], // 自动绘制颤音
graceNotes: { // 自动绘制倚音
notes: [{ pitch: 5, octave: 0, duration: 0.25 }],
slash: true,
},
},
};
// 小节数据中包含反复记号
const measure: JianpuMeasure = {
// ...基础属性
volta: { number: 1, text: '1.', type: 'start' }, // 自动绘制跳房子
repeatMark: { type: 'dc', text: 'D.C.' }, // 自动绘制反复标记
};
每个绘制器都可以通过渲染器实例获取,用于自定义绘制。
const renderer = createJianpuRenderer(container);
// 获取各种绘制器
const slurTieDrawer = renderer.getSlurTieDrawer();
const tupletDrawer = renderer.getTupletDrawer();
const repeatDrawer = renderer.getRepeatDrawer();
const dynamicsDrawer = renderer.getDynamicsDrawer();
const articulationDrawer = renderer.getArticulationDrawer();
const chordDrawer = renderer.getChordDrawer();
const ornamentDrawer = renderer.getOrnamentDrawer();
const tempoDrawer = renderer.getTempoDrawer();
const octaveShiftDrawer = renderer.getOctaveShiftDrawer();
const pedalDrawer = renderer.getPedalDrawer();
const tablatureDrawer = renderer.getTablatureDrawer();
const percussionDrawer = renderer.getPercussionDrawer();
const slurTieDrawer = renderer.getSlurTieDrawer();
// 绘制延音线
const tieGroup = slurTieDrawer.drawTie(
{ x: 100, y: 60 }, // 起始位置
{ x: 150, y: 60 }, // 结束位置
'above' // 曲线位置: 'above' | 'below'
);
// 绘制圆滑线
const slurGroup = slurTieDrawer.drawSlur(
{ x: 100, y: 60 },
{ x: 200, y: 70 },
'below'
);
const tupletDrawer = renderer.getTupletDrawer();
// 绘制三连音
const tupletGroup = tupletDrawer.drawTuplet({
notes: [note1, note2, note3],
actualNotes: 3,
normalNotes: 2,
showBracket: true,
showNumber: true,
position: 'above',
});
const dynamicsDrawer = renderer.getDynamicsDrawer();
// 绘制力度文字
const dynGroup = dynamicsDrawer.drawDynamic('f', 100, 80); // type: 'ppp'|'pp'|'p'|'mp'|'mf'|'f'|'ff'|'fff'|'sfz'|'fp'
// 绘制渐强楔形
const crescGroup = dynamicsDrawer.drawHairpin('crescendo', 100, 200, 80);
// 绘制渐弱楔形
const dimGroup = dynamicsDrawer.drawHairpin('diminuendo', 100, 200, 80);
const chordDrawer = renderer.getChordDrawer();
// 从音符数组创建和弦
const chordGroup = chordDrawer.drawChordFromNotes(
[note1, note2, note3], // JianpuNote数组
100, // X坐标
60 // Y坐标
);
// 使用ChordInfo对象
const chordGroup2 = chordDrawer.drawChord({
notes: [
{ pitch: 1, octave: 0 },
{ pitch: 3, octave: 0 },
{ pitch: 5, octave: 0 },
],
x: 100,
y: 60,
duration: 1,
});
const ornamentDrawer = renderer.getOrnamentDrawer();
// 绘制装饰音
const trillGroup = ornamentDrawer.drawTrill(100, 60, 50); // x, y, 波浪线宽度
const mordentGroup = ornamentDrawer.drawMordent(100, 60);
const turnGroup = ornamentDrawer.drawTurn(100, 60);
const tremoloGroup = ornamentDrawer.drawTremolo(100, 60, 3); // 3条线
// 绘制倚音
const graceGroup = ornamentDrawer.drawGraceNotes(
[{ pitch: 5, octave: 0 }, { pitch: 6, octave: 0 }], // 倚音音符
100, // 主音符X
60, // 主音符Y
true // 是否有斜杠(短倚音)
);
const tempoDrawer = renderer.getTempoDrawer();
// 绘制BPM标记
const bpmGroup = tempoDrawer.drawSimpleBpmMark(120, 50, 30);
// 绘制完整BPM标记(指定音符时值)
const bpmGroup2 = tempoDrawer.drawBpmMark(
{ noteValue: 'quarter', bpm: 120 },
50, 30
);
// 绘制速度术语
const tempoWord = tempoDrawer.drawTempoWord('Allegro', 50, 30, true); // 显示BPM参考
// 绘制渐变速度
const ritGroup = tempoDrawer.drawRitardando(100, 60, 80); // rit.
const accelGroup = tempoDrawer.drawAccelerando(100, 60, 80); // accel.
const octaveDrawer = renderer.getOctaveShiftDrawer();
// 绘制8va(高八度)
const octave8va = octaveDrawer.draw8va(50, 200, 60);
// 绘制8vb(低八度)
const octave8vb = octaveDrawer.draw8vb(50, 200, 100);
// 绘制15ma(高两个八度)
const octave15ma = octaveDrawer.draw15ma(50, 200, 60);
// 绘制loco(恢复原位)
const locoGroup = octaveDrawer.drawLoco(200, 60);
const pedalDrawer = renderer.getPedalDrawer();
// 绘制踏板区间
const pedalGroup = pedalDrawer.drawPedalRange({
type: 'sustain', // 'sustain' | 'sostenuto' | 'soft'
startX: 50,
endX: 200,
y: 120,
changePoints: [100, 150], // 换踩点
});
// 单独绘制踏板标记
const pedStart = pedalDrawer.drawPedalStart(50, 120);
const pedStop = pedalDrawer.drawPedalStop(200, 120);
const pedChange = pedalDrawer.drawPedalChange(125, 120);
const tablatureDrawer = renderer.getTablatureDrawer();
// 绘制指法
const fingerGroup = tablatureDrawer.drawLeftHandFingering(2, 100, 50); // 左手2指
const rightGroup = tablatureDrawer.drawRightHandFingering('i', 100, 50); // 右手i指
// 绘制弦号
const stringGroup = tablatureDrawer.drawString({ x: 100, y: 50, string: 1 });
// 绘制把位
const posGroup = tablatureDrawer.drawPosition({ x: 50, y: 30, position: 5, endX: 200 });
// 绘制技法
const hammerOn = tablatureDrawer.drawHammerOn(100, 50, 150);
const pullOff = tablatureDrawer.drawPullOff(100, 50, 150);
const slide = tablatureDrawer.drawSlide(100, 50, 150);
const percussionDrawer = renderer.getPercussionDrawer();
// 绘制鼓组符号
const bassGroup = percussionDrawer.drawBassDrum(100, 60);
const snareGroup = percussionDrawer.drawSnare(150, 60);
const hihatGroup = percussionDrawer.drawHiHat(200, 40);
// 绘制音头
const xHead = percussionDrawer.drawNoteHead({ type: 'x', x: 100, y: 60 });
const diamondHead = percussionDrawer.drawNoteHead({ type: 'diamond', x: 150, y: 60 });
// 绘制技法
const openGroup = percussionDrawer.drawOpen(100, 40);
const closedGroup = percussionDrawer.drawClosed(150, 40);
// 绘制滚奏
const rollGroup = percussionDrawer.drawRoll({ startX: 100, endX: 150, y: 60 });
interface JianpuRendererOptions {
// 布局配置
quarterNoteSpacing?: number; // 四分音符间距(默认: 50)
measurePadding?: number; // 小节内边距(默认: 20)
systemWidth?: number; // 行宽度(默认: 800)
systemHeight?: number; // 行高度(默认: 150)
// 显示配置
drawPartNames?: boolean; // 是否绘制声部名称
drawLyrics?: boolean; // 是否绘制歌词(默认: true)
musicColor?: string; // 音符颜色
// 字体配置
noteFontSize?: number; // 音符字体大小(默认: 24)
fontFamily?: string; // 字体
// 调试配置
debug?: boolean; // 调试模式(默认: false)
}
const renderer = createJianpuRenderer(container, {
systemWidth: 1000,
systemHeight: 180,
noteFontSize: 28,
musicColor: '#333333',
drawLyrics: true,
debug: false,
});
// 获取SVG元素进行自定义操作
const svg = renderer.getSVGElement();
// 获取所有音符
const notes = renderer.getAllNotes();
// 获取所有小节
const measures = renderer.getAllMeasures();
// 使用绘制器添加自定义元素
const dynamicsDrawer = renderer.getDynamicsDrawer();
const customDynamic = dynamicsDrawer.drawDynamic('ff', 200, 100);
svg?.appendChild(customDynamic);
每个绘制器都提供统计信息:
const articulationDrawer = renderer.getArticulationDrawer();
// 执行一些绘制操作后...
const stats = articulationDrawer.getStats();
console.log(`绘制数量: ${stats.articulationsDrawn}`);
console.log(`绘制耗时: ${stats.drawTime}ms`);
// 重置统计
articulationDrawer.resetStats();
// 渲染器提供OSMD兼容接口
const cursor = renderer.cursor; // 光标适配器
const graphicSheet = renderer.GraphicSheet; // 图形表适配器
const sheet = renderer.Sheet; // 乐谱信息
const rules = renderer.EngravingRules; // 渲染规则
// 启用调试模式
const renderer = createJianpuRenderer(container, {
debug: true, // 启用调试日志
});
const stats = renderer.getStats();
console.log(`解析时间: ${stats.parseTime}ms`);
console.log(`布局时间: ${stats.layoutTime}ms`);
console.log(`绘制时间: ${stats.drawTime}ms`);
console.log(`总时间: ${stats.totalTime}ms`);
const svg = renderer.getSVGElement();
const dynamicsDrawer = renderer.getDynamicsDrawer();
// 创建新元素
const element = dynamicsDrawer.drawDynamic('f', 100, 80);
// 添加到SVG
svg?.appendChild(element);
const articulationDrawer = renderer.getArticulationDrawer();
// 更新绘制器配置
articulationDrawer.updateConfig({
color: '#0000ff',
});
文档版本: 2.0
最后更新: 2026-01-30
维护者: 开发团队