// Nauka Czytania ;(function () { // ms between line advances for each speed level (0 = manual) const SPEEDS_MS = [0, 3500, 2000, 1000] const SPEED_LABELS = ['Pauza', 'Wolno', 'Średnio', 'Szybko'] let yOffset = 0 let lineH = 80 // recalculated after render let maxOffset = 0 let speedIdx = 0 let autoTimer = null const listWrap = document.getElementById('list-wrap') const readWrap = document.getElementById('read-wrap') const textList = document.getElementById('text-list') const customInput = document.getElementById('custom-input') const customStartBtn = document.getElementById('custom-start-btn') const readBackBtn = document.getElementById('read-back-btn') const readTitleEl = document.getElementById('read-title') const speedBtn = document.getElementById('speed-btn') const readViewport = document.getElementById('read-viewport') const readTextEl = document.getElementById('read-text') const nextLineBtn = document.getElementById('next-line-btn') const progressBar = document.getElementById('read-progress-bar-inner') // ── Load text list from dyktanda.json ──────────────────────────────────── fetch('json/dyktanda.json') .then(r => r.json()) .then(data => { data.forEach(item => { const btn = document.createElement('button') btn.className = 'list-item-btn' btn.textContent = item.name btn.addEventListener('click', () => startReading(item.name, item.text)) textList.appendChild(btn) }) }) .catch(() => { textList.innerHTML = '
Nie udało się wczytać tekstów.
' }) customStartBtn.addEventListener('click', () => { const txt = customInput.value.trim() if (!txt) return startReading('Własny', txt) }) // ── Start reading ───────────────────────────────────────────────────────── function startReading(title, text) { yOffset = 0 speedIdx = 0 clearInterval(autoTimer) autoTimer = null readTitleEl.textContent = title readTextEl.textContent = text readTextEl.style.transform = 'translateY(0)' speedBtn.textContent = SPEED_LABELS[0] progressBar.style.width = '0%' listWrap.classList.add('hidden') readWrap.classList.remove('hidden') // measure real line height after layout requestAnimationFrame(() => { requestAnimationFrame(() => { const cs = getComputedStyle(readTextEl) const lhVal = cs.lineHeight lineH = (lhVal === 'normal') ? parseFloat(cs.fontSize) * 1.35 : parseFloat(lhVal) maxOffset = Math.max(0, readTextEl.offsetHeight - lineH) updateNextBtn() updateProgressBar() }) }) } // ── Back to list ────────────────────────────────────────────────────────── readBackBtn.addEventListener('click', () => { const active = yOffset > 0 || autoTimer !== null if (active && !confirm('Wrócić do listy tekstów?')) return clearInterval(autoTimer) autoTimer = null readWrap.classList.add('hidden') listWrap.classList.remove('hidden') // Reset speed to manual on exit speedIdx = 0; }) // ── Speed selector ──────────────────────────────────────────────────────── speedBtn.addEventListener('click', () => { clearInterval(autoTimer) autoTimer = null speedIdx = (speedIdx + 1) % SPEEDS_MS.length speedBtn.textContent = SPEED_LABELS[speedIdx] if (SPEEDS_MS[speedIdx] > 0 && yOffset < maxOffset) { autoTimer = setInterval(advanceLine, SPEEDS_MS[speedIdx]) } }) // ── Manual line advance ─────────────────────────────────────────────────── nextLineBtn.addEventListener('click', advanceLine) // ── Core scroll logic ───────────────────────────────────────────────────── function advanceLine() { if (yOffset >= maxOffset) { stopAutoAtEnd() return } yOffset = Math.min(yOffset + lineH, maxOffset) readTextEl.style.transform = `translateY(${-yOffset}px)` updateNextBtn() updateProgressBar() if (yOffset >= maxOffset) stopAutoAtEnd() } function stopAutoAtEnd() { if (autoTimer !== null) { clearInterval(autoTimer) autoTimer = null speedIdx = 0 speedBtn.textContent = SPEED_LABELS[0] } updateNextBtn() } function updateNextBtn() { nextLineBtn.disabled = yOffset >= maxOffset } function updateProgressBar() { const progress = maxOffset > 0 ? (yOffset / maxOffset) * 100 : 100 progressBar.style.width = `${progress}%` } })()