feat(labs): новая симуляция «Гонка с задачами» — кинематика 1D с геймификацией
race.js (1357 строк): - 8 сценариев: встречи (поезд+машина, 2 лодки), догон (мотоциклист, поезда), кто первый (авто vs поезд, 3 спортсмена), свободное падение vs парашют, обгон с разгоном - Иконки movers inline SVG: car, train, bike, moto, runner, ball, boat - Аналитический поиск точки встречи: линейный + квадратный + численный (если задержка) - Стробоскоп положений каждые 0.5-1 с - Canvas-графики x(t) и v(t) с маркером встречи (красная точка + бейдж) - Проверка ответа с tolerance ±5%, verdict зелёный/красный - Слайдеры x₀/v₀/a для каждого мовера + кнопка 'Сброс к сценарию' - Stats bar 5 ячеек: Время, t_встречи, x_встречи, Лидер, Расстояние между UI (lab.html): - Sticky quick-bar: Старт/Пауза/Сброс - Карточка вопроса вверху + answer-bar внизу с input + verdict - Collapsible-секции (race-acc): Параметры мовера 1, 2, 3, Настройки Интеграция: - lab-init.js: 'sim-race' в ALL_SIM_BODIES + роутинг _openRace - admin/sims.js: запись в ADMIN_SIMS (cat: Физика, title: 'Гонка с задачами') - lab-glue.js: P_RACE preset с SVG-превью (дорожка + кривые x(t)) - lab.css: ~200 строк стилей .race-* по паттерну elec/geo/dyn-acc
This commit is contained in:
@@ -2293,3 +2293,309 @@ canvas[data-draggable]:active { cursor: grabbing; }
|
||||
border-color: #06D6E0 !important;
|
||||
box-shadow: 0 0 8px rgba(6,214,224,0.3);
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════
|
||||
RACE SIM — Гонка с задачами (кинематика 1D)
|
||||
═══════════════════════════════════════════════════════════ */
|
||||
.race-sim-host {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.race-root {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #0b0b1a;
|
||||
font-family: 'Manrope', system-ui, sans-serif;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Question bar */
|
||||
.race-question-bar {
|
||||
flex: 0 0 auto;
|
||||
padding: 10px 16px 8px;
|
||||
background: rgba(155,93,229,0.07);
|
||||
border-bottom: 1px solid rgba(155,93,229,0.18);
|
||||
font-size: .88rem;
|
||||
color: rgba(255,255,255,0.88);
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.race-question-text {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Body: panel + scene */
|
||||
.race-body {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Left panel */
|
||||
.race-panel {
|
||||
width: 260px;
|
||||
flex: 0 0 260px;
|
||||
overflow-y: auto;
|
||||
background: rgba(255,255,255,0.018);
|
||||
border-right: 1px solid var(--border);
|
||||
padding: 8px 10px 12px;
|
||||
font-size: .82rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
/* Quick bar */
|
||||
.race-quick-bar {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.race-quick-bar .mag-mode-btn {
|
||||
flex: 1;
|
||||
font-size: .80rem;
|
||||
padding: 8px 4px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Accordion */
|
||||
.race-acc {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
margin-bottom: 6px;
|
||||
background: rgba(255,255,255,0.02);
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.race-acc > summary {
|
||||
cursor: pointer;
|
||||
list-style: none;
|
||||
padding: 9px 12px 9px 30px;
|
||||
font-family: 'Unbounded', sans-serif;
|
||||
font-size: .78rem;
|
||||
font-weight: 700;
|
||||
color: var(--text);
|
||||
letter-spacing: .04em;
|
||||
text-transform: uppercase;
|
||||
position: relative;
|
||||
user-select: none;
|
||||
transition: background .15s;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.race-acc[open] > summary { border-radius: 10px 10px 0 0; }
|
||||
.race-acc > summary:hover { background: rgba(255,255,255,0.04); }
|
||||
.race-acc > summary::-webkit-details-marker { display: none; }
|
||||
.race-acc > summary::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
top: 50%;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 5px solid currentColor;
|
||||
border-top: 4px solid transparent;
|
||||
border-bottom: 4px solid transparent;
|
||||
transform: translateY(-50%);
|
||||
transition: transform .18s;
|
||||
opacity: .65;
|
||||
}
|
||||
.race-acc[open] > summary::before {
|
||||
transform: translateY(-50%) rotate(90deg);
|
||||
}
|
||||
.race-acc-body {
|
||||
padding: 8px 10px 10px;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
|
||||
/* Scenario cards */
|
||||
.race-scenarios-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
}
|
||||
.race-scene-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 7px 10px;
|
||||
border-radius: 8px;
|
||||
border: 1.5px solid transparent;
|
||||
background: rgba(255,255,255,0.025);
|
||||
cursor: pointer;
|
||||
transition: border-color .15s, background .15s;
|
||||
}
|
||||
.race-scene-card:hover {
|
||||
background: rgba(255,255,255,0.05);
|
||||
border-color: rgba(155,93,229,0.25);
|
||||
}
|
||||
.race-scene-card.active {
|
||||
border-color: rgba(155,93,229,0.55);
|
||||
background: rgba(155,93,229,0.1);
|
||||
}
|
||||
.race-scene-icons {
|
||||
display: flex;
|
||||
gap: 3px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.race-scene-title {
|
||||
font-size: .78rem;
|
||||
font-weight: 600;
|
||||
color: var(--text);
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
/* Mover param card */
|
||||
.race-mover-card {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 8px 10px;
|
||||
margin-bottom: 8px;
|
||||
background: rgba(255,255,255,0.02);
|
||||
}
|
||||
.race-mover-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-bottom: 6px;
|
||||
font-size: .80rem;
|
||||
}
|
||||
|
||||
/* Scene: canvas + graph + answer */
|
||||
.race-scene-wrap {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
min-width: 0;
|
||||
}
|
||||
.race-canvas-outer {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.race-track-canvas {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* Graph area */
|
||||
.race-graph-wrap {
|
||||
flex: 0 0 120px;
|
||||
border-top: 1px solid var(--border);
|
||||
background: rgba(8,8,20,0.92);
|
||||
overflow: hidden;
|
||||
}
|
||||
.race-graph-canvas {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* Answer bar */
|
||||
.race-answer-bar {
|
||||
flex: 0 0 auto;
|
||||
padding: 10px 16px;
|
||||
border-top: 1px solid var(--border);
|
||||
background: rgba(255,255,255,0.018);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
.race-answer-inputs {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
flex: 1;
|
||||
}
|
||||
.race-ans-label {
|
||||
font-size: .82rem;
|
||||
color: rgba(255,255,255,0.75);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
.race-ans-inp {
|
||||
width: 80px;
|
||||
padding: 5px 8px;
|
||||
background: rgba(255,255,255,0.06);
|
||||
border: 1px solid var(--border-h);
|
||||
border-radius: 6px;
|
||||
color: var(--text);
|
||||
font-family: 'Manrope', sans-serif;
|
||||
font-size: .85rem;
|
||||
}
|
||||
.race-ans-inp:focus {
|
||||
outline: none;
|
||||
border-color: rgba(155,93,229,0.55);
|
||||
background: rgba(155,93,229,0.07);
|
||||
}
|
||||
.race-answer-btns {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
.race-check-btn {
|
||||
background: rgba(155,93,229,0.15) !important;
|
||||
border-color: rgba(155,93,229,0.4) !important;
|
||||
color: var(--violet) !important;
|
||||
}
|
||||
.race-check-btn:hover {
|
||||
background: rgba(155,93,229,0.28) !important;
|
||||
}
|
||||
|
||||
/* Verdict */
|
||||
.race-verdict {
|
||||
width: 100%;
|
||||
padding: 7px 12px;
|
||||
border-radius: 8px;
|
||||
font-size: .83rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.race-verdict-ok {
|
||||
background: rgba(74,222,128,0.12);
|
||||
border: 1px solid rgba(74,222,128,0.35);
|
||||
color: #4ADE80;
|
||||
}
|
||||
.race-verdict-err {
|
||||
background: rgba(239,71,111,0.12);
|
||||
border: 1px solid rgba(239,71,111,0.35);
|
||||
color: #EF476F;
|
||||
}
|
||||
.race-ok { color: #4ADE80; margin-right: 8px; }
|
||||
.race-err { color: #EF476F; margin-right: 8px; }
|
||||
|
||||
/* Stats bar */
|
||||
.race-stats-bar {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
gap: 0;
|
||||
border-top: 1px solid var(--border);
|
||||
background: rgba(255,255,255,0.02);
|
||||
}
|
||||
.race-stats-bar .pstat {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 6px 4px;
|
||||
border-right: 1px solid var(--border);
|
||||
min-width: 0;
|
||||
}
|
||||
.race-stats-bar .pstat:last-child { border-right: none; }
|
||||
.race-stats-bar .pstat-label {
|
||||
font-size: .68rem;
|
||||
color: rgba(255,255,255,0.4);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .04em;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.race-stats-bar .pstat-val {
|
||||
font-size: .82rem;
|
||||
font-weight: 700;
|
||||
font-family: 'Unbounded', sans-serif;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
{ id: 'waves', cat: 'Физика', title: 'Волны и звук' },
|
||||
{ id: 'heatengine', cat: 'Физика', title: 'Тепловые двигатели' },
|
||||
{ id: 'radioactive', cat: 'Физика', title: 'Радиоактивный распад' },
|
||||
{ id: 'race', cat: 'Физика', title: 'Гонка с задачами' },
|
||||
{ id: 'logic', cat: 'Физика', title: 'Логические схемы' },
|
||||
{ id: 'molphys', cat: 'Химия', title: 'Молекулярная физика' },
|
||||
{ id: 'chemistry', cat: 'Химия', title: 'Химические реакции' },
|
||||
|
||||
@@ -652,6 +652,18 @@
|
||||
<text x="131" y="16" font-size="9" fill="rgba(255,255,255,0.5)" font-family="Manrope,sans-serif">C</text>`);
|
||||
|
||||
|
||||
/* Race sim preview — two objects on a track, x(t) lines */
|
||||
const P_RACE = _svg(`${_grid('rgba(255,255,255,0.05)')}
|
||||
<line x1="20" y1="75" x2="250" y2="75" stroke="rgba(255,255,255,0.18)" stroke-width="8" stroke-linecap="round"/>
|
||||
<line x1="20" y1="75" x2="250" y2="75" stroke="rgba(30,32,50,0.9)" stroke-width="6" stroke-linecap="round"/>
|
||||
<circle cx="75" cy="75" r="9" fill="rgba(6,214,224,0.2)" stroke="#06D6E0" stroke-width="2"/>
|
||||
<circle cx="185" cy="75" r="9" fill="rgba(239,71,111,0.2)" stroke="#EF476F" stroke-width="2"/>
|
||||
<line x1="30" y1="110" x2="130" y2="30" stroke="#06D6E0" stroke-width="2" opacity="0.85"/>
|
||||
<line x1="30" y1="30" x2="200" y2="110" stroke="#EF476F" stroke-width="2" opacity="0.85"/>
|
||||
<circle cx="107" cy="60" r="5" fill="#FF6B6B" stroke="#fff" stroke-width="1.5"/>
|
||||
<text x="115" y="57" font-size="9" fill="#FF6B6B" font-family="Manrope,sans-serif" font-weight="700">встреча</text>
|
||||
<text x="135" y="130" font-size="8" fill="rgba(255,255,255,0.35)" text-anchor="middle" font-family="Manrope,sans-serif">x = x₀ + v₀t + at²/2</text>`);
|
||||
|
||||
/* Logic Circuits preview */
|
||||
const P_LOGIC = _svg(`${_grid('rgba(255,255,255,0.04)')}
|
||||
<rect x="20" y="38" width="60" height="30" fill="rgba(155,93,229,0.12)" stroke="#9B5DE5" stroke-width="1.5" rx="4"/>
|
||||
@@ -824,6 +836,10 @@
|
||||
title: 'Радиоактивный распад',
|
||||
desc: 'Период полураспада, цепочки распадов, активность. Визуализация ядер + кривая N(t). Радиоуглеродное датирование.',
|
||||
preview: P_RADIOACTIVE },
|
||||
{ id: 'race', cat: 'phys',
|
||||
title: 'Гонка с задачами',
|
||||
desc: 'Кинематика 1D: встреча, догон, кто первый. Реши задачу — проверь анимацией и графиком x(t).',
|
||||
preview: P_RACE },
|
||||
{ id: 'heatengine', cat: 'phys',
|
||||
title: 'Тепловые двигатели',
|
||||
desc: 'Циклы Карно, Отто, Дизеля, Брайтона. PV-диаграмма, поршень, КПД.',
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
'sim-quadratic','sim-normaldist','sim-graphtransform',
|
||||
'sim-pendulum','sim-equilibrium','sim-opticsbench','sim-titration',
|
||||
'sim-isoprocess','sim-probability','sim-bohratom','sim-electrolysis',
|
||||
'sim-race',
|
||||
'sim-waves','sim-hydro','sim-radioactive','sim-geometry','sim-heatengine','sim-logic',
|
||||
'sim-qualanalysis','sim-periodic','sim-organic','sim-solutions'];
|
||||
var ALL_CTRL_BARS = ['ctrl-graph','ctrl-proj','ctrl-coll','ctrl-tri','ctrl-trigcircle','ctrl-emfield',
|
||||
@@ -98,6 +99,7 @@
|
||||
if (id === 'probability') _openProbability();
|
||||
if (id === 'bohratom') _openBohrAtom();
|
||||
if (id === 'electrolysis') _openElectrolysis();
|
||||
if (id === 'race') _openRace();
|
||||
if (id === 'waves') _openWaves();
|
||||
if (id === 'hydrostatics') _openHydro();
|
||||
if (id.startsWith('hydrostatics:')) _openHydro(id.split(':')[1]);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3657,6 +3657,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── RACE sim body ── -->
|
||||
<div id="sim-race" class="sim-proj-wrap race-sim-host" style="display:none">
|
||||
<div id="race-wrap" style="width:100%;height:100%;display:flex;flex-direction:column"></div>
|
||||
</div>
|
||||
|
||||
<!-- ── WAVES sim body ── -->
|
||||
<div id="sim-waves" class="sim-proj-wrap" style="display:none">
|
||||
<div class="sim-body-wrap">
|
||||
@@ -4818,6 +4823,7 @@
|
||||
<script src="/js/labs/probability.js"></script>
|
||||
<script src="/js/labs/bohratom.js"></script>
|
||||
<script src="/js/labs/electrolysis.js"></script>
|
||||
<script src="/js/labs/race.js"></script>
|
||||
<script src="/js/labs/hydrostatics.js"></script>
|
||||
<script src="/js/labs/radioactive.js"></script>
|
||||
<script src="/js/labs/geometry.js"></script>
|
||||
|
||||
Reference in New Issue
Block a user