In this article, I am going to explain how to design a drawing board or paint brush using Canvas in HTML5.
Introduction
After introduction of Canvas element in HTML 5, it has become easy to draw lines, shapes, images and perform animations in the browser without taking help of any third party component or plug-ins such as Flash, Silverlight etc. If you want to know more about Canvas, read
this article by Sourav or
this article from Mallesh.
Objective
The objective of this article is to explain how to create Paint brush type of application or a Drawing board using Canvas element of HTML 5 that works in the browser without any dependency on third party application or plug-ins. Our final product would look almost similar to below image.
Creating panel for Selected commands and Tool selection
This panel of our drawing board app shows selected color, selected line width of the brush. Also it provides option to choose the tool for the drawing.
The code to design this panel is below.
<div class="table">
<div class="tr0">
<div class="td spacerH" style="width:100px;">Selected color: <div class="clrPicker" id="divSelectedColor" style="background-color:black;"></div></div>
<div class="td" style="width:130px;">Selected Line width: <div class="lineWidthC"><div class="lineWidth" id="divLineWidth" style="height:5px;"></div></div></div>
<div class="td">
Select Drawing tool : <br />
<input type="radio" name="dTool" id="dToolP" value="Pencil" checked="checked" /> <label for="dToolP"><img src="/images/pencil.gif" />Pencil</label>
<input type="radio" name="dTool" id="dToolL" value="Line" /> <label for="dToolL"> <img src="/images/line.gif" />Line</label>
<input type="radio" name="dTool" id="dToolR" value="Rectangle" /> <label for="dToolR"> <img src="/images/rectangle.gif" />Rectangle</label>
<input type="radio" name="dTool" id="dToolC" value="Circle" /> <label for="dToolC"> <img src="/images/Circle.gif" />Circle</label>
</div>
</div>
</div>
Creating the drawing panel and right side panel
Drawing panel for our app allows to draw a shape, art and right panel allows
- to choose or set color
- select an Eraser to erase drawing
- select or set line width
The code to create our drawing panel, color, eraser and line width panel is below.
<div id="board" style="width: 930px;">
<canvas id="kfCanvas" width="800px" height="500px;" style="border: 3px dotted #000;cursor:crosshair;">Sorry, your browser doesn't support canvas technology.
</canvas>
<div style="float: right;">
<div>
Color picker:
<div class="clrPicker" style="background-color:black;" onclick="SetBrushColor('black')"></div>
<div class="clrPicker" style="background-color:red;" onclick="SetBrushColor('red')"></div>
<div class="clrPicker" style="background-color:blue;" onclick="SetBrushColor('blue')"></div>
<div class="clrPicker" style="background-color:green;" onclick="SetBrushColor('green')"></div>
<div class="clrPicker" style="background-color:orange;" onclick="SetBrushColor('orange')"></div>
<div class="clrPicker" style="background-color:yellow;" onclick="SetBrushColor('yellow')"></div>
<div>Set color<br />
<input type="text" title="Write color code and click button to set new brush color" name="clrText" id="clrText" style="width:50px;" placeholder="Color code/name" />
<input type="button" value="Set" onclick="SetBrushColor('Text')" />
</div>
<div class="clrPicker" onclick="SetBrushColor('white')" style="width:45px;height:25px;padding-top:3px;"> Eraser</div>
</div>
<div>
Line width:
<div class="lineWidthC" onclick="SetLineWidth('5')"><div class="lineWidth" style="height:5px;"></div></div>
<div class="lineWidthC" onclick="SetLineWidth('4')"><div class="lineWidth" style="height:4px;"></div></div>
<div class="lineWidthC" onclick="SetLineWidth('2')"><div class="lineWidth" style="height:2px;"></div></div>
<div>Set width<br />
<input type="text" title="Write line width and click button to set new line width" name="widthText" id="widthText" style="width:50px;" placeholder="Width in pixel" />
<input type="button" value="Set" onclick="SetLineWidth('0')" />
</div>
</div>
</div>
</div>
In the above code snippet, we have 6 color boxes with different different color, clicking those boxes calls
SetBrushColor
function. We also have a textbox and a Set button that is used to set custom color for the drawing.
The eraser box simply calls the same SetBrushColor
method with 'white' parameter.
It also has three line width boxes, clicking those calls SetLineWidth
function that sets the line width. In case we want to define the line width ourselves we can use the textbox and the set button.
To create a complete UI like the first image above, simply merge the first and second code snippets one after another.
CSS class used in this app
We are also using some .css classes in our code snippets and here are those.
<style type="text/css">
.clrPicker
{
width: 30px;
height: 30px;
border: 1px solid #808080;
margin-bottom: 5px;
cursor:pointer;
}
.lineWidth
{
background-color:black;
margin-bottom:5px;
margin-top:15px;
}
.lineWidthC
{
width:30px;
height:30px;
border:1px dotted #808080;
margin-bottom:5px;
overflow:hidden;
cursor:pointer;
}
</style>
Above code simply create the UI without any functionalities as Canvas element has no properties or behaviours or commands to draw any kind of shapes or drawing. It always needs JavaScript to instruct what to do.
JavaScript code to make the Paint brush or Drawing board app live
Some global variables being used in this page are below.
var curColor = 'black'; //$('#selectColor option:selected').val(); // set color here
var lineWidth = 5;
var context;
var startX, startY;
var canvasX, canvasY;
var width, height;
var toolSelected;
curColor
is the default color selectedlineWidth
is the default line width selectedcontext
is the Canvas contextstartX
is the starting point from left on the canvas (when mouse is clicked and drawing starts)startY
is the starting point from top on the canvas (when mouse is clicked and drawing starts)canvasX
is the ending point from left on the canvas (when mouse is clicked and drawing ends)canvasY
is the ending point from top on the canvas (when mouse is clicked and drawing ends)width
is the difference between canvasX
and startX
height
is the difference between canvasY
and startY
toolSelected
is the variable that holds the tool selected by the user from top - left of the drawing board
Now lets see the heart of the JavaScript code for this drawing board that makes this drawing board live.
var kfCanvas = document.getElementById("kfCanvas"); // jQuery doesn't work as .getContext throw error
if (kfCanvas) {
var isDown = false;
context = kfCanvas.getContext("2d");
context.lineWidth = lineWidth; // set line width here
DrawAWhiteBase(); // Draw a white base on the canvas
$(kfCanvas).mousedown(function (e) {
isDown = true;
startX = e.pageX - kfCanvas.offsetLeft;
startY = e.pageY - kfCanvas.offsetTop;
toolSelected = $("input[type='radio'][name='dTool']:checked");
if (toolSelected.length > 0) {
toolSelected = toolSelected.val();
// pencil
if (toolSelected == 'Pencil') {
context.lineWidth = lineWidth;
context.strokeStyle = curColor;
context.lineCap = 'round';
context.beginPath();
context.moveTo(startX, startY)
}
}
}).mousemove(function (e) {
if (isDown != false) {
canvasX = e.pageX - kfCanvas.offsetLeft;
canvasY = e.pageY - kfCanvas.offsetTop;
width = Math.abs(canvasX - startX);
height = Math.abs(canvasY - startY);
var beginrad = startX;
var endrad = canvasX;
var radius = endrad - beginrad; //to calculate circle radius
var toolSelected = $("input[type='radio'][name='dTool']:checked");
if (toolSelected.length > 0) {
toolSelected = toolSelected.val();
// pencil
if (toolSelected == 'Pencil') {
context.lineTo(canvasX, canvasY);
context.stroke();
}
// line
else if (toolSelected == 'Line') {
DrawLine(startX, startY, canvasX, canvasY);
}
// rectangle
else if (toolSelected == 'Rectangle') {
DrawRect(startX, startY, width, height);
}
else if (toolSelected == 'Circle') {
DrawCircle(startX, startY, radius);
}
}
}
}).mouseup(function (e) {
isDown = false;
context.closePath();
});
}
First we are holding the canvas element in the
kfCanvas
variable and if it exists, we are entering into the condition block. Then setting the
context
of the canvas that is used to draw shapes or an art, then
lineWidth
of the canvas to the
lineWidth
by default set in the global variable.
To make the Canvas background white, we are calling DrawAWhiteBase()
function (we will see it later on). We are doing it purposefully as saving this canvas as .png creates transparent background.
Now we are attaching mousedown
, mousemove
and mouseup
event on the canvas element.
mousedown event
This sets the isDown
flag to true, that let us know that user has pressed the mouse. Then it calculates the startX
and startY
value (mouse position from left and top where the mouse was clicked on the canvas).
It also sets the tools selected variable to know which tool is selected by the user currently.
If user has selected Pencil then it sets the lineWidth
, strokeStyle
, lineCap
properties of the context and moving the cursor to the clicked position.
mousemove event
This checks if mouse is clicked and user is keep holding the mouse while moving the mouse and sets following
canvasX
the current position of the mouse being dragged from leftcanvasY
the current position of the mouser being dragged from top
calculates the width
and height
of the selection.
In case user has selected Circle tool, we need to know the radius too so we are also calculating that. Now based on tool selected we are performing some action or calling respecting functions
If tool selected is
- Pencil - we are drawing the line
- Line - calling
DrawLine
function by passing starting position of the mouse and ending position of the mouse - Rectangle - calling
DrawRect
function by passing starting position of the mouse, width and height - Circle - calling
DrawCircle
by passing starting position of the mouse and radius
mouseup event
In this event, we are setting the isDown
flag and calling closePath
function of the canvas to indicate that the drawing is complete.
Now, lets see each of the javascript functions we have used in the HTML UI code
DrawAWhileBase()
It is being called when page loads.
function DrawAWhiteBase() {
context.beginPath();
context.fillStyle = "white";
context.fillRect(0, 0, kfCanvas.width, kfCanvas.height);
context.closePath();
}
This function fills the entire width and height of the canvas with the white color, this comes as a base of the drawing board for us.
DrawLine()
This function is called when Line tool is selected and user drags the mouse on the canvas.
function DrawLine(fromX, fromY, toX, toY) {
context.lineWidth = lineWidth;
context.strokeStyle = curColor;
context.beginPath();
context.moveTo(fromX, fromY); //moveTo starts the line (x,y coords of the initial point)
context.lineTo(toX, toY); //lineTo ends the line (x,y coords of the final point)
context.stroke();
}
This function receives starting and ending position of the mouse drag draws a line.
DrawRect()
This function is called when Rectangle tool is selected and user drags the mouse on the canvas.
function DrawRect(mouseX, mouseY, width, height) {
context.beginPath();
context.rect(mouseX, mouseY, width, height);
context.fillStyle = curColor;
context.closePath();
context.fill();
}
This function takes the starting position of the mouse draw, width and height and draws a rectangle.
DrawCircle()
This function is called when Circle tool is selected and user drags the mouse on the canvas.
function DrawCircle(x, y, r) {
context.beginPath();
context.fillStyle = curColor;
context.arc(x, y, r, 0, Math.PI * 2, true);
context.closePath();
context.fill();
}
This function takes starting position of the mouse drag and radius and draw a circle.
SetBrushColor()
This function is called by onclick event of the color boxes at the right side or color set button at the right panel. It also gets called when Erase box is clicked.
function SetBrushColor(c) {
if (c == 'Text') {
c = $("#clrText").val();
}
curColor = c;
$("#divSelectedColor").css('background-color', c);
}
This function performs more than one activities
- It checks whether this function is being called on click of the Set button, in that case it takes the value of the Color set TextBox and set to the c variable.
- Then it sets the value of
curColor
global variables that is being used through out the page for several drawing functions. - Changes the top panel selected color box to the selected color.
SetLineWidth()
This function is called when Line width boxes are clicked or Set button of the Line width button is clicked from the right side panel.
function SetLineWidth(w) {
if (w == '0') {
w = $("#widthText").val();
}
lineWidth = w;
context.lineWidth = w; // set line width here
$("#divLineWidth").css('height', w + 'px');
}
This function also performs more than one activities
- If this function is being called on click of the line width Set button then it gets the textbox value and set to
w
. - Then sets the lineWidth global variable so that all other drawing functions also use the same line width
- setting the context
lineWidth
- setting the selected line width into selected line width box at the top panel
Saving the canvas as Image on the server
You must have noticed in the 1st image at the top we have a Save Drawing button at the top - left, that basically helps us to save the drawing done on the canvas to the server.
I have already explained this in another article of mine, please
click here to read that article.
Thanks so much for reading this, do vote and share your comment or feedback about this article. If you liked this, please do share to the social websites, your friends and colleagues.
You can see the live demo of this drawing board, click below link
Live Demo (I am keep working on this project so the live demo might be more advanced than what is described in this article)
UPDATED:
To achieve Undo and Redo functionalities in the Drawing board
To achieve Undo and Redo functionality in this Drawing board, keep historySave
function inside .mouseup
event of the canvas.
.mouseup(function (e) {
isDown = false;
context.closePath();
historySave();
});
In the below code we are first declaring an array where we would keep the canvas snapshot till the last activity performed by the user.
// START - Undo Redo functionality
var undoRedo = new Array();
var unStep = -1;
function historySave() {
unStep++;
while (undoRedo.length > 20) {
undoRedo.shift();
unStep--;
}
if (unStep !== 0 && unStep < undoRedo.length) {
undoRedo.length = unStep;
unStep++;
} else {
undoRedo.length = unStep;
}
undoRedo.push(document.getElementById('kfCanvas').toDataURL());
}
function Undo() {
if (unStep > -1) {
unStep--;
var canvasPic = new Image();
canvasPic.src = undoRedo[unStep];
canvasPic.onload = function () { context.drawImage(canvasPic, 0, 0); }
}
}
function Redo() {
if (unStep < undoRedo.length - 1) {
unStep++;
var canvasPic = new Image();
canvasPic.src = undoRedo[unStep];
canvasPic.onload = function () { context.drawImage(canvasPic, 0, 0); }
}
}
// END - Undo Redo functionality
In this case historySave
function is keeping track of 20 canvas snapshots so that user will be able to go 20 steps backwards and forwards.
historySave()This function simply pushes the current snapshot of the image to the array, remember that we are calling this function in
.mouseup
event so whenever an activity is complete, this function is called.
Undo()
This function go 1 step backward and get the last canvas snapshot and draw on the canvas.
Redo()
This function go 1 step forward if exists and get the canvas snapshot and raw on the canvas.
Reference
The idea of creating a drawing board using Canvas element and the base code was taken from http://www.onlywebpro.com/demo/gamedev/canvas_drawing_board.html. Thanks to onlywebpro for providing the strating point.