| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819 |
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>简谱渲染引擎 - 开发进度可视化</title>
- <style>
- :root {
- --primary: #6366f1;
- --primary-light: #818cf8;
- --success: #10b981;
- --warning: #f59e0b;
- --error: #ef4444;
- --bg: #0f172a;
- --bg-card: #1e293b;
- --bg-hover: #334155;
- --text: #f1f5f9;
- --text-muted: #94a3b8;
- --border: #334155;
- }
-
- * { box-sizing: border-box; margin: 0; padding: 0; }
-
- body {
- font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
- background: var(--bg);
- color: var(--text);
- line-height: 1.6;
- min-height: 100vh;
- }
-
- .container {
- max-width: 1400px;
- margin: 0 auto;
- padding: 24px;
- }
-
- /* 头部 */
- header {
- text-align: center;
- padding: 40px 20px;
- background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #a855f7 100%);
- border-radius: 16px;
- margin-bottom: 32px;
- position: relative;
- overflow: hidden;
- }
-
- header::before {
- content: '';
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='0.05'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
- opacity: 0.3;
- }
-
- header h1 {
- font-size: 2.5rem;
- font-weight: 700;
- margin-bottom: 12px;
- position: relative;
- }
-
- header p {
- font-size: 1.1rem;
- opacity: 0.9;
- position: relative;
- }
-
- .version-badge {
- display: inline-block;
- background: rgba(255,255,255,0.2);
- padding: 6px 16px;
- border-radius: 20px;
- font-size: 0.9rem;
- margin-top: 16px;
- position: relative;
- }
-
- /* 进度概览 */
- .progress-overview {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
- gap: 16px;
- margin-bottom: 32px;
- }
-
- .progress-card {
- background: var(--bg-card);
- border-radius: 12px;
- padding: 20px;
- text-align: center;
- border: 1px solid var(--border);
- transition: transform 0.2s, box-shadow 0.2s;
- }
-
- .progress-card:hover {
- transform: translateY(-2px);
- box-shadow: 0 8px 24px rgba(0,0,0,0.3);
- }
-
- .progress-card .icon {
- font-size: 2rem;
- margin-bottom: 8px;
- }
-
- .progress-card .value {
- font-size: 2rem;
- font-weight: 700;
- color: var(--primary-light);
- }
-
- .progress-card .label {
- font-size: 0.85rem;
- color: var(--text-muted);
- margin-top: 4px;
- }
-
- .progress-card.success .value { color: var(--success); }
- .progress-card.warning .value { color: var(--warning); }
-
- /* 阶段进度 */
- .stages {
- background: var(--bg-card);
- border-radius: 16px;
- padding: 24px;
- margin-bottom: 32px;
- border: 1px solid var(--border);
- }
-
- .stages h2 {
- font-size: 1.3rem;
- margin-bottom: 20px;
- display: flex;
- align-items: center;
- gap: 8px;
- }
-
- .stage-list {
- display: flex;
- flex-direction: column;
- gap: 12px;
- }
-
- .stage-item {
- display: flex;
- align-items: center;
- gap: 16px;
- padding: 16px;
- background: var(--bg);
- border-radius: 10px;
- }
-
- .stage-status {
- width: 32px;
- height: 32px;
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 1rem;
- flex-shrink: 0;
- }
-
- .stage-status.done { background: var(--success); }
- .stage-status.active { background: var(--primary); animation: pulse 2s infinite; }
- .stage-status.pending { background: var(--border); color: var(--text-muted); }
-
- @keyframes pulse {
- 0%, 100% { box-shadow: 0 0 0 0 rgba(99, 102, 241, 0.4); }
- 50% { box-shadow: 0 0 0 8px rgba(99, 102, 241, 0); }
- }
-
- .stage-info {
- flex: 1;
- }
-
- .stage-info h3 {
- font-size: 1rem;
- margin-bottom: 4px;
- }
-
- .stage-info p {
- font-size: 0.85rem;
- color: var(--text-muted);
- }
-
- .stage-progress {
- width: 120px;
- text-align: right;
- }
-
- .stage-progress .bar {
- height: 6px;
- background: var(--border);
- border-radius: 3px;
- overflow: hidden;
- margin-bottom: 4px;
- }
-
- .stage-progress .bar-fill {
- height: 100%;
- background: linear-gradient(90deg, var(--primary), var(--success));
- transition: width 0.5s ease;
- }
-
- .stage-progress .text {
- font-size: 0.8rem;
- color: var(--text-muted);
- }
-
- /* 演示区域 */
- .demo-section {
- background: var(--bg-card);
- border-radius: 16px;
- padding: 24px;
- margin-bottom: 32px;
- border: 1px solid var(--border);
- }
-
- .demo-section h2 {
- font-size: 1.3rem;
- margin-bottom: 20px;
- display: flex;
- align-items: center;
- gap: 8px;
- }
-
- .demo-tabs {
- display: flex;
- gap: 8px;
- margin-bottom: 20px;
- flex-wrap: wrap;
- }
-
- .demo-tab {
- padding: 10px 20px;
- background: var(--bg);
- border: 1px solid var(--border);
- border-radius: 8px;
- cursor: pointer;
- font-size: 0.9rem;
- color: var(--text-muted);
- transition: all 0.2s;
- }
-
- .demo-tab:hover {
- background: var(--bg-hover);
- color: var(--text);
- }
-
- .demo-tab.active {
- background: var(--primary);
- border-color: var(--primary);
- color: white;
- }
-
- .demo-content {
- background: var(--bg);
- border-radius: 12px;
- padding: 24px;
- min-height: 300px;
- }
-
- /* SVG演示 */
- .svg-demo {
- display: flex;
- flex-wrap: wrap;
- gap: 24px;
- justify-content: center;
- }
-
- .note-demo {
- background: white;
- border-radius: 8px;
- padding: 20px;
- text-align: center;
- }
-
- .note-demo svg {
- display: block;
- margin: 0 auto 12px;
- }
-
- .note-demo .label {
- font-size: 0.8rem;
- color: #64748b;
- }
-
- /* 测试统计 */
- .test-stats {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
- gap: 16px;
- }
-
- .test-stat {
- background: var(--bg);
- border-radius: 10px;
- padding: 16px;
- display: flex;
- align-items: center;
- gap: 12px;
- }
-
- .test-stat .icon {
- width: 40px;
- height: 40px;
- border-radius: 8px;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 1.2rem;
- }
-
- .test-stat .icon.success { background: rgba(16, 185, 129, 0.2); }
- .test-stat .icon.info { background: rgba(99, 102, 241, 0.2); }
-
- .test-stat .info h4 {
- font-size: 1.2rem;
- font-weight: 600;
- }
-
- .test-stat .info p {
- font-size: 0.8rem;
- color: var(--text-muted);
- }
-
- /* 模块列表 */
- .modules-grid {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
- gap: 16px;
- }
-
- .module-card {
- background: var(--bg);
- border-radius: 10px;
- padding: 16px;
- border: 1px solid var(--border);
- }
-
- .module-card h4 {
- font-size: 0.95rem;
- margin-bottom: 8px;
- display: flex;
- align-items: center;
- gap: 8px;
- }
-
- .module-card .status-badge {
- padding: 2px 8px;
- border-radius: 4px;
- font-size: 0.7rem;
- font-weight: 500;
- }
-
- .status-badge.done { background: rgba(16, 185, 129, 0.2); color: var(--success); }
- .status-badge.wip { background: rgba(245, 158, 11, 0.2); color: var(--warning); }
- .status-badge.pending { background: rgba(148, 163, 184, 0.2); color: var(--text-muted); }
-
- .module-card p {
- font-size: 0.8rem;
- color: var(--text-muted);
- margin-bottom: 8px;
- }
-
- .module-card .tests {
- font-size: 0.75rem;
- color: var(--success);
- }
-
- /* 底部 */
- footer {
- text-align: center;
- padding: 24px;
- color: var(--text-muted);
- font-size: 0.85rem;
- }
-
- footer a {
- color: var(--primary-light);
- text-decoration: none;
- }
- </style>
- </head>
- <body>
- <div class="container">
- <header>
- <h1>🎵 简谱渲染引擎</h1>
- <p>JianpuRenderer - 高性能简谱渲染解决方案</p>
- <div class="version-badge">开发版本 v0.3.0 | 阶段3进行中</div>
- </header>
-
- <!-- 进度概览 -->
- <div class="progress-overview">
- <div class="progress-card success">
- <div class="icon">✅</div>
- <div class="value">193</div>
- <div class="label">测试用例通过</div>
- </div>
- <div class="progress-card">
- <div class="icon">📦</div>
- <div class="value">12</div>
- <div class="label">已实现模块</div>
- </div>
- <div class="progress-card">
- <div class="icon">📄</div>
- <div class="value">~3500</div>
- <div class="label">代码行数</div>
- </div>
- <div class="progress-card warning">
- <div class="icon">🎯</div>
- <div class="value">45%</div>
- <div class="label">总体进度</div>
- </div>
- </div>
-
- <!-- 阶段进度 -->
- <div class="stages">
- <h2>📊 开发阶段进度</h2>
- <div class="stage-list">
- <div class="stage-item">
- <div class="stage-status done">✓</div>
- <div class="stage-info">
- <h3>阶段0:准备工作</h3>
- <p>项目结构、数据模型、测试环境</p>
- </div>
- <div class="stage-progress">
- <div class="bar"><div class="bar-fill" style="width: 100%"></div></div>
- <div class="text">100% 完成</div>
- </div>
- </div>
- <div class="stage-item">
- <div class="stage-status done">✓</div>
- <div class="stage-info">
- <h3>阶段0.5:规范文档编写</h3>
- <p>MusicXML映射、渲染规范、VexFlow兼容</p>
- </div>
- <div class="stage-progress">
- <div class="bar"><div class="bar-fill" style="width: 100%"></div></div>
- <div class="text">100% 完成</div>
- </div>
- </div>
- <div class="stage-item">
- <div class="stage-status done">✓</div>
- <div class="stage-info">
- <h3>阶段1:核心解析器</h3>
- <p>Divisions处理、OSMD数据解析、时间计算</p>
- </div>
- <div class="stage-progress">
- <div class="bar"><div class="bar-fill" style="width: 100%"></div></div>
- <div class="text">100% 完成</div>
- </div>
- </div>
- <div class="stage-item">
- <div class="stage-status done">✓</div>
- <div class="stage-info">
- <h3>阶段2:布局引擎</h3>
- <p>小节布局、多声部对齐、行布局、Y坐标计算</p>
- </div>
- <div class="stage-progress">
- <div class="bar"><div class="bar-fill" style="width: 100%"></div></div>
- <div class="text">100% 完成</div>
- </div>
- </div>
- <div class="stage-item">
- <div class="stage-status active">▶</div>
- <div class="stage-info">
- <h3>阶段3:绘制引擎</h3>
- <p>音符绘制、线条绘制、歌词绘制、修饰符绘制</p>
- </div>
- <div class="stage-progress">
- <div class="bar"><div class="bar-fill" style="width: 25%"></div></div>
- <div class="text">25% 进行中</div>
- </div>
- </div>
- <div class="stage-item">
- <div class="stage-status pending">○</div>
- <div class="stage-info">
- <h3>阶段4:兼容层实现</h3>
- <p>OSMD兼容适配器、业务集成</p>
- </div>
- <div class="stage-progress">
- <div class="bar"><div class="bar-fill" style="width: 0%"></div></div>
- <div class="text">待开始</div>
- </div>
- </div>
- <div class="stage-item">
- <div class="stage-status pending">○</div>
- <div class="stage-info">
- <h3>阶段5:测试与优化</h3>
- <p>完整性测试、兼容性测试、性能优化</p>
- </div>
- <div class="stage-progress">
- <div class="bar"><div class="bar-fill" style="width: 0%"></div></div>
- <div class="text">待开始</div>
- </div>
- </div>
- </div>
- </div>
-
- <!-- 演示区域 -->
- <div class="demo-section">
- <h2>🎨 渲染演示</h2>
- <div class="demo-tabs">
- <button class="demo-tab active" onclick="showDemo('notes')">音符绘制</button>
- <button class="demo-tab" onclick="showDemo('octaves')">高低音点</button>
- <button class="demo-tab" onclick="showDemo('accidentals')">升降号</button>
- <button class="demo-tab" onclick="showDemo('durations')">附点音符</button>
- <button class="demo-tab" onclick="showDemo('complex')">复杂音符</button>
- </div>
- <div class="demo-content" id="demo-content">
- <!-- 动态内容 -->
- </div>
- </div>
-
- <!-- 测试统计 -->
- <div class="demo-section">
- <h2>🧪 测试覆盖</h2>
- <div class="test-stats">
- <div class="test-stat">
- <div class="icon success">✓</div>
- <div class="info">
- <h4>39 通过</h4>
- <p>DivisionsHandler.test.ts</p>
- </div>
- </div>
- <div class="test-stat">
- <div class="icon success">✓</div>
- <div class="info">
- <h4>30 通过</h4>
- <p>OSMDDataParser.test.ts</p>
- </div>
- </div>
- <div class="test-stat">
- <div class="icon success">✓</div>
- <div class="info">
- <h4>23 通过</h4>
- <p>TimeCalculator.test.ts</p>
- </div>
- </div>
- <div class="test-stat">
- <div class="icon success">✓</div>
- <div class="info">
- <h4>31 通过</h4>
- <p>MeasureLayoutEngine.test.ts</p>
- </div>
- </div>
- <div class="test-stat">
- <div class="icon success">✓</div>
- <div class="info">
- <h4>31 通过</h4>
- <p>MultiVoiceAligner.test.ts</p>
- </div>
- </div>
- <div class="test-stat">
- <div class="icon success">✓</div>
- <div class="info">
- <h4>40 通过</h4>
- <p>SystemLayoutEngine.test.ts</p>
- </div>
- </div>
- <div class="test-stat">
- <div class="icon success">✓</div>
- <div class="info">
- <h4>35 通过</h4>
- <p>NotePositionCalculator.test.ts</p>
- </div>
- </div>
- <div class="test-stat">
- <div class="icon success">✓</div>
- <div class="info">
- <h4>49 通过</h4>
- <p>NoteDrawer.test.ts</p>
- </div>
- </div>
- </div>
- </div>
-
- <!-- 模块列表 -->
- <div class="demo-section">
- <h2>📦 已实现模块</h2>
- <div class="modules-grid">
- <div class="module-card">
- <h4>DivisionsHandler <span class="status-badge done">完成</span></h4>
- <p>MusicXML divisions处理,时值转换</p>
- <div class="tests">✓ 39个测试通过</div>
- </div>
- <div class="module-card">
- <h4>OSMDDataParser <span class="status-badge done">完成</span></h4>
- <p>OSMD数据解析,音符提取</p>
- <div class="tests">✓ 30个测试通过</div>
- </div>
- <div class="module-card">
- <h4>TimeCalculator <span class="status-badge done">完成</span></h4>
- <p>时间计算,BPM处理,弱起检测</p>
- <div class="tests">✓ 23个测试通过</div>
- </div>
- <div class="module-card">
- <h4>MeasureLayoutEngine <span class="status-badge done">完成</span></h4>
- <p>小节布局,固定时间比例算法</p>
- <div class="tests">✓ 31个测试通过</div>
- </div>
- <div class="module-card">
- <h4>MultiVoiceAligner <span class="status-badge done">完成</span></h4>
- <p>多声部对齐,时间戳统一</p>
- <div class="tests">✓ 31个测试通过</div>
- </div>
- <div class="module-card">
- <h4>SystemLayoutEngine <span class="status-badge done">完成</span></h4>
- <p>行布局,自动换行,Y坐标</p>
- <div class="tests">✓ 40个测试通过</div>
- </div>
- <div class="module-card">
- <h4>NotePositionCalculator <span class="status-badge done">完成</span></h4>
- <p>音符Y坐标,声部间距</p>
- <div class="tests">✓ 35个测试通过</div>
- </div>
- <div class="module-card">
- <h4>NoteDrawer <span class="status-badge done">完成</span></h4>
- <p>音符SVG绘制,高低音点,附点,升降号</p>
- <div class="tests">✓ 49个测试通过</div>
- </div>
- <div class="module-card">
- <h4>LineDrawer <span class="status-badge wip">开发中</span></h4>
- <p>增时线、减时线、小节线绘制</p>
- <div class="tests">待实现</div>
- </div>
- <div class="module-card">
- <h4>LyricDrawer <span class="status-badge pending">待开始</span></h4>
- <p>歌词绘制,多遍歌词</p>
- <div class="tests">待实现</div>
- </div>
- <div class="module-card">
- <h4>ModifierDrawer <span class="status-badge pending">待开始</span></h4>
- <p>装饰音、连音符绘制</p>
- <div class="tests">待实现</div>
- </div>
- <div class="module-card">
- <h4>OSMDCompatibilityAdapter <span class="status-badge pending">待开始</span></h4>
- <p>OSMD兼容层,state.times生成</p>
- <div class="tests">待实现</div>
- </div>
- </div>
- </div>
-
- <footer>
- <p>简谱渲染引擎 | 开发中 | <a href="compare.html">对比测试</a> | <a href="collect-baseline.html">基准收集</a></p>
- </footer>
- </div>
-
- <script>
- // SVG命名空间
- const SVG_NS = 'http://www.w3.org/2000/svg';
-
- // 创建SVG容器
- function createSVG(width, height) {
- const svg = document.createElementNS(SVG_NS, 'svg');
- svg.setAttribute('width', width);
- svg.setAttribute('height', height);
- svg.setAttribute('viewBox', `0 0 ${width} ${height}`);
- return svg;
- }
-
- // 绘制音符
- function drawNote(x, y, pitch, options = {}) {
- const { octave = 0, dots = 0, accidental = null, fontSize = 24 } = options;
-
- const g = document.createElementNS(SVG_NS, 'g');
- g.setAttribute('transform', `translate(${x}, ${y})`);
-
- // 升降号
- if (accidental) {
- const accText = document.createElementNS(SVG_NS, 'text');
- accText.setAttribute('x', '-12');
- accText.setAttribute('y', '-8');
- accText.setAttribute('font-size', '12');
- accText.setAttribute('fill', '#333');
- accText.setAttribute('text-anchor', 'end');
- accText.textContent = accidental === 'sharp' ? '#' : accidental === 'flat' ? '♭' : '♮';
- g.appendChild(accText);
- }
-
- // 高音点
- if (octave > 0) {
- for (let i = 0; i < octave; i++) {
- const dot = document.createElementNS(SVG_NS, 'circle');
- dot.setAttribute('cx', '0');
- dot.setAttribute('cy', String(-fontSize/2 - 6 - i * 5));
- dot.setAttribute('r', '2.5');
- dot.setAttribute('fill', '#333');
- g.appendChild(dot);
- }
- }
-
- // 音符数字
- const text = document.createElementNS(SVG_NS, 'text');
- text.setAttribute('x', '0');
- text.setAttribute('y', '0');
- text.setAttribute('font-size', String(fontSize));
- text.setAttribute('font-family', 'Arial, sans-serif');
- text.setAttribute('fill', '#333');
- text.setAttribute('text-anchor', 'middle');
- text.setAttribute('dominant-baseline', 'central');
- text.textContent = String(pitch);
- g.appendChild(text);
-
- // 低音点
- if (octave < 0) {
- for (let i = 0; i < Math.abs(octave); i++) {
- const dot = document.createElementNS(SVG_NS, 'circle');
- dot.setAttribute('cx', '0');
- dot.setAttribute('cy', String(fontSize/2 + 6 + i * 5));
- dot.setAttribute('r', '2.5');
- dot.setAttribute('fill', '#333');
- g.appendChild(dot);
- }
- }
-
- // 附点
- if (dots > 0) {
- for (let i = 0; i < dots; i++) {
- const dot = document.createElementNS(SVG_NS, 'circle');
- dot.setAttribute('cx', String(fontSize * 0.4 + 4 + i * 6));
- dot.setAttribute('cy', '0');
- dot.setAttribute('r', '2');
- dot.setAttribute('fill', '#333');
- g.appendChild(dot);
- }
- }
-
- return g;
- }
-
- // 显示演示
- function showDemo(type) {
- // 更新标签状态
- document.querySelectorAll('.demo-tab').forEach(tab => tab.classList.remove('active'));
- event.target.classList.add('active');
-
- const container = document.getElementById('demo-content');
- container.innerHTML = '';
-
- const demos = {
- notes: [
- { pitch: 1, label: 'Do (1)' },
- { pitch: 2, label: 'Re (2)' },
- { pitch: 3, label: 'Mi (3)' },
- { pitch: 4, label: 'Fa (4)' },
- { pitch: 5, label: 'Sol (5)' },
- { pitch: 6, label: 'La (6)' },
- { pitch: 7, label: 'Si (7)' },
- { pitch: 0, label: '休止符 (0)' },
- ],
- octaves: [
- { pitch: 5, octave: 2, label: '高两个八度' },
- { pitch: 5, octave: 1, label: '高一个八度' },
- { pitch: 5, octave: 0, label: '中音' },
- { pitch: 5, octave: -1, label: '低一个八度' },
- { pitch: 5, octave: -2, label: '低两个八度' },
- ],
- accidentals: [
- { pitch: 4, accidental: 'sharp', label: '升号 #4' },
- { pitch: 7, accidental: 'flat', label: '降号 ♭7' },
- { pitch: 3, accidental: 'natural', label: '还原号 ♮3' },
- ],
- durations: [
- { pitch: 5, dots: 0, label: '四分音符' },
- { pitch: 5, dots: 1, label: '附点四分' },
- { pitch: 5, dots: 2, label: '双附点四分' },
- ],
- complex: [
- { pitch: 5, octave: 1, dots: 1, accidental: 'sharp', label: '升5·(高)' },
- { pitch: 3, octave: -1, dots: 2, accidental: 'flat', label: '降3··(低)' },
- { pitch: 1, octave: 2, dots: 1, label: '高高Do·' },
- { pitch: 7, octave: -2, accidental: 'sharp', label: '升Si(低低)' },
- ],
- };
-
- const items = demos[type] || demos.notes;
-
- const wrapper = document.createElement('div');
- wrapper.className = 'svg-demo';
-
- items.forEach(item => {
- const noteDemo = document.createElement('div');
- noteDemo.className = 'note-demo';
-
- const svg = createSVG(80, 80);
- const note = drawNote(40, 40, item.pitch, {
- octave: item.octave || 0,
- dots: item.dots || 0,
- accidental: item.accidental || null,
- });
- svg.appendChild(note);
-
- const label = document.createElement('div');
- label.className = 'label';
- label.textContent = item.label;
-
- noteDemo.appendChild(svg);
- noteDemo.appendChild(label);
- wrapper.appendChild(noteDemo);
- });
-
- container.appendChild(wrapper);
- }
-
- // 初始化
- document.addEventListener('DOMContentLoaded', () => {
- showDemo('notes');
- });
- </script>
- </body>
- </html>
|