Building A Drawing Board Using JavaScript

Building A Drawing Board Using JavaScript

Introduction

Drawing boards are a big part of many applications today, such as Google Jamboard, Canva, sketch.io and many more.

With so many such apps out there, they still haven't scratched the itch to get a customized drawing board that can be personalized to my thoughts. So, I thought why not make one where I can write the components I want.

"How will those components work?", you ask?

  • Need a binary tree? Write nodes in order like ['H', 'E', 'L', 'L', 'O'] and Voila! A Binary Tree Sketch.

  • Need a neural network? Enter your config and Bam! Get a neural network ready!

In this article, we are going to make a drawing board so that you can personalize your own components according to your needs.

We are going to use a library called Fabric.js, which is perfect for these scenarios.

tree.gif

In the GIF above, you can see the way I solved my problem for trees and neural networks. The GIF below is the product that you will make using this article. Then you can tinker and add the code for your components.

The app made in this article requires only JavaScript, HTML and CSS so that most people can build it. In case you want to use this in your app with a different stack, know that I have used it with React as well and there is also an npm package available.

icecreamEditor.gif

Step 0

Make one HTML, one CSS and one JavaScript file, link them and add Fabric.js in your script tags.

I will be adding links to code pen in this article at certain points, so you can copy code and play around in the editor.

Step 1

Let's start by defining our Canvas. This is the area where we will draw and render components based on the input. First, add a canvas tag in your HTML as shown below:

 <canvas id="demoCanvas"/>

Fabric.js gives us a Canvas class whose instance can be created by simply invoking new fabric.Canvas. So, let us add that to our JavaScript file. See in the code below, how we pass it in our demoCanvas id and an object with some properties to its constructor. This tiny boilerplate is all that we need to start basic sketching in Fabric.

const myCanvas = new fabric.Canvas("demoCanvas", {
  width: window.innerWidth - 200,
  height: window.innerHeight - 100,
  backgroundColor: "white",
  isDrawingMode: true,
});

Go ahead and show some of your drawing skills before we move to the next step.

The 'object' that we passed after the id in our Canvas Class in Fabric signifies the properties that we want to set in our Component (currently Canvas itself).

In this 'object', backgroundColor, height and width are self-explanatory.

Fabric.js also gives us many other properties (you can read about them here). The one we just used is called isDrawingMode which, as you can guess, acts as a switch to start and stop drawing mode.

Step 2

Now that we have a drawing mode ready, Let's add two buttons to our app which will be toggle draw mode and add rectangle shape.

  • Toggle Draw Mode: This button will toggle between drawing and selecting the drawing and the shapes we will add. So, when we click this button, what it needs to do is if 'drawing mode' (isDrawingMode) is enabled, then disable it and vice-versa.
    const toggleDraw = () => {
    myCanvas.set({ isDrawingMode: !myCanvas.get("isDrawingMode") });
    };
    
  • Rectangle: This will generate a new Rectangle shape and add it to our canvas every time we click on it.
    const createRectangle = () => {
    const rectangle = new fabric.Rect({
      width: 100,
      height: 100,
    });
    myCanvas.add(rectangle);
    };
    
    So what does the above code do? It tells Fabric to make an instance of its rectangle class and we pass how big the rectangle should be using width and height attributes.

Lastly, we instruct Fabric.js to add this rectangle to the canvas instance that we just made.

Step 3

Now, let us quickly add a Textbox and a simple delete button.

const createTextbox = () => {
  const textbox = new fabric.Textbox("Hakuna Matata",{
    width : 400,
  });
  myCanvas.add(textbox);
};

const deleteObject = () => {
  if(myCanvas.getActiveObject()){ myCanvas.remove(myCanvas.getActiveObject());
  }
}

Adding a textbox is as simple as switching the name from Rect to Textbox in Fabric.js.

For the delete button, we use Fabric's getActiveObject method. This function returns the currently selected object in our canvas. So, what we wrote in deleteObject function was that when the delete button is clicked and if there is an object which is selected, remove it. Do you remember how simple adding things to canvas was? Well, removing is just as easy.

While you can always add a rectangle with height 1 and pretend that it is a line, simply saying Line instead of Rect will do the job for you. Same goes for a Circle.


Look at the progress so far. We have a drawing board where we can:

  • Add shapes such as Rectangle, Circle, Line etc.
  • Add a textbox where we can write using our keyboard.
  • Delete an object and the Path we draw.
  • Freehand drawing.

Step 4

Now, lets us add the last element in this tool which is not found in the common editors.

We are going to make an ice-cream element.

Why? Because I love Ice-Cream.

To make an ice cream element, we will need an ice cream cone and the number of scoops on top of the ice cream.

This could be our input factor. So, now all we need is an input element for users to tell us the number of scoops in the ice cream.

So we make a button to make ice cream and an input element to our HTML File, like below:

<button onclick="icecreamFunction()"> Ice cream </button>
<input type="number" id="scoops" value="1" min="1" max="5">

Now that we have an input element,

  • We will get the value from this element using its corresponding id.
  • Then, to make a stack of all the ice cream scoops, we are going to write a simple for loop and add those many circles. These circles will represent the scoops we are talking about.
  • We will have a variable called icecreamScoops, in which we will push these circles (our scoops). Since, we need to stack one scoop on top of each other, we will use the top property of fabric.Circle to decrease the y-coordinate on every loop.

    const icecreamFunction = () => {
    const scoops = document.getElementById("scoops").value;
    
    const scoopRadius = 20;
    let initialPos = 0;
    const icecreamScoops = [];
    for(let i=0;i<scoops;i++){
      initialPos += scoopRadius/2;
      const flavour =  new fabric.Circle({
        top: -initialPos,
        left:0,
        radius: scoopRadius,
        fill : "red",
      });
      icecreamScoops.push(flavour);
    }
    

    After adding the scoops, we will now make a Triangle element using Fabric. This will act as the cone for the ice creams.

    const icecreamCone = new fabric.Triangle({
      width: 40,
      height: 80,
      top: scoopRadius,
      left:0,
      fill: "#d2b48c",
      flipY: true,
    });
    

    Now, let us add our ice cream scoops on top of our ice cream cone. For this purpose, where we need to combine different element, FabricJS gives us another class called fabric.Group.

The idea of Groups is simple. Just add all the elements in an array as the first argument of the group and that's it, they are joined.

const icecream = new fabric.Group([...icecreamScoops, icecreamCone], {left: 100,
    top: 100,
  })
myCanvas.add(icecream);

So, let us add all the icecreamScoops using the spread operator and then add our icecreamCone.

Finally, do not forget to add this fabric.Group element to the Canvas.

That's it, you have finally created a drawing board for yourself.

Now go and translate your thoughts into code that makes it into a drawing :)