fix(textbooks): подписи на числовой прямой §2 больше не перекрываются
Было: 3 уровня (i%3) × 12px — близко стоящие √2 √3 √5 π √15 наложились друг на друга. Стало: - Точки сортируются по координате - Для каждой подписи ищется минимальный уровень БЕЗ перекрытия с уже размещёнными (с учётом ширины метки ~44px и шкалы в пикселях) - До 9 уровней по 20px вверх от оси - От подписи к точке идёт тонкая линия-выноска (0.45 opacity) - Box-shadow на метках для разделения если плотно Также: ось перемещена с y=60 на y=100 — больше места сверху для уровней. Контейнер 120 → 140px высоты.
This commit is contained in:
@@ -2238,7 +2238,7 @@ function buildP2(){
|
||||
`)}
|
||||
|
||||
${widget('Числовая прямая с корнями', 'VISUAL', 'Нажимайте кнопки, чтобы поставить точки √n и π на прямую. Точные десятичные значения — в подсказках.', `
|
||||
<div id="nl-line" style="position:relative;height:120px;background:var(--card);border:1px solid var(--border);border-radius:9px;margin-bottom:14px"></div>
|
||||
<div id="nl-line" style="position:relative;height:140px;background:var(--card);border:1px solid var(--border);border-radius:9px;margin-bottom:14px"></div>
|
||||
<div class="row-c">
|
||||
<button class="btn" onclick="nlAdd(Math.sqrt(2),'√2')">√2</button>
|
||||
<button class="btn" onclick="nlAdd(Math.sqrt(3),'√3')">√3</button>
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user