Making a Whiteboard Application with ReactJs

Surya Bansal
7 min readJun 26, 2021

--

A light drawing web application using HTML5 Canvas API and Roughjs

This is a good project for beginners who want to use React and its hooks and API reference functionality.

The challenge here is to use the canvas API in React instead of vanilla JavaScript and HTML. Let’s start making a whiteboard from scratch!

The first step to your application is to create your react-app with one simple step. I am using npx(Node Package eXecute) instead of npm as it is faster and executes any package that you want from the npm registry without even installing that package. Rough.js is a light javascript library that will make the lines and shapes in a hand-drawn-like style.

Write the following commands in the terminal at a suitable location(npm is an alternative).

npx create-react-app whiteboard-app 
cd whiteboard-app
npm install --save roughjs
npm install bootstrap
npm start

add the following code to your src/index.js file to use bootstrap.

import 'bootstrap/dist/css/bootstrap.min.css';

move to src/App.js and edit it as follows:

Since there is no drawingtool.js file, our next step is to create a folder named “components” in folder src. Here all the components being rendered will be created. Make one new file named “drawingtool.js”.

Similarly, create “penciltool.js” which will be used later.

File Structure

✔️Part 0: Adding Prettier in VSCode(Optional)*

It is a good practice to indent your code properly for easier readability. I always create a .prettierrc.json file that formats my code automatically. Follow these steps to proceed:

Step 1: Launch VS Code Quick Open( Ctrl+P/⌘P).Paste the following command, and press enter.

ext install esbenp.prettier-vscode

Step 2: Make a .prettierrc.jsonfile at the root of your project and write the following:

{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}

Step 3: Ctrl+S. And it should work! Note: The formatter works on all files in your current folder(whiteboard-app).

✔️Part 1: Adding canvas API to your project

The React hook useEffect helps in adding componentDidUpdate and componentDidMount combined lifecycle in React’s functional component.

Let’s Move to drawingtool.js and add the following code to it.

the <canvas> element along with JavaScript lets us use the canvas API. It has many functionalities and supports animations but sticking to this application we are using features to draw shapes and scribble on the whiteboard.

The Document.getElementById() method gets a reference to the HTML <canvas> element. Next, the HTMLCanvasElement.getContext() method gets that element's context—the thing onto which the drawing will be rendered. It returns null if the context identifier is not supported. The actual drawing is done using the CanvasRenderingContext2D. Here "2d"is leading to the creation of an object representing a two-dimensional rendering context.

Next, we use Rough.js which is responsible for rendering Canvas. It also supports SVGs. We create a roughCanvas variable that stores the canvas when passed through the rough.canvas() method. This canvas is accessed by its id which is stored in canvas(line 10).

we are also making use of a generator which is a readonly property that lets you create a drawable object for a shape that can be later used with draw method. In the above code, we have created three shapes. With the formulas:

Considering 2 points (x1, y1), (x2, y2)
rectangle = (x1, y1, width, height) //width = x2-x1 height = y2-y1
circle =(x1, y1, diameter) //diameter = 2*(x2-x1 + y2-y1)
line = (x1, y1, x2, y2)

This works on the coordinate system. Let’s try and understand it, as it will be necessary for part 2!

From the above explanation, we can see to actually draw any shape we need 2 points and their coordinates. These coordinates will be calculated with the help of Eventlisteners() which in react are passed as parameters in the <canvas> element. Namely, onMouseDown onMouseMove onMouseUp

✔️Part 2: Implementing ShapeTool(Line)

We are going to draw three shapes. The above output is what we have done till now. Let’s proceed and write a code to draw a line first, with the help of a mouse pointer. Edit the file you are currently working in as follows:

