Thursday, August 17, 2017

6. Using Chart.js library

The easiest ways of creating a bar chart is using a library.


Here the Chart.js library is used to create a Bar Chart like in the previous example.


// 6. Using Chart.js library

var ctx = document.getElementById('myCanvas').getContext('2d');
var chart = new Chart(ctx, {
    type: 'bar',
    data: {
        labels: [ "California", "Texas", "Florida", "New York",
                  "Illinois", "Pennsylvania", "Ohio", "Georgia",
                  "North Carolina", "Michigan" ],
        datasets: [{
            label: "Ten largest states",
            backgroundColor: 'rgb(255, 99, 132)',
            borderColor: 'rgb(255, 99, 132)',
            data: [ 39.250017, 27.862596, 20.612439, 19.745289, 12.801539,
                    12.784227, 11.646273, 10.310371, 10.146788, 9.928301]
        }]
    },
    options: {}
});

The html file links to the Chart.js library:


<!DOCTYPE HTML>
<html>
<head>
  <style>
    canvas {
      position: absolute;
      left: 0;
      top: 0;
    }
  </style>
</head>
<body>
  <canvas id="myCanvas" width="1200" height="600"></canvas>
  <script src="Chart.js"></script>
  <script src="main.js"></script>
</body>
</html>

Output:


5. Bar chart

A Bar class is used to create a bar chart. In the constructor, we initialize most values, such as data.


The data is an array of length 10, each entry being an array of length 2, with state name and state population in millions, which will be the x and y values. There are logging of values, and setting debug to true or false will lead to text rectangles being shown or not. The xAxis() and yAxis() as well as bars() function can be combined into a draws function. However, if you should comment out the axis function calls, axis will not be shown.


// 5. Bar Chart

let canvas = document.querySelector("#myCanvas");
let ctx = canvas.getContext("2d");

class Bar {

  constructor(params) {
    this.data = data;
    this.offx = params.off.x;
    this.offy = params.off.y;
    this.width = params.width;
    this.ticks = params.ticks;
    this.tickWidth = params.tickWidth;
    this.max = params.max;
    this.font = params.font;
    this.style = params.style;
    this.x = this.offx + this.width;
    this.y = this.offy + this.ticks * this.width
    this.totWidth = this.width * (3/2 * this.data.length + 2);
    this.debug = params.debug;
    console.log("Width of bar graph: " + this.totWidth);
    console.log("From: " + this.offx);
    console.log("To: " + (this.offx + this.totWidth));
  }

  yAxis() {
    ctx.font = this.font;
    ctx.textAlign = "center";
    ctx.fillStyle = this.style;
    ctx.strokeStyle = this.style;
    ctx.lineWidth = 2;
    for(let i = 0; i<this.ticks; i++) {
      let textVal = this.max - i/(this.ticks-1) * this.max;
      console.log(textVal);
      if (this.debug) {
        ctx.strokeRect(this.offx, i*this.width + this.offy,
            this.width, this.width);
      }
      let posY = (i+1)*this.width + this.offy;
      console.log("i = " + i + ", posY = " + posY);
      ctx.fillText(textVal+"",this.offx + this.width/2, posY );
      ctx.beginPath();
      ctx.moveTo(this.x, posY);
      console.log(this.x);
      ctx.lineTo(this.x + this.tickWidth, posY);
      console.log(this.x + this.tickWidth);
      ctx.stroke();
    }
    ctx.beginPath();
    ctx.moveTo(this.x,this.offy);
    ctx.lineTo(this.x,this.offy + this.ticks * this.width);
    ctx.stroke();
  }

  xAxis() {
    ctx.beginPath();
    ctx.moveTo(this.offx + this.width, this.y);
    ctx.lineTo(this.offx + this.totWidth, this.y);
    ctx.stroke();
    ctx.textAlign = "center";
    for(let i=0; i<this.data.length; i++) {
      let posX = this.x + this.width/2 * (3 * i + 1);
      console.log("i = " + i + ", posX = " + posX);
      if (this.debug === true) {
        ctx.strokeRect(posX, this.y, this.width, this.width);
      }
      ctx.fillText(this.data[i][0], posX  + this.width/2,
                   this.y + this.width/2, this.width);
    }
  }

  bars(style) {
    ctx.fillStyle = style;
    for(let i=0; i<data.length; i++) {
      let posX = this.x + this.width/2 * (3 * i + 1);
      let val = data[i][1];
      let height = val/this.max*(this.ticks-1)*this.width;
      ctx.fillRect(posX, this.offy + this.width*this.ticks - height,
        this.width, height);
    }
  }
}

let data = [   ["California",39.250017],
               ["Texas",27.862596],
               ["Florida",20.612439],
               ["New York",19.745289],
               ["Illinois",12.801539],
               ["Pennsylvania",12.784227],
               ["Ohio",11.646273],
               ["Georgia",10.310371],
               ["North Carolina",10.146788],
               ["Michigan",9.928301]            ];

bar = new Bar({
  data: data,
  font: "14px Comic",
  style: "purple",
  off: {x:30, y:40},
  width: 70,
  ticks: 5,
  tickWidth: 10,
  max: 50,
  debug: false
});

