From 35f7addb0336f44a2418604d19e245ea12e93d01 Mon Sep 17 00:00:00 2001 From: luciano Date: Fri, 19 Jun 2026 17:18:19 +0200 Subject: [PATCH] Scrolling text: 'side edges' mode with characters stacked vertically on both borders Co-Authored-By: Claude Opus 4.8 (1M context) --- src/control.html | 1 + src/output.html | 6 ++++++ src/output.js | 50 +++++++++++++++++++++++++++++++++++------------- src/style.css | 30 +++++++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 13 deletions(-) diff --git a/src/control.html b/src/control.html index 25c66de..3f4234b 100644 --- a/src/control.html +++ b/src/control.html @@ -257,6 +257,7 @@ + diff --git a/src/output.html b/src/output.html index f4a1412..01d8abf 100644 --- a/src/output.html +++ b/src/output.html @@ -29,6 +29,12 @@
+ +
+
+
+
+
🎛 In attesa dell'audio…
Usa il pannello di controllo sull'altro schermo
diff --git a/src/output.js b/src/output.js index 7b2a529..bf7a714 100644 --- a/src/output.js +++ b/src/output.js @@ -127,7 +127,7 @@ function composeFrame() { } recCtx.globalAlpha = 1; - if (ticker.classList.contains('show')) { + if (ticker.classList.contains('show') || sideText.classList.contains('show')) { recCtx.textBaseline = 'middle'; for (const span of tickerLetters()) { const txt = span.textContent; @@ -233,6 +233,9 @@ function setLogo(i, url) { const ticker = $('#ticker'); const tickerTrack = $('#ticker-track'); const tickCopies = ticker.querySelectorAll('.tick-copy'); +const sideText = $('#side-text'); +const sideCopies = sideText.querySelectorAll('.side-copy'); +let tickerVisible = false, tickerDir = 'h'; function buildLetters(txt) { const full = (txt || '') + ' • '; let html = ''; @@ -243,9 +246,24 @@ function buildLetters(txt) { } return html; } -function setTickerText(t) { const h = buildLetters(t); tickCopies.forEach(c => c.innerHTML = h); } -function setTickerSpeed(m) { tickerTrack.style.setProperty('--ticker-dur', (18 / m) + 's'); } -function tickerLetters() { return tickerTrack.querySelectorAll('.tl'); } +function setTickerText(t) { + const h = buildLetters(t); + tickCopies.forEach(c => c.innerHTML = h); + sideCopies.forEach(c => c.innerHTML = h); +} +function setTickerSpeed(m) { document.documentElement.style.setProperty('--ticker-dur', (18 / m) + 's'); } +function tickerLetters() { + let out = []; + if (ticker.classList.contains('show')) out = out.concat([...tickerTrack.querySelectorAll('.tl')]); + if (sideText.classList.contains('show')) out = out.concat([...sideText.querySelectorAll('.tl')]); + return out; +} +function styleBoth(prop, value) { tickerTrack.style[prop] = value; sideText.style[prop] = value; } +function updateTickerVis() { + const sides = tickerDir === 'sides'; + ticker.classList.toggle('show', tickerVisible && !sides); + sideText.classList.toggle('show', tickerVisible && sides); +} ticker.classList.add('pos-bottom'); ticker.classList.add('dir-h'); setTickerSpeed(1); @@ -384,23 +402,29 @@ djv.onControl(async (m) => { case 'logoOpacity': logos[m.index].style.opacity = m.value; break; case 'tickerText': setTickerText(m.text); break; - case 'tickerOn': ticker.classList.toggle('show', m.on); break; + case 'tickerOn': tickerVisible = m.on; updateTickerVis(); break; case 'tickerPos': ticker.classList.remove('pos-bottom', 'pos-top', 'pos-middle'); ticker.classList.add('pos-' + m.pos); break; case 'tickerSpeed': setTickerSpeed(m.mult); break; case 'tickerDir': - ticker.classList.remove('dir-h', 'dir-vup', 'dir-vdown'); - ticker.classList.add('dir-' + m.value); + tickerDir = m.value; + if (m.value !== 'sides') { + ticker.classList.remove('dir-h', 'dir-vup', 'dir-vdown'); + ticker.classList.add('dir-' + m.value); + } + updateTickerVis(); break; - case 'tickerFont': tickerTrack.style.fontFamily = m.value; break; - case 'tickerSize': tickerTrack.style.fontSize = m.value + 'vh'; break; - case 'tickerWeight': tickerTrack.style.fontWeight = m.on ? '800' : '400'; break; - case 'tickerColor': tickerTrack.style.color = m.value; break; + case 'tickerFont': styleBoth('fontFamily', m.value); break; + case 'tickerSize': styleBoth('fontSize', m.value + 'vh'); break; + case 'tickerWeight': styleBoth('fontWeight', m.on ? '800' : '400'); break; + case 'tickerColor': styleBoth('color', m.value); break; case 'tickerFx': - tickerTrack.classList.remove('fx-updown', 'fx-wave', 'fx-zoom', 'fx-flash', 'fx-rotate'); - if (m.value && m.value !== 'none') tickerTrack.classList.add('fx-' + m.value); + [tickerTrack, sideText].forEach(el => { + el.classList.remove('fx-updown', 'fx-wave', 'fx-zoom', 'fx-flash', 'fx-rotate'); + if (m.value && m.value !== 'none') el.classList.add('fx-' + m.value); + }); break; } }); diff --git a/src/style.css b/src/style.css index a216fe3..1cfe070 100644 --- a/src/style.css +++ b/src/style.css @@ -529,6 +529,36 @@ body.output { cursor: none; background: #000; } #ticker.dir-vdown #ticker-track { animation: ticker-scroll-vdown var(--ticker-dur, 18s) linear infinite; } @keyframes ticker-scroll-vup { from { transform: translateY(0); } to { transform: translateY(-50%); } } @keyframes ticker-scroll-vdown { from { transform: translateY(-50%); } to { transform: translateY(0); } } + +/* Side text: vertical stacked characters scrolling on both edges */ +#side-text { + position: fixed; + inset: 0; + z-index: 6; + pointer-events: none; + display: none; + font-size: 6vh; + font-weight: 800; + color: #fff; + text-transform: uppercase; +} +#side-text.show { display: block; } +.side-col { position: absolute; top: 0; bottom: 0; overflow: hidden; display: flex; } +.side-col.left { left: 1.5vw; } +.side-col.right { right: 1.5vw; } +.side-track { display: flex; flex-direction: column; align-items: center; will-change: transform; } +.side-col.left .side-track { animation: side-scroll-down var(--ticker-dur, 18s) linear infinite; } +.side-col.right .side-track { animation: side-scroll-up var(--ticker-dur, 18s) linear infinite; } +.side-copy { display: flex; flex-direction: column; align-items: center; padding: 18vh 0; } +.side-copy .tl { + display: block; + line-height: 1.0; + letter-spacing: 0; + text-shadow: 0 0 calc(8px + var(--ticker-glow, 0px)) rgba(140,180,255,0.9), + 0 2px 20px rgba(0,0,0,0.8); +} +@keyframes side-scroll-up { from { transform: translateY(0); } to { transform: translateY(-50%); } } +@keyframes side-scroll-down { from { transform: translateY(-50%); } to { transform: translateY(0); } } .tl { display: inline-block; white-space: pre;