From 6864db5b9429213b81c513acb065d1de948a7595 Mon Sep 17 00:00:00 2001 From: Maxim Dolgolyov Date: Wed, 27 May 2026 13:10:20 +0300 Subject: [PATCH] =?UTF-8?q?fix(textbooks):=20=D0=BF=D0=BE=D0=B4=D0=BF?= =?UTF-8?q?=D0=B8=D1=81=D0=B8=20=D0=BD=D0=B0=20=D1=87=D0=B8=D1=81=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=D0=BE=D0=B9=20=D0=BF=D1=80=D1=8F=D0=BC=D0=BE=D0=B9?= =?UTF-8?q?=20=C2=A72=20=D0=B1=D0=BE=D0=BB=D1=8C=D1=88=D0=B5=20=D0=BD?= =?UTF-8?q?=D0=B5=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BA=D1=80=D1=8B=D0=B2=D0=B0?= =?UTF-8?q?=D1=8E=D1=82=D1=81=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Было: 3 уровня (i%3) × 12px — близко стоящие √2 √3 √5 π √15 наложились друг на друга. Стало: - Точки сортируются по координате - Для каждой подписи ищется минимальный уровень БЕЗ перекрытия с уже размещёнными (с учётом ширины метки ~44px и шкалы в пикселях) - До 9 уровней по 20px вверх от оси - От подписи к точке идёт тонкая линия-выноска (0.45 opacity) - Box-shadow на метках для разделения если плотно Также: ось перемещена с y=60 на y=100 — больше места сверху для уровней. Контейнер 120 → 140px высоты. --- frontend/textbooks/algebra_8.html | 49 ++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/frontend/textbooks/algebra_8.html b/frontend/textbooks/algebra_8.html index 0606b55..fe2eafb 100644 --- a/frontend/textbooks/algebra_8.html +++ b/frontend/textbooks/algebra_8.html @@ -2238,7 +2238,7 @@ function buildP2(){ `)} ${widget('Числовая прямая с корнями', 'VISUAL', 'Нажимайте кнопки, чтобы поставить точки √n и π на прямую. Точные десятичные значения — в подсказках.', ` -
+
@@ -2442,25 +2442,54 @@ function initNumLine(){ function nlRender(){ const line = document.getElementById('nl-line'); line.innerHTML = ''; - const w = line.clientWidth; - // axis 0..15 - const axis = el('div', {style:'position:absolute;top:60px;left:3%;right:3%;height:2px;background:var(--text)'}); + // axis 0..15 — нижняя часть, чтобы сверху было место под подписи в несколько уровней + const AXIS_Y = 100; + const axis = el('div', {style:`position:absolute;top:${AXIS_Y}px;left:3%;right:3%;height:2px;background:var(--text)`}); line.appendChild(axis); // ticks 0..15 const lo = 0, hi = 15; for(let i = lo; i <= hi; i++){ const x = 3 + (i - lo) / (hi - lo) * 94; - const t = el('div', {style:`position:absolute;top:54px;left:${x}%;width:2px;height:14px;background:var(--text);transform:translateX(-50%)`}); + const t = el('div', {style:`position:absolute;top:${AXIS_Y-6}px;left:${x}%;width:2px;height:14px;background:var(--text);transform:translateX(-50%)`}); line.appendChild(t); - const lab = el('div', {style:`position:absolute;top:72px;left:${x}%;transform:translateX(-50%);font-size:.74rem;font-family:'JetBrains Mono',monospace;color:var(--muted)`}, ''+i); + const lab = el('div', {style:`position:absolute;top:${AXIS_Y+12}px;left:${x}%;transform:translateX(-50%);font-size:.74rem;font-family:'JetBrains Mono',monospace;color:var(--muted)`}, ''+i); line.appendChild(lab); } - NL_POINTS.forEach((p,i)=>{ - const x = 3 + (p.v - lo) / (hi - lo) * 94; - const pt = el('div', {style:`position:absolute;top:54px;left:${x}%;width:14px;height:14px;background:var(--pri);border-radius:50%;transform:translateX(-50%);border:2.5px solid var(--card);box-shadow:0 0 0 2px var(--pri);cursor:pointer;z-index:2`}); + // Сортируем по x для расчёта уровней без перекрытия + const w = line.clientWidth || 600; + const labelHalfPxApprox = 22; // полу-ширина подписи в пикселях + const placed = []; // {xPct, level} + const sorted = NL_POINTS.map((p,i)=>({...p, _i:i})).sort((a,b)=>a.v-b.v); + sorted.forEach(p=>{ + const xPct = 3 + (p.v - lo) / (hi - lo) * 94; + // Найти минимальный уровень без перекрытия с placed + let level = 0; + while(true){ + const conflict = placed.some(q=>{ + if(q.level !== level) return false; + const dxPx = Math.abs(q.xPct - xPct) / 100 * w; + return dxPx < (labelHalfPxApprox * 2 + 4); + }); + if(!conflict) break; + level++; + if(level > 8) break; + } + placed.push({xPct, level}); + p._level = level; + }); + sorted.forEach(p=>{ + const xPct = 3 + (p.v - lo) / (hi - lo) * 94; + const pt = el('div', {style:`position:absolute;top:${AXIS_Y-6}px;left:${xPct}%;width:14px;height:14px;background:var(--pri);border-radius:50%;transform:translateX(-50%);border:2.5px solid var(--card);box-shadow:0 0 0 2px var(--pri);cursor:pointer;z-index:2`}); pt.title = p.lab + ' ≈ ' + p.v.toFixed(4); line.appendChild(pt); - const lab = el('div', {style:`position:absolute;top:${20 + (i%3)*12}px;left:${x}%;transform:translateX(-50%);font-size:.78rem;font-weight:700;color:var(--pri);background:var(--card);padding:2px 6px;border-radius:5px;border:1px solid var(--pri);font-family:'JetBrains Mono',monospace`}, p.lab); + // Подпись сверху, нескольких уровней; линия-выноска вниз к точке + const labY = 4 + p._level * 20; + const stemTop = labY + 18; + const stemHeight = AXIS_Y - stemTop - 2; + if(stemHeight > 0){ + line.appendChild(el('div', {style:`position:absolute;top:${stemTop}px;left:${xPct}%;width:1px;height:${stemHeight}px;background:var(--pri);opacity:.45;transform:translateX(-50%);z-index:1`})); + } + const lab = el('div', {style:`position:absolute;top:${labY}px;left:${xPct}%;transform:translateX(-50%);font-size:.78rem;font-weight:700;color:var(--pri);background:var(--card);padding:2px 7px;border-radius:5px;border:1px solid var(--pri);font-family:'JetBrains Mono',monospace;white-space:nowrap;z-index:3;box-shadow:0 1px 4px rgba(0,0,0,.08)`}, p.lab); line.appendChild(lab); }); }