bar.yAxis();
bar.xAxis();
bar.bars("blue");

Output (debug is true):



Output (debug is false):


4. Ellipses

An Ellipse class is written storing center coordinates (x, y) and radius in x,y (radiusX, radiusY). It also has a draw method to draw the ellipse using the ellipse method.


The draw method requires optional fill color which is "blue" by default. The rotation is fixed at 0 and start angle is 0 and stop angle is 2 pi for a complete ellipse.


// 4. Ellipses

let canvas = document.querySelector("#myCanvas");
let ctx = canvas.getContext("2d");

class Ellipse {
  constructor(x, y, radiusX, radiusY) {
    this.x = x;
    this.y = y;
    this.radiusX = radiusX;
    this.radiusY = radiusY;
  }
  draw(color="blue") {
    ctx.beginPath();
    ctx.ellipse(this.x, this.y, this.radiusX, this.radiusY,
      0, 0, 2 * Math.PI);
    ctx.fillStyle = color;
    ctx.fill();
  }
}

let ellipse1 = new Ellipse(100, 100, 40, 86);
ellipse1.draw("red");

let ellipse2 = new Ellipse(300, 100, 42, 84);
ellipse2.draw();

let ellipse3 = new Ellipse(100, 300, 84, 42);
ellipse3.draw("green");

let ellipse4 = new Ellipse(300, 300, 86, 40);
ellipse4.draw("purple");

Output:


3. Circles

A Circle class stores center coordinates (x, y) and radius. It also has a draw method to draw the circle using the arc method.


The draw method requires optional fill color which is "blue" by default.


// 3. Circles

let canvas = document.querySelector("#myCanvas");
let ctx = canvas.getContext("2d");

class Circle {
  constructor(x, y, radius) {
    this.x = x;
    this.y = y;
    this.radius = radius;
  }
  draw(color="blue") {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, 2*Math.PI);
    ctx.fillStyle = color;
    ctx.fill();
  }
}

let circle1 = new Circle(100, 100, 40);
circle1.draw("red");

let circle2 = new Circle(200, 100, 42);
circle2.draw();

let circle3 = new Circle(100, 200, 44);
circle3.draw("green");

let circle4 = new Circle(200, 200, 46);
circle4.draw("purple");

Output:


2. Stroke Rectangles

This Rect class expects i and j grid locations and gets x, y, width, and height of the rectangles using global variables. It generates random colors for their stroke and a light color for the fill.


The draw method uses strokeRect and fillRect. In the loop we draw xtot rectangles in x-direction and ytot rectangles in the y-direction. Now we have simplified the loop and increased complexity of class. Usually this is what you want to do so the main code is less.


// 2. stroke Rectangles

let canvas = document.querySelector("#myCanvas");
let context = canvas.getContext("2d");
let strokeSize = 10;

let xoff = 10, yoff = 20,
    xsize = 50, ysize = 50,
    xpad = 10, ypad = 10,
    xtot = 4, ytot = 3;

class Rect {
  constructor(i, j) {
    this.x = xoff + i * (xsize + xpad);
    this.y = yoff + j * (ysize + ypad);
    this.width = xsize;
    this.height = ysize;
    this.color = "rgb(" + Math.floor(Math.random() * 256) +
                 "," + Math.floor(Math.random() * 256) +
                 "," + Math.floor(Math.random() * 256) + ")";
  }
  draw() {
    context.lineWidth = strokeSize;
    context.strokeStyle = this.color;
    context.strokeRect(this.x, this.y, this.width, this.height);
    context.fillStyle = "#EDC";
    context.fillRect(this.x, this.y, this.width, this.height);
  }
}

for (let j = 0; j < ytot; j++) {
  for (let i = 0; i < xtot; i++) {
    let rect = new Rect(i, j);
    rect.draw();
  }
}

Output:


1. Filled Rectangles

A Rect class holds the x, y, width, and height of the rectangles. It generates random colors for their fill.


The draw method uses fillRect. In the loop we draw xtot rectangles in x-direction and ytot rectangles in the y-direction. Each rectangle has x,y size (xsize, ysize) and also x,y padding (xpad, ypad). There are x,y offsets (xoff, yoff), and thus the top-left rectangle starts at those coordinates.


let canvas = document.querySelector("#myCanvas");
let context = canvas.getContext("2d");


class Rect {
  constructor(x, y, width, height) {
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
    this.color = "rgb(" + Math.floor(Math.random() * 256) +
                 "," + Math.floor(Math.random() * 256) +
                 "," + Math.floor(Math.random() * 256) + ")";
  }
  draw() {
    context.fillStyle = this.color;
    context.fillRect(this.x, this.y, this.width, this.height);
  }
}

let xoff = 10, yoff = 20,
    xsize = 45, ysize = 50,
    xpad = 5, ypad = 10,
    xtot = 5, ytot = 6;

for (let j = 0; j < ytot; j++) {
  let y = yoff + j * (ysize + ypad);
  for (let i = 0; i < xtot; i++) {
    let x = xoff + i * (xsize + xpad);
    let rect = new Rect(x, y, xsize, ysize);
    rect.draw();
  }
}

Output: