|
|
@@ -229,19 +229,30 @@
|
|
|
background: var(--ink);
|
|
|
color: #4af626;
|
|
|
font-family: 'Roboto Mono', monospace;
|
|
|
- font-size: 24px;
|
|
|
- font-weight: 700;
|
|
|
padding: 10px 16px;
|
|
|
- text-align: right;
|
|
|
border-radius: 3px;
|
|
|
letter-spacing: 1px;
|
|
|
text-shadow: 0 0 8px rgba(74,246,38,0.6);
|
|
|
margin: 12px 0;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 6px;
|
|
|
+ }
|
|
|
+ .indicator .row {
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
align-items: baseline;
|
|
|
}
|
|
|
- .indicator small { font-size: 11px; color: #8aa; letter-spacing: 1px; }
|
|
|
+ .indicator .main {
|
|
|
+ font-size: 24px;
|
|
|
+ font-weight: 700;
|
|
|
+ }
|
|
|
+ .indicator .secondary {
|
|
|
+ font-size: 16px;
|
|
|
+ opacity: 0.8;
|
|
|
+ font-weight: 700;
|
|
|
+ }
|
|
|
+ .indicator small { font-size: 11px; color: #8aa; letter-spacing: 1px; font-weight: 400; }
|
|
|
.feedback {
|
|
|
background: rgba(26,74,106,0.06);
|
|
|
border-left: 3px solid var(--blue);
|
|
|
@@ -444,8 +455,10 @@
|
|
|
<div class="viz-caption">Линии магнитного поля: чем ближе магнит, тем гуще и ярче · гальванометр показывает ЭДС</div>
|
|
|
</div>
|
|
|
<div class="indicator">
|
|
|
- <small>ЭДС = −dФ/dt</small>
|
|
|
- <span><span id="emfValue">0.0</span> мВ</span>
|
|
|
+ <div class="row">
|
|
|
+ <small>ЭДС = −dФ/dt</small>
|
|
|
+ <span class="main"><span id="emfValue">0.0</span> мВ</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
<div class="feedback" id="inductionFeedback">Зажмите магнит и двигайте его.</div>
|
|
|
<div class="notebook">
|
|
|
@@ -542,7 +555,7 @@
|
|
|
<p>Источник — провод с переменным током. Среда — магнитное поле. Приёмник — петля сигнального провода. Меняйте ток, частоту, расстояние и площадь петли. Провод можно перетаскивать мышью, углы петли — тянуть и менять площадь.</p>
|
|
|
<div class="viz">
|
|
|
<canvas id="inductiveViz" width="800" height="440"></canvas>
|
|
|
- <div class="viz-caption">Плотность линий = интенсивность поля · мерцание = частота · стрелка в петле = наведённая ЭДС</div>
|
|
|
+ <div class="viz-caption">Плотность линий = интенсивность поля · мерцание = частота · длина дуги в петле = амплитуда ЭДС</div>
|
|
|
</div>
|
|
|
<div class="two-col">
|
|
|
<div>
|
|
|
@@ -567,8 +580,14 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="indicator">
|
|
|
- <small>Мгновенная ЭДС = −dФ/dt</small>
|
|
|
- <span><span id="indResult">0.0</span> мВ</span>
|
|
|
+ <div class="row">
|
|
|
+ <small>Мгновенная ЭДС = −dФ/dt</small>
|
|
|
+ <span class="main"><span id="indResult">0.0</span> мВ</span>
|
|
|
+ </div>
|
|
|
+ <div class="row">
|
|
|
+ <small>Амплитуда (средняя)</small>
|
|
|
+ <span class="secondary"><span id="indPeak">0.0</span> мВ</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
<div class="feedback" id="indFeedback">Двигайте ползунки или перетаскивайте провод и углы петли.</div>
|
|
|
<div class="notebook">
|
|
|
@@ -583,7 +602,7 @@
|
|
|
<p>Источник — провод с переменным напряжением. Среда — электрическое поле. Приёмник — провод или плата. Меняйте напряжение, частоту, расстояние и площадь. Провод можно перетаскивать мышью, углы приёмника — тянуть и менять площадь.</p>
|
|
|
<div class="viz">
|
|
|
<canvas id="capacitiveViz" width="800" height="440"></canvas>
|
|
|
- <div class="viz-caption">Плотность линий = напряжённость поля E · мерцание = частота · стрелка на приёмнике = наведённый заряд</div>
|
|
|
+ <div class="viz-caption">Плотность линий = напряжённость поля E · мерцание = частота · длина стрелки = амплитуда наводки</div>
|
|
|
</div>
|
|
|
<div class="two-col">
|
|
|
<div>
|
|
|
@@ -608,8 +627,14 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="indicator">
|
|
|
- <small>Мгновенное наведённое напряжение</small>
|
|
|
- <span><span id="capResult">0.0</span> мВ</span>
|
|
|
+ <div class="row">
|
|
|
+ <small>Мгновенное наведённое напряжение</small>
|
|
|
+ <span class="main"><span id="capResult">0.0</span> мВ</span>
|
|
|
+ </div>
|
|
|
+ <div class="row">
|
|
|
+ <small>Амплитуда (средняя)</small>
|
|
|
+ <span class="secondary"><span id="capPeak">0.0</span> мВ</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
<div class="feedback" id="capFeedback">Двигайте ползунки или перетаскивайте провод и углы приёмника.</div>
|
|
|
<div class="notebook">
|
|
|
@@ -645,8 +670,10 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="indicator">
|
|
|
- <small>Помеха в сигнальной цепи</small>
|
|
|
- <span><span id="galResult">0.0</span> мВ</span>
|
|
|
+ <div class="row">
|
|
|
+ <small>Помеха в сигнальной цепи</small>
|
|
|
+ <span class="main"><span id="galResult">0.0</span> мВ</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
<div class="feedback" id="galFeedback">Двигайте ползунки, чтобы увидеть, как меняется помеха.</div>
|
|
|
<div class="notebook">
|
|
|
@@ -1544,10 +1571,13 @@
|
|
|
drawInd8Distance(ctx, wireRight, lx1, (ly1 + ly2) / 2, distCm);
|
|
|
|
|
|
let emfInst = 0;
|
|
|
+ let emfPeak = 0;
|
|
|
if (f > 1 && I > 0 && areaCm2 > 0) {
|
|
|
- emfInst = 0.02 * I * areaCm2 * apparentF * Math.sin(ind8.phase) / Math.max(1, distCm);
|
|
|
+ const base = 0.02 * I * areaCm2 * apparentF / Math.max(1, distCm);
|
|
|
+ emfPeak = Math.min(base, 99.9);
|
|
|
+ emfInst = emfPeak * Math.sin(ind8.phase);
|
|
|
}
|
|
|
- drawInd8EmfArrow(ctx, lx1, ly1, lx2, ly2, emfInst);
|
|
|
+ drawInd8EmfArrow(ctx, lx1, ly1, lx2, ly2, emfInst, emfPeak);
|
|
|
|
|
|
ctx.fillStyle = '#4a5a7a';
|
|
|
ctx.font = '11px "Roboto Mono", monospace';
|
|
|
@@ -1557,6 +1587,8 @@
|
|
|
|
|
|
const emfEl = document.getElementById('indResult');
|
|
|
if (emfEl) emfEl.textContent = emfInst.toFixed(1);
|
|
|
+ const peakEl = document.getElementById('indPeak');
|
|
|
+ if (peakEl) peakEl.textContent = emfPeak.toFixed(1);
|
|
|
|
|
|
const fbEl = document.getElementById('indFeedback');
|
|
|
if (fbEl) {
|
|
|
@@ -1706,22 +1738,28 @@
|
|
|
ctx.fillText('d = ' + distCm.toFixed(1) + ' см', (x1 + x2) / 2, y - 6);
|
|
|
}
|
|
|
|
|
|
- function drawInd8EmfArrow(ctx, x1, y1, x2, y2, emf) {
|
|
|
+ function drawInd8EmfArrow(ctx, x1, y1, x2, y2, emf, peak) {
|
|
|
const maxEmf = 30;
|
|
|
- const ratio = Math.min(1, Math.abs(emf) / maxEmf);
|
|
|
- if (ratio < 0.03) return;
|
|
|
+ const instRatio = Math.min(1, Math.abs(emf) / maxEmf);
|
|
|
+ const peakRatio = Math.min(1, Math.abs(peak) / maxEmf);
|
|
|
+ if (peakRatio < 0.02 && instRatio < 0.02) return;
|
|
|
|
|
|
const cx = (x1 + x2) / 2;
|
|
|
const cy = (y1 + y2) / 2;
|
|
|
- const r = Math.min(x2 - x1, y2 - y1) * 0.28;
|
|
|
- if (r < 8) return;
|
|
|
+ const baseR = Math.min(x2 - x1, y2 - y1) * 0.28;
|
|
|
+ if (baseR < 8) return;
|
|
|
|
|
|
- const alpha = 0.35 + ratio * 0.6;
|
|
|
- const clockwise = emf >= 0;
|
|
|
- const lw = 2 + ratio * 3;
|
|
|
+ // Длина дуги зависит от АМПЛИТУДЫ (peakRatio), а не от мгновенного значения
|
|
|
+ // Это убирает «мельтешение» длины при высокой частоте
|
|
|
+ const spanFactor = 0.25 + 0.75 * peakRatio;
|
|
|
+ const lw = 1.5 + instRatio * 3.5;
|
|
|
+ const alpha = 0.25 + instRatio * 0.7;
|
|
|
|
|
|
- const A = -Math.PI * 0.85;
|
|
|
- const B = Math.PI * 0.55;
|
|
|
+ const A = -Math.PI * 0.85 * spanFactor;
|
|
|
+ const B = Math.PI * 0.55 * spanFactor;
|
|
|
+ const r = baseR;
|
|
|
+
|
|
|
+ const clockwise = emf >= 0;
|
|
|
|
|
|
ctx.save();
|
|
|
ctx.translate(cx, cy);
|
|
|
@@ -1746,7 +1784,7 @@
|
|
|
tang = A - Math.PI / 2;
|
|
|
}
|
|
|
|
|
|
- const size = 6 + ratio * 6;
|
|
|
+ const size = 5 + instRatio * 7;
|
|
|
const tipX = hx + Math.cos(tang) * size;
|
|
|
const tipY = hy + Math.sin(tang) * size;
|
|
|
const perpX = Math.cos(tang + Math.PI / 2);
|
|
|
@@ -1767,12 +1805,13 @@
|
|
|
ctx.restore();
|
|
|
ctx.lineCap = 'butt';
|
|
|
|
|
|
+ // Подпись: амплитуда (средняя), чтобы не мельтешило
|
|
|
ctx.fillStyle = '#8b1a1a';
|
|
|
- ctx.font = 'bold 12px "Roboto Mono", monospace';
|
|
|
+ ctx.font = 'bold 11px "Roboto Mono", monospace';
|
|
|
ctx.textAlign = 'center';
|
|
|
ctx.textBaseline = 'middle';
|
|
|
const sign = emf >= 0 ? '+' : '−';
|
|
|
- ctx.fillText(sign + Math.abs(emf).toFixed(1) + ' мВ', cx, cy + r + 18);
|
|
|
+ ctx.fillText(sign + ' ' + Math.abs(peak).toFixed(1) + ' мВ', cx, cy + r + 18);
|
|
|
}
|
|
|
|
|
|
// === СЦЕНА 9: Ёмкостная наводка ===
|
|
|
@@ -2053,7 +2092,15 @@
|
|
|
drawCap8Source(ctx, s, Uinst);
|
|
|
drawCap8Receiver(ctx, r, Uinst, Uabs, areaCm2);
|
|
|
drawCap8Distance(ctx, srcRight, recvLeft, (overlapTop + overlapBot) / 2, distCm);
|
|
|
- drawCap8CurrentArrow(ctx, srcRight, recvLeft, overlapTop, overlapBot, Uinst, f);
|
|
|
+
|
|
|
+ let vInst = 0;
|
|
|
+ let vPeak = 0;
|
|
|
+ if (f > 1 && U > 0 && areaCm2 > 0) {
|
|
|
+ const base = 0.02 * U * areaCm2 * apparentF / Math.max(1, distCm * distCm);
|
|
|
+ vPeak = Math.min(base, 99.9);
|
|
|
+ vInst = vPeak * Math.sin(cap8.phase);
|
|
|
+ }
|
|
|
+ drawCap8CurrentArrow(ctx, srcRight, recvLeft, overlapTop, overlapBot, vInst, vPeak, f);
|
|
|
|
|
|
ctx.fillStyle = '#4a5a7a';
|
|
|
ctx.font = '11px "Roboto Mono", monospace';
|
|
|
@@ -2061,15 +2108,10 @@
|
|
|
ctx.textBaseline = 'alphabetic';
|
|
|
ctx.fillText('S = ' + areaCm2.toFixed(0) + ' см²', r.x1 - 4, r.y1 - 24);
|
|
|
|
|
|
- let vInst = 0;
|
|
|
- if (f > 1 && U > 0 && areaCm2 > 0) {
|
|
|
- vInst = 0.02 * U * areaCm2 * apparentF * Math.sin(cap8.phase) / Math.max(1, distCm * distCm);
|
|
|
- if (vInst > 99.9) vInst = 99.9;
|
|
|
- if (vInst < -99.9) vInst = -99.9;
|
|
|
- }
|
|
|
-
|
|
|
const resEl = document.getElementById('capResult');
|
|
|
if (resEl) resEl.textContent = vInst.toFixed(1);
|
|
|
+ const peakEl = document.getElementById('capPeak');
|
|
|
+ if (peakEl) peakEl.textContent = vPeak.toFixed(1);
|
|
|
|
|
|
const fbEl = document.getElementById('capFeedback');
|
|
|
if (fbEl) {
|
|
|
@@ -2239,21 +2281,26 @@
|
|
|
ctx.fillText('d = ' + distCm.toFixed(1) + ' см', (x1 + x2) / 2, y - 6);
|
|
|
}
|
|
|
|
|
|
- function drawCap8CurrentArrow(ctx, x1, x2, top, bot, Uinst, f) {
|
|
|
+ function drawCap8CurrentArrow(ctx, x1, x2, top, bot, inst, peak, f) {
|
|
|
if (f <= 1) return;
|
|
|
const midX = (x1 + x2) / 2;
|
|
|
const midY = (top + bot) / 2;
|
|
|
if (!isFinite(midY)) return;
|
|
|
|
|
|
- const Iinst = -Math.sin(cap8.phase);
|
|
|
- const ratio = Math.abs(Iinst);
|
|
|
- const alpha = 0.4 + ratio * 0.55;
|
|
|
- const lw = 2 + ratio * 2;
|
|
|
+ const instRatio = Math.min(1, Math.abs(inst) / 30);
|
|
|
+ const peakRatio = Math.min(1, Math.abs(peak) / 30);
|
|
|
+ if (peakRatio < 0.02 && instRatio < 0.02) return;
|
|
|
+
|
|
|
+ const alpha = 0.25 + instRatio * 0.7;
|
|
|
+ const lw = 1.5 + instRatio * 2.5;
|
|
|
|
|
|
- const arrowLen = Math.min(40, (x2 - x1) * 0.3);
|
|
|
- if (arrowLen < 10) return;
|
|
|
+ const baseLen = Math.min(40, (x2 - x1) * 0.3);
|
|
|
+ if (baseLen < 6) return;
|
|
|
+ // Длина стрелки зависит от АМПЛИТУДЫ (peak), а не от мгновенного значения
|
|
|
+ // Это убирает «мельтешение» длины
|
|
|
+ const arrowLen = baseLen * (0.25 + 0.75 * peakRatio);
|
|
|
|
|
|
- const dir = Iinst >= 0 ? 1 : -1;
|
|
|
+ const dir = inst >= 0 ? 1 : -1;
|
|
|
const tipX = midX + dir * arrowLen;
|
|
|
const tailX = midX - dir * arrowLen * 0.4;
|
|
|
|
|
|
@@ -2265,7 +2312,7 @@
|
|
|
ctx.lineTo(tipX, midY);
|
|
|
ctx.stroke();
|
|
|
|
|
|
- const size = 6 + ratio * 5;
|
|
|
+ const size = 5 + instRatio * 5;
|
|
|
ctx.beginPath();
|
|
|
ctx.moveTo(tipX, midY);
|
|
|
ctx.lineTo(tipX - dir * size, midY - size * 0.6);
|
|
|
@@ -2275,11 +2322,13 @@
|
|
|
ctx.fill();
|
|
|
ctx.lineCap = 'butt';
|
|
|
|
|
|
+ // Подпись: амплитуда (средняя), чтобы не мельтешило
|
|
|
ctx.fillStyle = '#8b1a1a';
|
|
|
- ctx.font = 'bold 10px "Roboto Mono", monospace';
|
|
|
+ ctx.font = 'bold 11px "Roboto Mono", monospace';
|
|
|
ctx.textAlign = 'center';
|
|
|
ctx.textBaseline = 'top';
|
|
|
- ctx.fillText('ток смещения', midX, midY + 12);
|
|
|
+ const sign = inst >= 0 ? '+' : '−';
|
|
|
+ ctx.fillText(sign + ' ' + Math.abs(peak).toFixed(1) + ' мВ', midX, midY + 14);
|
|
|
}
|
|
|
|
|
|
// === СЦЕНА 10: Гальваническая наводка ===
|