function numberDial(value, dials = 2, base = 10, hideLeadingZeros = false) {
const margin = 5;
const radius =
Math.max(20, Math.min(width / dials / 2 - margin * 2, base*16));
const w = width;
const h = (radius + margin * 2) * 2;
const ctx = DOM.context2d(w, h);
const x0 = 0;
const y0 = h / 2 - radius;
ctx.lineCap = "round";
ctx.lineJoin = "round";
ctx.lineWidth = 4;
const circle = (x, y, r, {fill, stroke}) => {
ctx.beginPath();
if (fill) ctx.fillStyle = fill;
if (stroke) ctx.strokeStyle = stroke;
ctx.moveTo(x + r, y);
ctx.arc(x, y, r, 0, Math.PI * 2, false);
if (fill) ctx.fill();
if (stroke) ctx.stroke();
}
let minDial = hideLeadingZeros ? dials-1 : 0;
for (let i = 0; hideLeadingZeros && i < dials; i++) {
const offset = (value / base ** (dials - 1 - i)) % base | 0;
if (offset) {
minDial = i;
break;
}
}
for (const [r, style] of [
[radius, {fill: "#F8F8F8", stroke: "#111"}],
[radius / 20, {fill: "#444", stroke: "#444"}]
]) {
for (let i = minDial; i < dials; i++) {
circle(x0 + margin + radius * (1 + i * 2), y0 + margin + radius, r, style);
}
}
ctx.beginPath();
const dialTextSize = Math.min(
48,
radius / 2,
radius * 0.75 * (base > 2 ? Math.sin((2 * Math.PI) / base) : 1)
);
ctx.font = `${dialTextSize}px sans`;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
for (let i = minDial; i < dials; i++) {
const cx = margin + x0 + radius * ((0.5 + i) * 2);
const cy = margin + y0 + radius;
const offset = (value / base ** (dials - 1 - i)) % base | 0;
for (let j = 0; j < base; j++) {
const x =
cx +
(radius - dialTextSize * 0.65) * Math.sin((j * 2 * Math.PI) / base);
const y =
cy +
(radius - dialTextSize * 0.65) * -Math.cos((j * 2 * Math.PI) / base);
if (j === offset) {
ctx.beginPath()
ctx.strokeStyle = "#444";
ctx.moveTo(cx, cy);
ctx.lineTo((cx * 0.5 + x * 0.5), (cy * 0.5 + y * 0.5));
ctx.stroke();
}
const numberText = b64[j];
ctx.fillStyle = j !== offset ? "#AAA" : "#000";
ctx.strokeStyle = "#FFF";
ctx.strokeText(numberText, x, y);
ctx.fillText(numberText, x, y);
}
}
return ctx.canvas;
}