<svg version="1.1" id="board" class="container" width="1000" height="500" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1000 500" xml:space="preserve">

  <style type="text/css">
    .element{
        stroke:#F15A24;
        stroke-width:20px;
        fill:none;
      }
  </style>

  <rect id="post_1" x="178" y="450" width="25" height="25" />

  <rect id="post_2" x="306" y="450" width="25" height="25" />

  <rect id="post_3" x="434" y="450" width="25" height="25" />

  <rect id="post_4" x="562" y="450" width="25" height="25" />

  <rect id="post_5" x="690" y="450" width="25" height="25" />

  <rect id="mid_post" x="50" y="177" width="25" height="25" />
  
  <rect id="frame" class="element" x="40" y="40" width="800" height="420" rx="0" />

  <symbol id="bead">
    <ellipse cx="35" cy="20" rx="35" ry="20" fill="deepPink" fill-opacity="0.8" />
  </symbol>

  <symbol id="post">
    <rect width="25" height="25" />
  </symbol>

</svg>
#viewPort {
  background-color: black
}

#viewPort2 {
  background-color: #1d1d1d
}
var bead = function(value, options, canMoveUp) {
  this.value = value;
  this.shape = this.drawSVGBead(options);
  this.beadAbove = null;
  this.beadBelow = null;
  this.canMoveUp = canMoveUp;
  this.currPos = options.yPos;
  var self = this;

  this.moveBead = function() {

    // depending on the bead.canMoveUp property, animation bead movement and update the canMoveUp property
    if (!self.canMoveUp) {
      //moving the bead down
      var curTransform = new WebKitCSSMatrix(window.getComputedStyle(self.shape).webkitTransform);
      TweenLite.to(self.shape, 0.3, {
        y: self.currPos + 48
      }, 1.8);

      if (self.beadBelow !== null && !self.beadBelow.canMoveUp) {
        self.beadBelow.moveBead();
      }

      self.currPos += 48;
    } else {
      //moving bead up
      var curTransform = new WebKitCSSMatrix(window.getComputedStyle(self.shape).webkitTransform);
      TweenLite.to(self.shape, 0.3, {
        y: self.currPos - 48
      }, 1.8);

      if (self.beadAbove !== null && self.beadAbove.canMoveUp) {
        self.beadAbove.moveBead();
      }

      self.currPos -= 48;
    }

    self.canMoveUp = !self.canMoveUp;
  }

  this.shape.addEventListener("click", this.moveBead, false);
}

bead.prototype.drawSVGBead = function drawSVGBead(options) {

	options = options || {};
	var xPos = options.xPos || 0,
		yPos = options.yPos || 0,
		aSVGBead;

	aSVGBead = document.createElementNS('http://www.w3.org/2000/svg', 'use');
	aSVGBead.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href','#bead');
	aSVGBead.setAttributeNS(null, "width", 70);
	aSVGBead.setAttributeNS(null, "height", 50);

	TweenLite.set(aSVGBead, {x:xPos, y:yPos});
	return aSVGBead;
}

var abacus = function(abacusName, columns, topBeadCount, bottomBeadCount) {
  this.element = document.getElementById("board");
	this.columns = columns;
	this.topBeadCount = topBeadCount;
	this.bottomBeadCount = bottomBeadCount;
	this.topLevelNodeHeads = []; 	
	this.bottomLevelNodeHeads = [];
	this.beadsXAxis = [667, 539, 411, 283, 155]; //Placing the beads from the right most axist fo the left
	this.beadYAxisSpaceTop = 50;
	this.beadYAxisSpaceBottom = 410;
	this.beadYAxisMovementTop = 71;
	
	//Finding out the maximum number the current abacus can hold
	var findMaxAbacusVal = function(digits){
		var max = 0;
		for(var i=0; i<digits; i++)
		{
			max+=Math.pow(10, i)*9
		}
		return max;
	}

	this.maxAbacusVal = findMaxAbacusVal(columns);
	this.displayedNumber = 0;
}

abacus.prototype.insertBead = function(headBead, newBead) {
	newBead.beadBelow = headBead;
	headBead.beadAbove = newBead;
	return newBead;
}

abacus.prototype.fillBeads = function() {
	var beadWidth = 50;
	var beadHeight = 40;

	//Loop through the number of columns the abacus contains
	for(var i=0; i<this.columns; i++)
	{
		//Inserting the top-level beads where the initial position is the two beads aligning to the top
		//The head of the top bead will be the second bead from the top
		var head = null;
		for (var j=this.topBeadCount-1; j>-1; j--) 
		{
			var beadShapeOptions = {xPos:this.beadsXAxis[i], yPos:this.beadYAxisSpaceTop+(j*beadHeight)};

			var value = Math.pow(10, i)*5;
			var newBead = new bead(value, beadShapeOptions, false);
			this.element.appendChild(newBead.shape);	

			if (head === null) {
				head = newBead;
				this.topLevelNodeHeads.push(head);
			}
			else {
				var insertedNewBead = this.insertBead(head, newBead);
				head = insertedNewBead;
			}
		}

		//Inserting the top-level beads where the initial position is the two beads aligning to the top
		//The head of the bottom bead will be the bead near the top 
		head = null;
		for (var j=0; j<this.bottomBeadCount; j++) 
		{
			var beadWidth = 50;
			var beadHeight = 40;
			var beadShapeOptions = {xPos:this.beadsXAxis[i], yPos:this.beadYAxisSpaceBottom-(j*beadHeight)};

			var value = Math.pow(10, i);
			var newBead = new bead(value, beadShapeOptions, true);
			this.element.appendChild(newBead.shape);

			if (head === null) {
				head = newBead;
				//this.bottomLevelNodeHeads.push(head);
			}
			else {
				var insertedNewBead = this.insertBead(head, newBead);
				head = insertedNewBead;
			}
		}

		this.bottomLevelNodeHeads.push(head);
	}
}

window.onload = function() {

  // Drawing the basic abacus frame
  var frame = $("#frame"),
      midPost = $("#mid_post"),
      posts = $("#post_1, #post_2, #post_3, #post_4, #post_5, #post_6"),
      tl = new TimelineMax();

  tl.to(frame, 2, {
    attr: {
      rx: "30"
    }
  })
  tl.to(posts, 2, {
    scaleY: -16
  }, 1.8)
  tl.to(midPost, 2, {
    scaleX: 32
  })

  var cnAbacus = new abacus("chineseAbacus", 5, 2, 5);
  cnAbacus.fillBeads();
};
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/gsap/1.16.1/TweenMax.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/gsap/1.16.1/TimelineLite.min.js
  3. https://code.jquery.com/jquery-latest.min.js