Let us try and understand the code in parts:

  1. States: elements is defined as an empty array that will store the coordinates of the line. drawing is defined to recognize the position of the mouse-click event.
  2. StartDrawing(): Sets the state of drawing as true. clientX clientY are coordinates from the event parameter and are pushed into the array elements with the help of the spread operator. These are the first coordinates ie, (x1,y1).
  3. FinishDrawing(): Sets the state of drawing as false.
  4. Draw(): Checks the state of drawing and continues if it’s true. Here we are trying to find the new coordinates (x2, y2) which we store in updatedEle .Which are finally pushed into the array using the spread operator.
  5. CreateElement(): Returns the calculated coordinates based on the position of the cursor along with the element to be drawn. roughEle is the line element we want to draw on the screen.

To draw different shapes. Introduce one more state as : const [elementType, setElementType] = useState(‘line’); and the check whether the elementType === ”line” or elementType === ”circle” orelementType === ”rectangle” and modify the code as per the given formulas above in Part 1. Radio buttons can be used to toggle between these options.

🖥️Result:

✔️Part 3: implementing PencilTool

This is a different part of the application which will deal with freehand drawing. We won’t need roughjs and only use thecanvas API.

Change line 9 in App.js to <PencilTool /> .Now

We are saving the coordinates (x1,y1) in an array points that useLayoutEffect() renders.

OnMouseDown the event we are storing the position of initial coordinate in a global variable called pos which is stored the array as sound as the onMouseMove event is triggered. During this event, the position also changes till the onMouseup event comes across.

CanvasRenderingContext2D properties such as lineCap strokeStyleandlineWidthhelp make this stroke visible.

Finally during rendering in the react hook we use ctx.lineTo() method to draw the points on the canvas and ctx.stroke() method to give it a freehand-stroke style.

🖥️Result:

✔️Part 4: Switching between Penciltool and ShapeTool

This can be a bit tricky since we want to render both shapeTool and pencilTool on the same canvas. I recommend you to read the above logic thoroughly. We are making major changes to our code.

i) App.js

Most changes are made here since we combine both logic in a single file, on a single canvas.

What’s happening? Lets look at it one by one.

We check if the tooltype is a pencil or something else, ie. a line. We draw on the canvas as well as store the path made by the cursor in a new array called points[] .Similarly, we create a new element for the desired shape and store it in an array elements[] .

We create another state action which comes in handy when creating many tools.

*Actions can be categorized as:

drawing → shapes like Circle, Rectangle, Triangle, etc.

sketching → Pencil/brush,

resizing → Resizing shapes

Again comparisons are based on actions and elements are updated accordingly. We are using a quadraticCurve which is an inbuilt functionality to create smoother curves.

UpdateElement makes sure that the last coordinates(x2,y2) stored are the ones at which the cursor “just stops moving”

Our last job is to store the end coordinates for both the shape and the stroke so that we can render the final element on the canvas board. A new array path[[], []] stores the current points and we set points[] to [] .

  • useEffect :

useEffect’s dependency arrays are both elements[] and path[] which makes sure there is a dynamic change visible on the canvas board.

ii) swatch.js

Swatch is a separate component that contains all the tools. It can be considered as the ToolBar of this application. To make things easier we only consider only two toolTypes, namely, Pencil and Line.

Note:- We use a bootstrap grid system in our project.

🖥️End Result:

Congratulations! We have a working canvas whiteboard that works for both shapes and pencil!

📖Final Takeaways:

Personal Project(PencilPal)

I still haven't discussed adding colors, more shapes, changing width, and other features. The canvas board has limitless features and can render 3d objects too. Many WebGL libraries also make use of the same. This was a basic project to get familiar with concepts of React, React Hooks, and Canvas.

The future scope of this application would be a powerful virtual whiteboard that can be used during the hiring process in technical interviews.

🔗Links:

📚 Github Link of whiteboard-app: https://github.com/bansalsurya/whiteboard-app

✏️ Final Project Link: https://bansalsurya.github.io/pencilpal/

👋 Connect with me on LinkedIn: https://www.linkedin.com/in/surya-bansal/

Thank You!

If you liked the article please share, react and comment!

--

--

Surya Bansal

Engineering @Walmart. Open-source Contributor. Programmer. Tech fanatic.