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 =...