Shorten scale labels, add safe-area clamping, responsive title
This commit is contained in:
parent
41ea27993a
commit
5818b7e24d
1 changed files with 34 additions and 27 deletions
|
|
@ -137,21 +137,20 @@
|
|||
function sceneScale() {
|
||||
const s = scene('scene-scale', {
|
||||
height: 260,
|
||||
caption: 'Each tick is ten times smaller than the one to its right. From the transmitter on the far right down to the echo on the far left, the signal shrinks by a factor of one billion trillion. The moving pulse is the signal going on its round trip.',
|
||||
caption: 'Power out is on the right, power back is on the left. Every tick is ten times smaller than the one to its right. There are twenty-one of them. The glow is the signal on its round trip, shrinking the whole way.',
|
||||
});
|
||||
if (!s) return;
|
||||
|
||||
// Powers of ten from 10^3 W down to 10^-18 W. Labels use relatable objects
|
||||
// instead of SI prefixes so non-hams can follow.
|
||||
// Fewer labels spaced every three decades so nothing collides on narrow viewports.
|
||||
const marks = [
|
||||
{ p: 1e3, label: 'microwave oven', align: 'right', color: C.red },
|
||||
{ p: 1, label: 'handheld radio', align: 'center', color: C.orange },
|
||||
{ p: 1e-3, label: 'laser pointer', align: 'center', color: C.yellow },
|
||||
{ p: 1e-6, label: 'faint LED indicator', align: 'center', color: C.green },
|
||||
{ p: 1e-9, label: 'Wi-Fi at the router', align: 'center', color: C.cyan },
|
||||
{ p: 1e-12, label: 'cell phone at edge of coverage', align: 'center', color: C.blue },
|
||||
{ p: 1e-15, label: 'weak HF radio signal', align: 'center', color: '#b199e4' },
|
||||
{ p: 1e-18, label: 'echo from the Moon', align: 'left', color: C.magenta },
|
||||
{ p: 1e3, label: 'microwave', color: C.red, anchor: 'right' },
|
||||
{ p: 1, label: 'handheld radio', color: C.orange, anchor: 'center' },
|
||||
{ p: 1e-3, label: 'laser pointer', color: C.yellow, anchor: 'center' },
|
||||
{ p: 1e-6, label: 'LED glow', color: C.green, anchor: 'center' },
|
||||
{ p: 1e-9, label: 'Wi-Fi signal', color: C.cyan, anchor: 'center' },
|
||||
{ p: 1e-12, label: 'faint radio', color: C.blue, anchor: 'center' },
|
||||
{ p: 1e-15, label: 'weak DX', color: '#b199e4', anchor: 'center' },
|
||||
{ p: 1e-18, label: 'Moon echo', color: C.magenta, anchor: 'left' },
|
||||
];
|
||||
|
||||
const start = performance.now();
|
||||
|
|
@ -160,13 +159,14 @@
|
|||
const ctx = s.ctx;
|
||||
clear(ctx, w, h);
|
||||
|
||||
// Generous margins so the endpoint labels can't clip
|
||||
const m = { l: 60, r: 60, t: 80, b: 70 };
|
||||
const pW = w - m.l - m.r;
|
||||
// Generous margins on all sides.
|
||||
const m = { l: 70, r: 70, t: 80, b: 70 };
|
||||
const pW = Math.max(120, w - m.l - m.r);
|
||||
const barY = m.t + 20;
|
||||
const barH = 14;
|
||||
|
||||
const xOf = logP => m.l + (logP + 18) / 21 * pW;
|
||||
const clampX = x => Math.max(m.l + 2, Math.min(m.l + pW - 2, x));
|
||||
|
||||
// Gradient bar
|
||||
const grad = ctx.createLinearGradient(m.l, 0, m.l + pW, 0);
|
||||
|
|
@ -193,16 +193,15 @@
|
|||
ctx.stroke();
|
||||
}
|
||||
|
||||
// Labels. Alternate above/below and align endpoints inward so they never clip.
|
||||
// Labels alternating above/below the bar. Endpoint anchors pull them inward.
|
||||
marks.forEach((mk, i) => {
|
||||
const x = xOf(Math.log10(mk.p));
|
||||
const above = i % 2 === 0;
|
||||
const y = above ? barY - 18 : barY + barH + 22;
|
||||
// Two-line label: the friendly name + the exact power in words on a second line
|
||||
pill(ctx, mk.label, x, y, mk.color, mk.align);
|
||||
pill(ctx, mk.label, x, y, mk.color, mk.anchor);
|
||||
});
|
||||
|
||||
// Animated pulse from right (microwave oven) to left (Moon echo)
|
||||
// Animated pulse from right (microwave) to left (Moon echo)
|
||||
const t = ((performance.now() - start) / 1000) % 7;
|
||||
const pulseLogP = 3 - (t / 7) * 21;
|
||||
const pulseX = xOf(pulseLogP);
|
||||
|
|
@ -217,21 +216,29 @@
|
|||
ctx.fillStyle = C.yellow;
|
||||
ctx.beginPath(); ctx.arc(pulseX, pulseY, visualR, 0, Math.PI * 2); ctx.fill();
|
||||
|
||||
// How many times smaller right now: 10^(3 - logP) = 10^(21 * t/7)
|
||||
// Readout under the pulse, clamped so it never runs off the canvas.
|
||||
const shrink = Math.pow(10, 3 - pulseLogP);
|
||||
let shrinkStr;
|
||||
if (shrink < 10) shrinkStr = `${shrink.toFixed(1)}x smaller than the transmitter`;
|
||||
else if (shrink < 1e6) shrinkStr = `${shrink.toExponential(0)}x smaller`;
|
||||
if (shrink < 10) shrinkStr = `${shrink.toFixed(0)}x smaller`;
|
||||
else if (shrink < 1000) shrinkStr = `${shrink.toFixed(0)}x smaller`;
|
||||
else {
|
||||
const exp = Math.log10(shrink).toFixed(0);
|
||||
const exp = Math.round(Math.log10(shrink));
|
||||
shrinkStr = `10^${exp}x smaller`;
|
||||
}
|
||||
pill(ctx, shrinkStr, Math.max(m.l + 50, Math.min(w - m.r - 50, pulseX)), pulseY + 42, C.yellow, 'center');
|
||||
// safe-area clamp: keep the label at least half its assumed width away from each edge
|
||||
ctx.save();
|
||||
ctx.font = '12px system-ui, sans-serif';
|
||||
const assumed = ctx.measureText(shrinkStr).width / 2 + 10;
|
||||
ctx.restore();
|
||||
const labelX = Math.max(m.l + assumed, Math.min(m.l + pW - assumed, pulseX));
|
||||
pill(ctx, shrinkStr, labelX, pulseY + 44, C.yellow, 'center');
|
||||
|
||||
// Title: visceral, not technical
|
||||
text(ctx, 'From your transmitter to the echo off the Moon', w / 2, 22, C.fg, 'center', 'alphabetic', 14);
|
||||
text(ctx, 'the signal gets one billion trillion times weaker', w / 2, 42, C.dim, 'center', 'alphabetic', 12);
|
||||
text(ctx, '(that is a 1 followed by 21 zeros)', w / 2, 60, C.dim, 'center', 'alphabetic', 11);
|
||||
// Title, sized to fit narrow viewports
|
||||
const titleSize = w < 460 ? 12 : 14;
|
||||
const subSize = w < 460 ? 10 : 12;
|
||||
text(ctx, 'transmitter to Moon echo', w / 2, 24, C.fg, 'center', 'alphabetic', titleSize);
|
||||
text(ctx, 'one billion trillion times weaker', w / 2, 24 + titleSize + 6, C.dim, 'center', 'alphabetic', subSize);
|
||||
text(ctx, '(a 1 followed by 21 zeros)', w / 2, 24 + titleSize + subSize + 14, C.dim, 'center', 'alphabetic', subSize - 1);
|
||||
|
||||
requestAnimationFrame(draw);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue