<head>
  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Montserrat:400,400i,700">
</head>
<body>
  <div class="container">
    <canvas id="myCanvas" width="270px" height="180px"></canvas>    

    <div class="result">
      <div class="label">Preorder</div>
      <div id="preorder"></div>  
    </div>
    
    <div class="result">
      <div class="label">Inorder</div>
      <div id="inorder"></div>
    </div>
    
    <div class="result">
      <div class="label">Postorder</div>
      <div id="postorder"></div>
    </div>

    <button type="button" onclick="window.preOrder()">Preorder</button>
    <button type="button" onclick="window.inOrder()">Inorder</button>
    <button type="button" onclick="window.postOrder()">Postorder</button>
  </div>
</body>
  
body {
  --black: #2e2e2e;
  --box-shadow: 1px 4px 5px 1px rgb(0 0 0 / 35%);
  font-family: Montserrat, sans-serif;
  display: flex;
  justify-content: center;
  margin-top: 2rem;
}

canvas {
  display: block;
  margin: auto; 
}

.result {
  height: 3.5rem;
}

.label {
  font-weight: bold;
}



button {
  font-family: Montserrat, sans-serif;
     margin: 1rem 0;
     padding: 6px 10px;
     border-radius: 5px;
     cursor: pointer;
     color: #FFFFFF;
     box-shadow: var(--box-shadow);
     background: var(--black);
 }

button:hover, button:focus {
     transform: scale(1.05);
 }

button:disabled {
  opacity: 0.5;
}

paper.install(window);

const btns = [...document.getElementsByTagName("button")];

function BinaryTree(el){
  this.element = el;
  this.left = null;
  this.right = null;
  this.node = null;
}

function createTree(nodes){ // it is kind of preorder style, because we have node itself first then its children
 
  function datum(tree){ return tree[0] }
  function leftChild(tree){ return tree[1] }
  function rightChild(tree){ return tree[2] }
   
  if(nodes == null)
    return null; 
 
  var tree = new BinaryTree( datum(nodes) || nodes );
  tree.left = createTree( leftChild(nodes) );
  tree.right = createTree( rightChild(nodes) );

  return tree;
}

nodes = [5, [3, 2, 4], [8, 6, 9] ];
var tree = createTree(nodes)
new VisualTree(tree, false)

function VisualTree(tree, animation){
  var origin = [190, 25]; 
  var height  = getHeight(tree);  
  
  paper.setup("myCanvas"); 
  document.getElementById("myCanvas").height = height*60+10;
  
  function BinaryNode(tree, depth, x){
    var pos = new Point(origin[0]+ x*height*30 , origin[1] + depth*50)

    function drawEdge(pos1, pos2){
      return new Path.Line({from: pos1, to: pos2, strokeColor: 'black', strokeWidth:2}).sendToBack()
    } 

    if(tree.left) // if left exists, the right child also exists
      this.leftEdge = drawEdge(pos, pos.add([-(1 / Math.pow(2,depth))*height*30, 50]))
    if(tree.right)
      this.rightEdge = drawEdge(pos, pos.add([(1 / Math.pow(2,depth))*height*30, 50]))
    
    this.circle = new Path.Circle( { radius:20, strokeWidth:2, fillColor: 'white', strokeColor:'black', center: pos });
    this.text = new PointText({ position: new Point(pos.x-8, pos.y+10), fontSize: '30px', fillColor: 'black', content:''+ tree.element});
    this.text.bringToFront();
 
    view.update();
  }

  t=0
  function drawPreOrder(tree, depth, x){ // depth: depth of the current node, x: x-coordinate of the current node
    if(tree == null)
      return	  
       
    if(animation)
      setTimeout(function() { tree.node = new BinaryNode(tree, depth, x); }, 500*(t++));
    else
      tree.node = new BinaryNode(tree, depth, x);

    drawPreOrder(tree.left, depth+1, x-(1 / Math.pow(2,depth)))
    drawPreOrder(tree.right, depth+1, x+(1 / Math.pow(2,depth)))
  }
  
  function getHeight(tree){
    if(tree == null)
      return 0;

    return Math.max(getHeight(tree.left)+1, getHeight(tree.right)+1) 	
  }
  drawPreOrder(tree, 0, 0)
}

var time = 0;
function deleteTreeVisual(tree){
  
  if(tree == null)
    return;
    
  deleteTreeVisual(tree.left);
  deleteTreeVisual(tree.right); 
  
  if(tree.node.leftEdge)
    tree.node.leftEdge.remove();
  
  if(tree.node.rightEdge)
    tree.node.rightEdge.remove();
  
  tree.node.circle.remove();
  tree.node.text.remove();
  
  view.update();
}

var time = 0;
function postOrderTraverse(tree){
  if(tree == null)
    return;	 
  
  // disable buttons during animation 
  btns.forEach(btn => btn.disabled = true);
 
  setTimeout(function() {
    btns.forEach(btn => btn.disabled = false);
  }, 10000);
           
  setTimeout(function() {  
      tree.node.circle.strokeColor = 'red';
      view.update();
    }, 750*(time++)); 

  postOrderTraverse(tree.left)
  postOrderTraverse(tree.right)  
    
  setTimeout(function() { 
      tree.node.circle.fillColor = 'black';
      tree.node.text.fillColor = 'white'; 
      document.getElementById("postorder").innerHTML += tree.element + ' ';

      view.update()
    }, 750*(time++));
}

function inOrderTraverse(tree){
  if(tree == null)
    return;	  
  
  // disable buttons during animation 
  btns.forEach(btn => btn.disabled = true);
 
  setTimeout(function() {
    btns.forEach(btn => btn.disabled = false);
  }, 10000);
          
  setTimeout(function() {  
      tree.node.circle.strokeColor = 'red';
      view.update()
    }, 750*(time++)); 

  inOrderTraverse(tree.left)
    setTimeout(function() { 
      tree.node.circle.fillColor = 'black';
      tree.node.text.fillColor = 'white'; 
      document.getElementById("inorder").innerHTML += tree.element + ' ';
      view.update()
    }, 750*(time++));
  inOrderTraverse(tree.right)  
}

function preOrderTraverse(tree){
  if(tree == null)
    return;	  
  
    // disable buttons during animation 
  btns.forEach(btn => btn.disabled = true);
 
  setTimeout(function() {
    btns.forEach(btn => btn.disabled = false);
  }, 10000);
         
  setTimeout(function() {  
      tree.node.circle.strokeColor = 'red';
      view.update()
    }, 750*(time++)); 

    setTimeout(function() { 
      tree.node.circle.fillColor = 'black';
      tree.node.text.fillColor = 'white'; 
      document.getElementById("preorder").innerHTML += tree.element + ' ';
      view.update()
    }, 750*(time++));
   
  preOrderTraverse(tree.left)
  preOrderTraverse(tree.right)  
}

window.preOrder = function(){
  document.getElementById("preorder").innerHTML = '';
  deleteTreeVisual(tree)
  new VisualTree(tree, false)
  time = 0; preOrderTraverse(tree);
}
window.inOrder = function(){
  document.getElementById("inorder").innerHTML = '';
  deleteTreeVisual(tree)
  new VisualTree(tree, false)
  time = 0; inOrderTraverse(tree);
}
window.postOrder = function(){
  document.getElementById("postorder").innerHTML = ''
  deleteTreeVisual(tree)
  new VisualTree(tree, false)
  time = 0; postOrderTraverse(tree);
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.9.18/paper-full.min.js