Virtual Abacus 2
by wio_dude
HTML
<div id="content"></div>
JavaScript
const DEFAULT_OPTS = {
columns: 16,
rows: [1, 4],
width: 300,
height: 75,
frameWidth: 5,
frameHeight: 5,
activeBarHeight: 4,
beadBarWidth: 3,
beadGapWidthRatio: 0.1,
beadGapHeightRatio: 0.5,
}
class SVGAbacusBead {
#root;
#value;
#position;
#gap;
constructor(opts, column, rowGroup, row) {
const {
frameWidth,
frameHeight,
width,
height,
activeBarHeight,
beadBarWidth,
beadGapHeightRatio,
beadGapWidthRatio,
columns,
rows
} = opts;
const innerWidth = width - 2 * frameWidth;
const innerHeight = height - 2 * frameHeight - activeBarHeight;
const columnWidth = innerWidth / columns;
const beadWidth = columnWidth * (1 - beadGapWidthRatio);
const beadHeight = innerHeight / (5 + 2 * beadGapHeightRatio);
this.#position = beadHeight * (rows[rowGroup] - 1 - row) + beadHeight / 2;
this.#gap = beadGapHeightRatio * beadHeight;
const bead = document.createElementNS('http://www.w3.org/2000/svg', 'ellipse');
bead.setAttribute('fill', 'blue');
bead.setAttribute('cy', this.#position);
bead.setAttribute('rx', beadWidth / 2);
bead.setAttribute('ry', beadHeight / 2);
this.#root = bead;
this.#value = 0;
}
get value() {
return this.#value;
}
set value(x) {
const value = Math.min(1, Math.max(0, x));
this.#value = value;
this.#root.setAttribute('cy', this.#position + this.#gap * this.#value);
}
ensureAbove(x) {
this.value = Math.max(this.#value, x);
}
ensureBelow(x) {
this.value = Math.min(this.#value, x);
}
get root() {
return this.#root;
}
}
class SVGAbacusColumn {
#root;
#beadBar;
#beadGroups;
#beads;
constructor(opts, column) {
const {
frameWidth,
frameHeight,
width,
height,
activeBarHeight,
beadBarWidth,
beadGapHeightRatio,
beadGapWidthRatio,
columns,
rows,
} = opts;
const innerWidth =...