| @@ -57,7 +57,22 @@ const tops = (() => { |
| 57 | let y = RULER; | 57 | let y = RULER; |
| 58 | return Object.fromEntries(ROWS.map((r) => { const o = y; y += r.h + 2; return [r.key, o]; })) as Record<RowKey, number>; | 58 | return Object.fromEntries(ROWS.map((r) => { const o = y; y += r.h + 2; return [r.key, o]; })) as Record<RowKey, number>; |
| 59 | })(); | 59 | })(); |
| 60 | -const height = RULER + ROWS.reduce((n, r) => n + r.h + 2, 0); | 60 | +// les sons qui se chevauchent passent sur des sous-pistes : la piste des sons (la dernière) grandit d'autant |
| | 61 | +const SFX_SUB = 20; |
| | 62 | +const sfxLanes = computed(() => { |
| | 63 | + const lane = new Map<string, number>(); |
| | 64 | + const ends: number[] = []; |
| | 65 | + for (const s of props.tl.audio.filter((s) => s.role === "sfx").sort((a, b) => a.at - b.at)) { |
| | 66 | + let k = ends.findIndex((e) => e <= s.at); |
| | 67 | + if (k < 0) k = ends.push(0) - 1; |
| | 68 | + ends[k] = s.at + s.out - s.in; |
| | 69 | + lane.set(s.id, k); |
| | 70 | + } |
| | 71 | + return { lane, count: Math.max(1, ends.length) }; |
| | 72 | +}); |
| | 73 | +const sfxH = computed(() => sfxLanes.value.count * SFX_SUB + 4); |
| | 74 | +const rowHeight = (key: RowKey) => (key === "sfx" ? sfxH.value : rowH[key]); |
| | 75 | +const height = computed(() => RULER + ROWS.reduce((n, r) => n + rowHeight(r.key) + 2, 0)); |
| 61 | | 76 | |
| 62 | // --- zoom et défilement | 77 | // --- zoom et défilement |
| 63 | const scroller = ref<HTMLElement>(); | 78 | const scroller = ref<HTMLElement>(); |
| @@ -533,7 +548,7 @@ function motionState(id: string) { |
| 533 | <div class="tl" :class="{ blade: tool === 'blade' }"> | 548 | <div class="tl" :class="{ blade: tool === 'blade' }"> |
| 534 | <div class="labels" :style="{ width: `${LABEL_W}px`, height: `${height}px` }"> | 549 | <div class="labels" :style="{ width: `${LABEL_W}px`, height: `${height}px` }"> |
| 535 | <div class="corner" :style="{ height: `${RULER}px` }" /> | 550 | <div class="corner" :style="{ height: `${RULER}px` }" /> |
| 536 | - <div v-for="r in ROWS" :key="r.key" class="lab" :style="{ top: `${tops[r.key]}px`, height: `${r.h}px` }">{{ tr(`editing.lanes.${r.key}`) }}</div> | 551 | + <div v-for="r in ROWS" :key="r.key" class="lab" :style="{ top: `${tops[r.key]}px`, height: `${rowHeight(r.key)}px` }">{{ tr(`editing.lanes.${r.key}`) }}</div> |
| 537 | </div> | 552 | </div> |
| 538 | <div ref="scroller" class="scroller" :style="{ height: `${height + 12}px` }" | 553 | <div ref="scroller" class="scroller" :style="{ height: `${height + 12}px` }" |
| 539 | @wheel="onWheel" @scroll="scrollX = ($event.target as HTMLElement).scrollLeft"> | 554 | @wheel="onWheel" @scroll="scrollX = ($event.target as HTMLElement).scrollLeft"> |
| @@ -549,7 +564,7 @@ function motionState(id: string) { |
| 549 | </div> | 564 | </div> |
| 550 | | 565 | |
| 551 | <!-- fonds des pistes --> | 566 | <!-- fonds des pistes --> |
| 552 | - <div v-for="r in ROWS" :key="r.key" class="lane" :class="r.key" :style="{ top: `${tops[r.key]}px`, height: `${r.h}px`, width: `${x(total)}px` }" /> | 567 | + <div v-for="r in ROWS" :key="r.key" class="lane" :class="r.key" :style="{ top: `${tops[r.key]}px`, height: `${rowHeight(r.key)}px`, width: `${x(total)}px` }" /> |
| 553 | | 568 | |
| 554 | <!-- chapitres --> | 569 | <!-- chapitres --> |
| 555 | <div v-for="v in chaptersV" :key="v.m.id" class="chapter" :class="{ on: isSel('markers', v.m.id) }" | 570 | <div v-for="v in chaptersV" :key="v.m.id" class="chapter" :class="{ on: isSel('markers', v.m.id) }" |
| @@ -647,7 +662,7 @@ function motionState(id: string) { |
| 647 | <i class="edge r" @pointerdown="down($event, 'audio', v.it.id, 'right')" /> | 662 | <i class="edge r" @pointerdown="down($event, 'audio', v.it.id, 'right')" /> |
| 648 | </div> | 663 | </div> |
| 649 | <div v-for="v in sfxV" :key="v.it.id" class="item sfx" :class="{ on: isSel('audio', v.it.id) }" | 664 | <div v-for="v in sfxV" :key="v.it.id" class="item sfx" :class="{ on: isSel('audio', v.it.id) }" |
| 650 | - :style="{ top: `${tops.sfx + 2}px`, height: `${rowH.sfx - 4}px`, left: `${x(v.a)}px`, width: `${Math.max(4, x(v.b - v.a))}px` }" | 665 | + :style="{ top: `${tops.sfx + 2 + (sfxLanes.lane.get(v.it.id) ?? 0) * SFX_SUB}px`, height: `${SFX_SUB - 2}px`, left: `${x(v.a)}px`, width: `${Math.max(4, x(v.b - v.a))}px` }" |
| 651 | :title="`${nameOf(v.it.src)} · ${v.it.gain_db} dB`" @pointerdown="down($event, 'audio', v.it.id)" @dblclick="goTo(v.a)"> | 666 | :title="`${nameOf(v.it.src)} · ${v.it.gain_db} dB`" @pointerdown="down($event, 'audio', v.it.id)" @dblclick="goTo(v.a)"> |
| 652 | <i class="edge l" @pointerdown="down($event, 'audio', v.it.id, 'left')" /> | 667 | <i class="edge l" @pointerdown="down($event, 'audio', v.it.id, 'left')" /> |
| 653 | <span>{{ nameOf(v.it.src) }}</span> | 668 | <span>{{ nameOf(v.it.src) }}</span> |