feat(math6): canvas-анимации — движок math6_anim.js + 3 флагмана
Новый headless-safe движок window.Math6Anim (по канве chem7_anim: RAF-цикл с паузой вне экрана через IntersectionObserver, prefers-reduced-motion, в jsdom/HeadlessChrome getContext НЕ вызывается → тесты не падают). Демо: rollingCircle (колесо катится → путь = C=2πr=πd), sweepArea (радиус заметает круг → S=πr²), areaModel (площадная модель умножения a·b на сетке 0,1). Вшито: Гл.6 §2 (колесо + заметание), Гл.1 §6 (умножение). Тесты math6: 19/19 (+canvas-демо монтируются headless-safe). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,7 @@ function buildPage(file) {
|
||||
let html = readF('frontend/textbooks/' + file);
|
||||
const inl = {
|
||||
'/js/math6_svg.js': readF('frontend/js/math6_svg.js'),
|
||||
'/js/math6_anim.js': readF('frontend/js/math6_anim.js'),
|
||||
'/js/math6_engine.js': readF('frontend/js/math6_engine.js')
|
||||
};
|
||||
html = html
|
||||
@@ -167,6 +168,20 @@ test('ch6: наглядная геометрия — интерактивы §1
|
||||
assert.deepEqual(errors, [], 'нет ошибок: ' + errors.join(' | '));
|
||||
});
|
||||
|
||||
test('анимации: canvas-демо монтируются (headless-safe)', async () => {
|
||||
// Глава 6 §2: колесо + заметание площади
|
||||
const r6 = await loadDom('math_6_ch6.html');
|
||||
r6.doc.defaultView.goTo('p2'); await wait(100);
|
||||
assert.ok(r6.doc.querySelector('#p2-roll canvas'), 'canvas «колесо» §6.2');
|
||||
assert.ok(r6.doc.querySelector('#p2-sweep canvas'), 'canvas «заметание площади» §6.2');
|
||||
assert.deepEqual(r6.errors, [], 'ch6 без ошибок: ' + r6.errors.join(' | '));
|
||||
// Глава 1 §6: площадная модель умножения
|
||||
const r1 = await loadDom('math_6_ch1.html');
|
||||
r1.doc.defaultView.goTo('p6'); await wait(100);
|
||||
assert.ok(r1.doc.querySelector('#p6-area canvas'), 'canvas «площадная модель» §1.6');
|
||||
assert.deepEqual(r1.errors, [], 'ch1 без ошибок: ' + r1.errors.join(' | '));
|
||||
});
|
||||
|
||||
test('hub: 6 карточек глав + курсовой финал', async () => {
|
||||
const { doc, errors } = await loadDom('math_6_hub.html');
|
||||
assert.deepEqual(errors, [], 'нет ошибок: ' + errors.join(' | '));
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
/* math6_anim.js — движок анимированных canvas-демонстраций «Математики 6».
|
||||
* Неймспейс window.Math6Anim. Используется билдерами глав (math_6_chN.html).
|
||||
*
|
||||
* Принципы (как в chem7_anim.js — проверено):
|
||||
* - один RAF-цикл на демо, пауза вне вьюпорта (IntersectionObserver), stop() при уходе;
|
||||
* - prefers-reduced-motion → один статичный кадр вместо анимации;
|
||||
* - HEADLESS (jsdom-тесты / HeadlessChrome): getContext НЕ вызывается (ctx=null),
|
||||
* строится только DOM-каркас → тесты не падают;
|
||||
* - тёмная тема и акценты — через CSS-переменные; ⛔ без эмодзи.
|
||||
*
|
||||
* Каждое демо: fn(host, opts) → { stop(), set(opts) }. Перерисовка при смене параметра — set().
|
||||
*/
|
||||
(function (W) {
|
||||
'use strict';
|
||||
if (W.Math6Anim && W.Math6Anim.__installed) return;
|
||||
var D = W.document;
|
||||
var M = W.Math6Anim = W.Math6Anim || {};
|
||||
M.__installed = true;
|
||||
|
||||
var HEADLESS = (typeof navigator !== 'undefined' && /jsdom|HeadlessChrome/i.test(navigator.userAgent || ''));
|
||||
function reduced() { try { return !!(W.matchMedia && W.matchMedia('(prefers-reduced-motion: reduce)').matches); } catch (e) { return false; } }
|
||||
function now() { try { return W.performance && W.performance.now ? W.performance.now() : Date.now(); } catch (e) { return Date.now(); } }
|
||||
function cssVar(name, fb) { try { var v = getComputedStyle(D.documentElement).getPropertyValue(name).trim(); return v || fb; } catch (e) { return fb; } }
|
||||
function kf(x) { var s = (Math.round(x * 100) / 100).toString(); return s.replace('.', ','); }
|
||||
|
||||
function observeVisible(host, cb) {
|
||||
if (typeof IntersectionObserver === 'undefined') { cb(true); return { disconnect: function () {} }; }
|
||||
var io = new IntersectionObserver(function (es) { cb(es[0] && es[0].isIntersecting); }, { threshold: 0.01 });
|
||||
io.observe(host); return io;
|
||||
}
|
||||
|
||||
/* canvas в host; ctx=null в HEADLESS (getContext не зовём) */
|
||||
function sceneCanvas(host, w, h) {
|
||||
host.innerHTML = '';
|
||||
var cv = D.createElement('canvas');
|
||||
var dpr = HEADLESS ? 1 : (W.devicePixelRatio || 1);
|
||||
cv.width = w * dpr; cv.height = h * dpr;
|
||||
cv.style.width = '100%'; cv.style.maxWidth = w + 'px'; cv.style.height = 'auto';
|
||||
cv.style.borderRadius = '12px'; cv.style.display = 'block'; cv.style.margin = '0 auto';
|
||||
cv.style.background = 'var(--card,#fff)'; cv.style.border = '1px solid var(--border,#e2e8f0)';
|
||||
host.appendChild(cv);
|
||||
var ctx = null;
|
||||
if (!HEADLESS) { try { ctx = cv.getContext('2d'); if (ctx) ctx.scale(dpr, dpr); } catch (e) { ctx = null; } }
|
||||
return { cv: cv, ctx: ctx, w: w, h: h };
|
||||
}
|
||||
|
||||
/* RAF-цикл; step(tSec). В HEADLESS/reduced — один кадр step(0). Возвращает {stop}. */
|
||||
function loop(host, step) {
|
||||
if (HEADLESS || reduced()) { try { step(0); } catch (e) {} return { stop: function () {} }; }
|
||||
var raf = 0, running = true, visible = true, t0 = now();
|
||||
var io = observeVisible(host, function (v) { visible = v; });
|
||||
function frame() {
|
||||
if (!running) return;
|
||||
if (visible) { try { step((now() - t0) / 1000); } catch (e) { running = false; return; } }
|
||||
raf = W.requestAnimationFrame(frame);
|
||||
}
|
||||
raf = W.requestAnimationFrame(frame);
|
||||
return { stop: function () { running = false; try { W.cancelAnimationFrame(raf); } catch (e) {} try { io.disconnect(); } catch (e) {} } };
|
||||
}
|
||||
|
||||
function caption(host, html) {
|
||||
var c = D.createElement('div');
|
||||
c.style.cssText = 'text-align:center;font-weight:700;color:var(--pri2,#3730a3);margin-top:8px;font-size:1.02rem';
|
||||
c.innerHTML = html; host.appendChild(c);
|
||||
if (W.renderMathInElement) try { W.renderMathInElement(c, { delimiters: [{ left: '$', right: '$', display: false }], throwOnError: false }); } catch (e) {}
|
||||
return c;
|
||||
}
|
||||
|
||||
/* ============================ ДЕМО 1: КОЛЕСО КАТИТСЯ (C = πd = 2πr) ============================ */
|
||||
M.rollingCircle = function (host, opts) {
|
||||
opts = opts || {}; var r = opts.r || 3;
|
||||
var W0 = 520, H0 = 200;
|
||||
var sc = sceneCanvas(host, W0, H0);
|
||||
var cap = caption(host, '');
|
||||
var PI = 3.14, period = 4.2;
|
||||
function draw(tSec) {
|
||||
var ctx = sc.ctx; var pri = cssVar('--pri', '#4f46e5'), acc = cssVar('--pri2', '#3730a3'), mut = cssVar('--muted', '#64748b');
|
||||
if (!ctx) return;
|
||||
var Rpx = 26 + r * 5; if (Rpx > 60) Rpx = 60;
|
||||
var baseY = H0 - 46, startX = 34;
|
||||
var p = (tSec % period) / period; /* 0..1 — один оборот */
|
||||
var travel = p * 2 * Math.PI * Rpx;
|
||||
var cx = startX + Rpx + travel, cy = baseY - Rpx, ang = p * 2 * Math.PI;
|
||||
ctx.clearRect(0, 0, W0, H0);
|
||||
/* линия земли */
|
||||
ctx.strokeStyle = mut; ctx.lineWidth = 1.5; ctx.beginPath(); ctx.moveTo(20, baseY); ctx.lineTo(W0 - 20, baseY); ctx.stroke();
|
||||
/* развёрнутый путь (длина окружности) — толстая лента */
|
||||
ctx.strokeStyle = acc; ctx.lineWidth = 5; ctx.lineCap = 'round';
|
||||
ctx.beginPath(); ctx.moveTo(startX + Rpx, baseY + 10); ctx.lineTo(startX + Rpx + travel, baseY + 10); ctx.stroke();
|
||||
/* колесо */
|
||||
ctx.strokeStyle = pri; ctx.lineWidth = 3; ctx.fillStyle = 'rgba(79,70,229,0.08)';
|
||||
ctx.beginPath(); ctx.arc(cx, cy, Rpx, 0, 2 * Math.PI); ctx.fill(); ctx.stroke();
|
||||
/* спица + метка на ободе (видно вращение) */
|
||||
var mx = cx + Rpx * Math.sin(ang), my = cy + Rpx * Math.cos(ang);
|
||||
ctx.strokeStyle = pri; ctx.lineWidth = 2; ctx.beginPath(); ctx.moveTo(cx, cy); ctx.lineTo(mx, my); ctx.stroke();
|
||||
ctx.fillStyle = '#e11d48'; ctx.beginPath(); ctx.arc(mx, my, 5, 0, 2 * Math.PI); ctx.fill();
|
||||
/* центр */
|
||||
ctx.fillStyle = acc; ctx.beginPath(); ctx.arc(cx, cy, 3, 0, 2 * Math.PI); ctx.fill();
|
||||
/* подпись пройденного пути */
|
||||
ctx.fillStyle = mut; ctx.font = '12px JetBrains Mono, monospace'; ctx.textAlign = 'center';
|
||||
ctx.fillText('путь за ' + (p < 0.999 ? Math.round(p * 100) + '% оборота' : 'полный оборот'), startX + Rpx + travel / 2, baseY + 30);
|
||||
}
|
||||
var L = loop(host, draw);
|
||||
cap.innerHTML = 'За один полный оборот колесо проходит путь, равный длине окружности: $C = 2\\pi r = \\pi d = ' + kf(2 * PI * r) + '$ (при $r=' + r + '$).';
|
||||
if (W.renderMathInElement) try { W.renderMathInElement(cap, { delimiters: [{ left: '$', right: '$', display: false }], throwOnError: false }); } catch (e) {}
|
||||
return { stop: L.stop, host: host };
|
||||
};
|
||||
|
||||
/* ============================ ДЕМО 2: ЗАМЕТАНИЕ ПЛОЩАДИ КРУГА (S = πr²) ============================ */
|
||||
M.sweepArea = function (host, opts) {
|
||||
opts = opts || {}; var r = opts.r || 3;
|
||||
var W0 = 300, H0 = 300; var sc = sceneCanvas(host, W0, H0);
|
||||
var cap = caption(host, '');
|
||||
var PI = 3.14, period = 3.6;
|
||||
function draw(tSec) {
|
||||
var ctx = sc.ctx; if (!ctx) return;
|
||||
var pri = cssVar('--pri', '#4f46e5'), acc = cssVar('--pri2', '#3730a3'), mut = cssVar('--muted', '#64748b');
|
||||
var cx = W0 / 2, cy = H0 / 2, Rpx = 40 + r * 12; if (Rpx > 130) Rpx = 130;
|
||||
var p = (tSec % period) / period; var sweep = p * 2 * Math.PI;
|
||||
ctx.clearRect(0, 0, W0, H0);
|
||||
/* контур круга */
|
||||
ctx.strokeStyle = pri; ctx.lineWidth = 2.5; ctx.beginPath(); ctx.arc(cx, cy, Rpx, 0, 2 * Math.PI); ctx.stroke();
|
||||
/* заметённый сектор */
|
||||
ctx.fillStyle = 'rgba(79,70,229,0.22)'; ctx.beginPath(); ctx.moveTo(cx, cy);
|
||||
ctx.arc(cx, cy, Rpx, -Math.PI / 2, -Math.PI / 2 + sweep); ctx.closePath(); ctx.fill();
|
||||
/* движущийся радиус */
|
||||
var ex = cx + Rpx * Math.cos(-Math.PI / 2 + sweep), ey = cy + Rpx * Math.sin(-Math.PI / 2 + sweep);
|
||||
ctx.strokeStyle = '#e11d48'; ctx.lineWidth = 2.5; ctx.beginPath(); ctx.moveTo(cx, cy); ctx.lineTo(ex, ey); ctx.stroke();
|
||||
/* подпись r */
|
||||
ctx.fillStyle = acc; ctx.font = '13px JetBrains Mono, monospace'; ctx.textAlign = 'left';
|
||||
ctx.fillText('r = ' + r, cx + 6, cy - 6);
|
||||
ctx.fillStyle = acc; ctx.beginPath(); ctx.arc(cx, cy, 3, 0, 2 * Math.PI); ctx.fill();
|
||||
}
|
||||
var L = loop(host, draw);
|
||||
cap.innerHTML = 'Радиус заметает круг — его площадь $S = \\pi r^2 = ' + kf(PI * r * r) + '$ (при $r=' + r + '$).';
|
||||
if (W.renderMathInElement) try { W.renderMathInElement(cap, { delimiters: [{ left: '$', right: '$', display: false }], throwOnError: false }); } catch (e) {}
|
||||
return { stop: L.stop, host: host };
|
||||
};
|
||||
|
||||
/* ============================ ДЕМО 3: ПЛОЩАДНАЯ МОДЕЛЬ УМНОЖЕНИЯ (a · b) ============================ */
|
||||
M.areaModel = function (host, opts) {
|
||||
opts = opts || {}; var a = opts.a || 1.2, b = opts.b || 0.3;
|
||||
var W0 = 360, H0 = 300; var sc = sceneCanvas(host, W0, H0);
|
||||
var cap = caption(host, '');
|
||||
var period = 2.8;
|
||||
function draw(tSec) {
|
||||
var ctx = sc.ctx; if (!ctx) return;
|
||||
var pri = cssVar('--pri', '#4f46e5'), acc = cssVar('--pri2', '#3730a3'), mut = cssVar('--muted', '#64748b'), bd = cssVar('--border', '#e2e8f0');
|
||||
var pad = 40, plotW = W0 - pad - 16, plotH = H0 - pad - 16;
|
||||
var maxU = Math.max(3, Math.ceil(a), Math.ceil(b)); /* единиц по каждой стороне на сетке (адаптивно) */
|
||||
var unit = Math.min(plotW, plotH) / maxU;
|
||||
var x0 = pad, y0 = H0 - pad;
|
||||
var aw = a * unit, bh = b * unit;
|
||||
var p = Math.min(1, (tSec % period) / (period * 0.7)); /* заполнение 0..1, потом пауза */
|
||||
ctx.clearRect(0, 0, W0, H0);
|
||||
/* сетка 0,1 */
|
||||
ctx.strokeStyle = bd; ctx.lineWidth = 0.7;
|
||||
for (var gx = 0; gx <= maxU * 10; gx++) { var X = x0 + gx * unit / 10; if (X > x0 + maxU * unit + 0.5) break; ctx.globalAlpha = (gx % 10 === 0) ? 1 : 0.4; ctx.beginPath(); ctx.moveTo(X, y0); ctx.lineTo(X, y0 - maxU * unit); ctx.stroke(); }
|
||||
for (var gy = 0; gy <= maxU * 10; gy++) { var Y = y0 - gy * unit / 10; ctx.globalAlpha = (gy % 10 === 0) ? 1 : 0.4; ctx.beginPath(); ctx.moveTo(x0, Y); ctx.lineTo(x0 + maxU * unit, Y); ctx.stroke(); }
|
||||
ctx.globalAlpha = 1;
|
||||
/* прямоугольник a×b, заполняется слева направо */
|
||||
ctx.fillStyle = 'rgba(79,70,229,0.28)';
|
||||
ctx.fillRect(x0, y0 - bh, aw * p, bh);
|
||||
ctx.strokeStyle = pri; ctx.lineWidth = 2.5; ctx.strokeRect(x0, y0 - bh, aw, bh);
|
||||
/* оси-подписи */
|
||||
ctx.fillStyle = acc; ctx.font = '13px JetBrains Mono, monospace';
|
||||
ctx.textAlign = 'center'; ctx.fillText('a = ' + kf(a), x0 + aw / 2, y0 + 22);
|
||||
ctx.save(); ctx.translate(x0 - 14, y0 - bh / 2); ctx.rotate(-Math.PI / 2); ctx.fillText('b = ' + kf(b), 0, 0); ctx.restore();
|
||||
}
|
||||
var L = loop(host, draw);
|
||||
var prod = Math.round(a * b * 1e6) / 1e6;
|
||||
cap.innerHTML = 'Площадь прямоугольника = произведению сторон: $' + kf(a) + ' \\cdot ' + kf(b) + ' = ' + kf(prod) + '$.';
|
||||
if (W.renderMathInElement) try { W.renderMathInElement(cap, { delimiters: [{ left: '$', right: '$', display: false }], throwOnError: false }); } catch (e) {}
|
||||
return { stop: L.stop, host: host };
|
||||
};
|
||||
|
||||
})(window);
|
||||
@@ -17,6 +17,7 @@
|
||||
<script src="/js/api.js" defer></script>
|
||||
<script src="/js/xp.js" defer></script>
|
||||
<script src="/js/math6_svg.js" defer></script>
|
||||
<script src="/js/math6_anim.js" defer></script>
|
||||
<script src="/js/math6_engine.js" defer></script>
|
||||
<style>:root{--pri:#4f46e5;--pri2:#3730a3;--pri-soft:#e0e7ff;--acc:#6366f1;--acc2:#4f46e5;--acc-soft:#eef2ff}</style>
|
||||
</head>
|
||||
@@ -532,7 +533,7 @@ function buildP6(){
|
||||
+'<div class="wg-help">Двигай множители — смотри, как число знаков после запятой определяет ответ.</div>'
|
||||
+'<div class="sliders"><label>Первый = <b id="p6-av">1,2</b><input type="range" id="p6-asl" min="0" max="5" value="1"></label>'
|
||||
+'<label>Второй = <b id="p6-bv">0,3</b><input type="range" id="p6-bsl" min="0" max="5" value="2"></label></div>'
|
||||
+'<div id="p6-out" class="qbox"></div></div>';
|
||||
+'<div id="p6-out" class="qbox"></div><div id="p6-area" style="margin-top:10px"></div></div>';
|
||||
h+='<div class="wg" id="p6-iv2"><div class="wg-header"><span class="wg-badge">Интерактив 2</span><div class="wg-title">Тренажёр умножения</div></div>'
|
||||
+'<div class="wg-help">Перемножь десятичные дроби и введи ответ.</div>'
|
||||
+'<div class="score-display"><span>Пример <b id="p6-i">1</b> / 6</span><span>Очки: <b id="p6-s">0</b> / 6</span></div>'
|
||||
@@ -544,11 +545,12 @@ function buildP6(){
|
||||
|
||||
(function(){
|
||||
var AS=[0.4,1.2,0.25,3.5,0.06,2.5], BS=[0.3,4,0.2,1.5,0.7,0.08];
|
||||
var asl=document.getElementById('p6-asl'), bsl=document.getElementById('p6-bsl'), out=document.getElementById('p6-out');
|
||||
var asl=document.getElementById('p6-asl'), bsl=document.getElementById('p6-bsl'), out=document.getElementById('p6-out'), area=null;
|
||||
function render(){ var a=AS[+asl.value], b=BS[+bsl.value]; document.getElementById('p6-av').textContent=_kf(a).replace('{,}',','); document.getElementById('p6-bv').textContent=_kf(b).replace('{,}',',');
|
||||
var da=_dec(a),db=_dec(b), ma=_mant(a,da),mb=_mant(b,db), p=ma*mb, r=p/Math.pow(10,da+db);
|
||||
out.innerHTML='<div style="font-size:.95rem;color:var(--muted)">$'+ma+'\\cdot '+mb+' = '+p+'$, знаков после запятой: $'+da+'+'+db+'='+(da+db)+'$</div>'
|
||||
+'<div style="font-size:1.4rem;font-weight:800;color:var(--pri2);margin-top:6px">$'+_kf(a)+'\\cdot '+_kf(b)+' = '+_kf(r)+'$</div>'; renderMath(out); }
|
||||
+'<div style="font-size:1.4rem;font-weight:800;color:var(--pri2);margin-top:6px">$'+_kf(a)+'\\cdot '+_kf(b)+' = '+_kf(r)+'$</div>'; renderMath(out);
|
||||
if(window.Math6Anim){ if(area)area.stop(); area=Math6Anim.areaModel(document.getElementById('p6-area'),{a:a,b:b}); } }
|
||||
asl.oninput=render; bsl.oninput=render; render();
|
||||
})();
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<script src="/js/api.js" defer></script>
|
||||
<script src="/js/xp.js" defer></script>
|
||||
<script src="/js/math6_svg.js" defer></script>
|
||||
<script src="/js/math6_anim.js" defer></script>
|
||||
<script src="/js/math6_engine.js" defer></script>
|
||||
<style>:root{--pri:#d97706;--pri2:#b45309;--pri-soft:#fef3c7;--acc:#f59e0b;--acc2:#d97706;--acc-soft:#fffbeb}</style>
|
||||
</head>
|
||||
@@ -255,6 +256,10 @@ function buildP2(){
|
||||
+'<div class="wg-help">Двигай радиус — смотри, как меняются длина окружности и площадь круга ($\\pi=3{,}14$).</div>'
|
||||
+'<div class="sliders"><label>Радиус $r$ = <b id="p2-rv">3</b><input type="range" id="p2-r" min="1" max="8" value="3"></label></div>'
|
||||
+'<div id="p2-fig"></div><div id="p2-out" class="qbox"></div></div>';
|
||||
h+='<div class="wg" id="p2-anim"><div class="wg-header"><span class="wg-badge">Анимация</span><div class="wg-title">Колесо катится · радиус заметает круг</div></div>'
|
||||
+'<div class="wg-help">Двигай радиус. <b>Колесо</b> за один полный оборот проходит путь, равный длине окружности $C=2\\pi r=\\pi d$. <b>Вращающийся радиус</b> заметает всю площадь круга $S=\\pi r^2$.</div>'
|
||||
+'<div class="sliders"><label>Радиус $r$ = <b id="p2-arv">3</b><input type="range" id="p2-ar" min="1" max="6" value="3"></label></div>'
|
||||
+'<div id="p2-roll"></div><div id="p2-sweep" style="margin-top:10px"></div></div>';
|
||||
h+='<div class="wg" id="p2-iv2"><div class="wg-header"><span class="wg-badge">Интерактив 2</span><div class="wg-title">Тренажёр: вычисли C или S</div></div>'
|
||||
+'<div class="wg-help">Используй $\\pi=3{,}14$. Ответ — число.</div>'
|
||||
+'<div class="score-display"><span>Задача <b id="p2-i">1</b> / 6</span><span>Очки: <b id="p2-s">0</b> / 6</span></div>'
|
||||
@@ -264,6 +269,16 @@ function buildP2(){
|
||||
h+=secNav('p1','p3')+readBtn('p2');
|
||||
box.innerHTML=h; renderMath(box);
|
||||
|
||||
(function(){
|
||||
if(!window.Math6Anim) return;
|
||||
var sl=document.getElementById('p2-ar'), roll=null, sweep=null;
|
||||
function render(){ var r=+sl.value; document.getElementById('p2-arv').textContent=r;
|
||||
if(roll)roll.stop(); if(sweep)sweep.stop();
|
||||
roll=Math6Anim.rollingCircle(document.getElementById('p2-roll'),{r:r});
|
||||
sweep=Math6Anim.sweepArea(document.getElementById('p2-sweep'),{r:r}); }
|
||||
sl.oninput=render; render();
|
||||
})();
|
||||
|
||||
(function(){
|
||||
var sl=document.getElementById('p2-r'), fig=document.getElementById('p2-fig'), out=document.getElementById('p2-out');
|
||||
function render(){ var r=+sl.value; document.getElementById('p2-rv').textContent=r;
|
||||
|
||||
Reference in New Issue
Block a user