style(phys-fx): редизайн EnergyLevels — белый фон, цветные полосы серий, info-box, glow на активных уровнях
This commit is contained in:
+107
-34
@@ -2026,48 +2026,121 @@ class EnergyLevels {
|
||||
render(){
|
||||
if (!this.el) return;
|
||||
const W = this.W, H = this.H;
|
||||
const pad = 40, leftLine = 200, rightLine = W - 100;
|
||||
let svg = util.svgFrame(W, H, {bg:'#fef3c7'});
|
||||
/* Уровни n=1..6 + ионизация */
|
||||
const padTop = 36, padBot = 30;
|
||||
const bandW = 16, bandLeft = 4;
|
||||
const leftLine = 170, rightLine = W - 80;
|
||||
const nMax = 6;
|
||||
/* y(n) — нелинейное расположение, E пропорционально -1/n² */
|
||||
const nFrom = this.n_from, nTo = this.n_to;
|
||||
|
||||
function E(n){ return -13.6 / (n * n); }
|
||||
function y(En){
|
||||
const minE = -13.6, maxE = 0;
|
||||
return pad + (maxE - En) / (maxE - minE) * (H - 2 * pad);
|
||||
function yE(En){
|
||||
const minE = -14.2, maxE = 0.4;
|
||||
return padTop + (maxE - En) / (maxE - minE) * (H - padTop - padBot);
|
||||
}
|
||||
/* Линия ионизации (E=0) */
|
||||
const y0 = y(0);
|
||||
svg += '<line x1="' + leftLine + '" y1="' + y0 + '" x2="' + rightLine + '" y2="' + y0 + '" stroke="#dc2626" stroke-width="1.6" stroke-dasharray="6 4"/>';
|
||||
svg += '<text x="' + (rightLine + 6) + '" y="' + (y0 + 4) + '" font-family="JetBrains Mono,monospace" font-size="11" fill="#dc2626" font-weight="700">E = 0 (ионизация)</text>';
|
||||
/* Уровни */
|
||||
|
||||
/* SVG открытие: белый фон, рамка */
|
||||
let svg = '<svg width="' + W + '" height="' + H + '" viewBox="0 0 ' + W + ' ' + H + '" xmlns="http://www.w3.org/2000/svg">';
|
||||
svg += '<rect x="0" y="0" width="' + W + '" height="' + H + '" rx="14" fill="#fff" stroke="#e2e8f0" stroke-width="1.2"/>';
|
||||
|
||||
/* Заголовок */
|
||||
svg += '<text x="' + (W / 2) + '" y="22" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" fill="#64748b">Eₙ = −13,6/n² эВ</text>';
|
||||
|
||||
/* Серии — цветные полосы между уровнями */
|
||||
const seriesBands = [
|
||||
{ n1: 1, n2: 2, fill: '#ede9fe', textFill: '#6d28d9', label: 'Лайман' },
|
||||
{ n1: 2, n2: 3, fill: '#dbeafe', textFill: '#1d4ed8', label: 'Бальмер' },
|
||||
{ n1: 3, n2: 6, fill: '#d1fae5', textFill: '#065f46', label: 'Пашен' },
|
||||
];
|
||||
for (const b of seriesBands){
|
||||
const yTop = yE(E(b.n2));
|
||||
const yBot = yE(E(b.n1));
|
||||
const bx = bandLeft;
|
||||
const bh = yBot - yTop;
|
||||
svg += '<rect x="' + bx + '" y="' + yTop.toFixed(1) + '" width="' + bandW + '" height="' + bh.toFixed(1) + '" rx="3" fill="' + b.fill + '"/>';
|
||||
/* Вертикальная метка внутри полосы */
|
||||
const ymid = ((yTop + yBot) / 2).toFixed(1);
|
||||
svg += '<text x="' + (bx + bandW / 2) + '" y="' + ymid + '" text-anchor="middle" dominant-baseline="middle" font-family="JetBrains Mono,monospace" font-size="8.5" fill="' + b.textFill + '" font-weight="700" transform="rotate(-90,' + (bx + bandW / 2) + ',' + ymid + ')">' + b.label + '</text>';
|
||||
}
|
||||
|
||||
/* Вертикальная ось слева от уровней */
|
||||
const axisX = leftLine - 3;
|
||||
svg += '<line x1="' + axisX + '" y1="' + padTop + '" x2="' + axisX + '" y2="' + (H - padBot) + '" stroke="#cbd5e1" stroke-width="1"/>';
|
||||
|
||||
/* Линия ионизации E=0 */
|
||||
const y0 = yE(0);
|
||||
svg += '<line x1="' + leftLine + '" y1="' + y0.toFixed(1) + '" x2="' + rightLine + '" y2="' + y0.toFixed(1) + '" stroke="#dc2626" stroke-width="1.4" stroke-dasharray="5 3"/>';
|
||||
svg += '<text x="' + (leftLine + 4) + '" y="' + (y0 - 3).toFixed(1) + '" font-family="JetBrains Mono,monospace" font-size="9" fill="#dc2626" font-weight="700">E = 0 (ионизация)</text>';
|
||||
|
||||
/* Уровни n=1..6 */
|
||||
for (let n = 1; n <= nMax; n++){
|
||||
const En = E(n);
|
||||
const yL = y(En);
|
||||
const active = (n === this.n_from || n === this.n_to);
|
||||
svg += '<line x1="' + leftLine + '" y1="' + yL.toFixed(1) + '" x2="' + rightLine + '" y2="' + yL.toFixed(1) + '" stroke="' + (active ? '#dc2626' : '#475569') + '" stroke-width="' + (active ? 2 : 1.2) + '"/>';
|
||||
svg += '<text x="' + (leftLine - 8) + '" y="' + (yL + 4).toFixed(1) + '" text-anchor="end" font-family="JetBrains Mono,monospace" font-size="11" fill="' + (active ? '#dc2626' : '#0f172a') + '" font-weight="' + (active ? '700' : '600') + '">n=' + n + '</text>';
|
||||
svg += '<text x="' + (rightLine + 6) + '" y="' + (yL + 4).toFixed(1) + '" font-family="JetBrains Mono,monospace" font-size="10" fill="#475569">' + En.toFixed(2) + ' эВ</text>';
|
||||
const yL = yE(En);
|
||||
const isFrom = (n === nFrom);
|
||||
const isTo = (n === nTo);
|
||||
const active = isFrom || isTo;
|
||||
const lineColor = isFrom ? '#4f46e5' : isTo ? '#0284c7' : '#94a3b8';
|
||||
const sw = active ? 2.4 : 1;
|
||||
|
||||
/* Glow под активными */
|
||||
if (active){
|
||||
svg += '<line x1="' + leftLine + '" y1="' + yL.toFixed(1) + '" x2="' + rightLine + '" y2="' + yL.toFixed(1) + '" stroke="' + lineColor + '" stroke-width="7" opacity="0.08"/>';
|
||||
}
|
||||
svg += '<line x1="' + leftLine + '" y1="' + yL.toFixed(1) + '" x2="' + rightLine + '" y2="' + yL.toFixed(1) + '" stroke="' + lineColor + '" stroke-width="' + sw + '"/>';
|
||||
|
||||
/* Метка n= слева от оси */
|
||||
const labelX = axisX - 4;
|
||||
const labelY = yL.toFixed(1);
|
||||
const labelColor = isFrom ? '#4f46e5' : isTo ? '#0284c7' : '#475569';
|
||||
const labelFW = active ? '800' : '500';
|
||||
if (active){
|
||||
svg += '<rect x="' + (labelX - 24) + '" y="' + (yL - 8).toFixed(1) + '" width="26" height="14" rx="3" fill="' + lineColor + '" opacity="0.12"/>';
|
||||
}
|
||||
svg += '<text x="' + labelX + '" y="' + (yL + 4).toFixed(1) + '" text-anchor="end" font-family="JetBrains Mono,monospace" font-size="11" fill="' + labelColor + '" font-weight="' + labelFW + '">n=' + n + '</text>';
|
||||
|
||||
/* Значения энергии справа */
|
||||
const eColor = active ? '#334155' : '#94a3b8';
|
||||
const eFW = active ? '600' : '400';
|
||||
svg += '<text x="' + (rightLine + 5) + '" y="' + (yL + 4).toFixed(1) + '" font-family="JetBrains Mono,monospace" font-size="9.5" fill="' + eColor + '" font-weight="' + eFW + '">' + En.toFixed(2) + 'эВ</text>';
|
||||
}
|
||||
/* Переход — стрелка */
|
||||
if (this.n_from !== this.n_to){
|
||||
const E1 = E(this.n_from), E2 = E(this.n_to);
|
||||
const y1 = y(E1), y2 = y(E2);
|
||||
const xT = (leftLine + rightLine) / 2;
|
||||
const color = E1 > E2 ? '#dc2626' : '#16a34a';
|
||||
svg += '<line x1="' + xT + '" y1="' + y1.toFixed(1) + '" x2="' + xT + '" y2="' + y2.toFixed(1) + '" stroke="' + color + '" stroke-width="2.6"/>';
|
||||
const dir = y2 < y1 ? -1 : 1;
|
||||
svg += '<polygon points="' + xT + ',' + y2.toFixed(1) + ' ' + (xT - 6) + ',' + (y2 + 10 * dir).toFixed(1) + ' ' + (xT + 6) + ',' + (y2 + 10 * dir).toFixed(1) + '" fill="' + color + '"/>';
|
||||
const dE = Math.abs(E1 - E2);
|
||||
svg += '<text x="' + (xT + 12) + '" y="' + ((y1 + y2) / 2).toFixed(1) + '" font-family="JetBrains Mono,monospace" font-size="12" fill="' + color + '" font-weight="700">hν = ' + dE.toFixed(2) + ' эВ</text>';
|
||||
|
||||
/* Стрелка перехода */
|
||||
if (nFrom !== nTo){
|
||||
const Ef = E(nFrom), Et = E(nTo);
|
||||
const yf = yE(Ef), yt = yE(Et);
|
||||
const emission = Ef > Et; /* испускание: падаем вниз по энергии */
|
||||
const arrowColor = emission ? '#ef4444' : '#16a34a';
|
||||
const xT = leftLine + (rightLine - leftLine) * 0.42;
|
||||
|
||||
/* Линия стрелки */
|
||||
svg += '<line x1="' + xT.toFixed(1) + '" y1="' + yf.toFixed(1) + '" x2="' + xT.toFixed(1) + '" y2="' + yt.toFixed(1) + '" stroke="' + arrowColor + '" stroke-width="2.2"/>';
|
||||
|
||||
/* Наконечник в точке назначения yt */
|
||||
const dir = yt < yf ? -1 : 1; /* направление стрелки */
|
||||
const tip = yt.toFixed(1);
|
||||
const tipBase = (yt + 10 * dir).toFixed(1);
|
||||
svg += '<polygon points="' + xT.toFixed(1) + ',' + tip + ' ' + (xT - 6).toFixed(1) + ',' + tipBase + ' ' + (xT + 6).toFixed(1) + ',' + tipBase + '" fill="' + arrowColor + '"/>';
|
||||
|
||||
/* Info-box */
|
||||
const dE = Math.abs(Ef - Et);
|
||||
const lam = 1240 / dE;
|
||||
svg += '<text x="' + (xT + 12) + '" y="' + ((y1 + y2) / 2 + 16).toFixed(1) + '" font-family="JetBrains Mono,monospace" font-size="11" fill="' + color + '">λ ≈ ' + lam.toFixed(0) + ' нм</text>';
|
||||
const boxW = 88, boxH = lam >= 380 && lam <= 700 ? 48 : 40;
|
||||
const yMid = (yf + yt) / 2;
|
||||
let bx = xT + 10;
|
||||
if (bx + boxW > W - 4) bx = xT - boxW - 10;
|
||||
const boxFill = emission ? '#fff5f5' : '#f0fdf4';
|
||||
const boxStroke = emission ? '#fca5a5' : '#86efac';
|
||||
svg += '<rect x="' + bx.toFixed(1) + '" y="' + (yMid - boxH / 2).toFixed(1) + '" width="' + boxW + '" height="' + boxH + '" rx="6" fill="' + boxFill + '" stroke="' + boxStroke + '" stroke-width="1.2"/>';
|
||||
svg += '<text x="' + (bx + 7).toFixed(1) + '" y="' + (yMid - boxH / 2 + 15).toFixed(1) + '" font-family="JetBrains Mono,monospace" font-size="11.5" fill="' + arrowColor + '" font-weight="800">hν = ' + dE.toFixed(3) + ' эВ</text>';
|
||||
svg += '<text x="' + (bx + 7).toFixed(1) + '" y="' + (yMid - boxH / 2 + 29).toFixed(1) + '" font-family="JetBrains Mono,monospace" font-size="11" fill="' + arrowColor + '">λ ≈ ' + lam.toFixed(0) + ' нм</text>';
|
||||
/* Цветовая метка для видимого диапазона */
|
||||
if (lam >= 380 && lam <= 700){
|
||||
const hue = Math.round(270 - (lam - 380) / (700 - 380) * 270);
|
||||
const cy = yMid - boxH / 2 + 39;
|
||||
svg += '<defs><linearGradient id="vis_g" x1="0%" y1="0%" x2="100%" y2="0%"><stop offset="0%" stop-color="hsl(' + hue + ',90%,55%)"/><stop offset="100%" stop-color="hsl(' + (hue - 20) + ',90%,60%)"/></linearGradient></defs>';
|
||||
svg += '<rect x="' + (bx + 7).toFixed(1) + '" y="' + cy.toFixed(1) + '" width="' + (boxW - 14) + '" height="6" rx="3" fill="url(#vis_g)"/>';
|
||||
}
|
||||
}
|
||||
/* Серии */
|
||||
svg += '<text x="14" y="' + (y(E(1)) + 4) + '" font-family="JetBrains Mono,monospace" font-size="10" fill="#9d174d" font-weight="700">Лайман →</text>';
|
||||
svg += '<text x="14" y="' + (y(E(2)) + 4) + '" font-family="JetBrains Mono,monospace" font-size="10" fill="#9d174d" font-weight="700">Бальмер →</text>';
|
||||
svg += '<text x="14" y="' + (y(E(3)) + 4) + '" font-family="JetBrains Mono,monospace" font-size="10" fill="#9d174d" font-weight="700">Пашен →</text>';
|
||||
svg += '<text x="' + (W/2) + '" y="22" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="11" fill="#475569" font-weight="700">E_n = -13,6/n² (атом водорода)</text>';
|
||||
|
||||
svg += '</svg>';
|
||||
this.el.innerHTML = svg;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user