Making a Whiteboard Application with ReactJs
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.
✔️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.json
file 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:
- 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. - StartDrawing(): Sets the state of
drawing
as true.clientX clientY
are coordinates from theevent
parameter and are pushed into the arrayelements
with the help of the spread operator. These are the first coordinates ie, (x1,y1). - FinishDrawing(): Sets the state of
drawing
as false. - 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 inupdatedEle
.Which are finally pushed into the array using the spread operator. - 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 strokeStyle
andlineWidth
help 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.
- handleMouseDown() :
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
- handleMouseMove() :
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”
- handleMouseUp() :
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:
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!