Click here to Skip to main content
15,880,543 members
Articles / Web Development / HTML

Building a Signature Control Using Canvas

Rate me:
Please Sign up or sign in to vote.
4.97/5 (11 votes)
2 Aug 2012CPOL5 min read 107.5K   5.6K   28   20
The article explains how to write a signature control using the canvas element and mouse events.

Introduction

Lately, I was developing a Windows 8 Metro app POC (Proof Of Concept) for a customer. One of the requirements in the POC was to capture customer’s signature which are written using touch events. In this article, I’ll show you how to write such a signature control using the HTML5 canvas element and mouse events. The same functionality can also be used in touch platforms using minor changes.

Canvas Element Basics

The canvas element is a drawing surface which can be placed inside a web page. It is part of the HTML5 specifications and is implemented in most of the major web browsers. The canvas exposes a set of JavaScript APIs that enables the developer to draw pixel based graphics. In order to use a canvas, you first need to create it inside your web page. Here is how you declare a canvas element:

<canvas id="myCanvas" width="300px" height="300px"></canvas>

Now that you have a canvas on the web page, you can use its JavaScript API to draw graphics. In order to draw, you will have to acquire a canvas context. Here is a code sample that shows how to get the context out of a canvas element:

var canvas = document.getElementById("signatureCanvas"),
    ctx = canvas.getContext("2d");   

In the code, two variables are declared – canvas and ctx. You get the canvas element (mostly by using its id property) and then use the getContext function to retrieve the drawing context. The getContext function gets a context id parameter that can have the 2d value or experimental-webgl value if webgl is supported in the browser. If you like to read more about creating 3d graphics using webgl, you can start from here

After you grab the drawing context, you can start drawing using canvas APIs. There are a lot of functions such as fillRect (to draw a rectangle with a fill color) and clearRect (to clear a rectangle area in the canvas). Since this article deals mostly with creating a control which wrap a canvas, I encourage you to read more about the canvas in the following articles before you continue:

Creating the Control Elements on The Fly

Now that you know a little bit about the canvas, lets start developing the signature control. At first, you will want to create the HTML look and feel. In order to do that, you can use the document.createElement function to create elements on the fly and build the representation of the control. Here is the code I used in the suggested solution:

function createControlElements() {            
    var signatureArea = document.createElement("div"),
        labelDiv = document.createElement("div"),
        canvasDiv = document.createElement("div"),
        canvasElement = document.createElement("canvas"),
        buttonsContainer = document.createElement("div"),
        buttonClear = document.createElement("button"),
        buttonAccept = document.createElement("button");
 
    labelDiv.className = "signatureLabel";
    labelDiv.textContent = label;
 
    canvasElement.id = "signatureCanvas";
    canvasElement.clientWidth = cWidth;
    canvasElement.clientHeight = cHeight;
    canvasElement.style.border = "solid 2px black";
 
    buttonClear.id = "btnClear";
    buttonClear.textContent = "Clear";
 
    buttonAccept.id = "btnAccept";
    buttonAccept.textContent = "Accept";
 
    canvasDiv.appendChild(canvasElement);
    buttonsContainer.appendChild(buttonClear);
    buttonsContainer.appendChild(buttonAccept);
 
    signatureArea.className = "signatureArea";
    signatureArea.appendChild(labelDiv);
    signatureArea.appendChild(canvasDiv);
    signatureArea.appendChild(buttonsContainer);
 
    document.getElementById(containerId).appendChild(signatureArea);
}

As you can see, I create some in-memory elements and then set some attributes on them. After that, I append the created elements to each other to create the HTML fragment and wire the fragment to a container element with containerId.

Implementing Drawing in The Canvas

Now that you have the elements in hand, the next task will be to implement the drawing in the canvas. In order to do that, you will need to add mouse event listeners to the canvas. The most appropriate events are mousedown and mouseup. Here is the code to wire the events:

canvas.addEventListener("mousedown", pointerDown, false);
canvas.addEventListener("mouseup", pointerUp, false);

and here is the code of pointerDown, pointerUp and paint functions:

function pointerDown(evt) {
    ctx.beginPath();
    ctx.moveTo(evt.offsetX, evt.offsetY);
    canvas.addEventListener("mousemove", paint, false);
}
 
function pointerUp(evt) {
    canvas.removeEventListener("mousemove", paint);
    paint(evt);
}
 
function paint(evt) {
    ctx.lineTo(evt.offsetX, evt.offsetY);
    ctx.stroke();
}

In the pointerDown function, you use the beginPath function to start a drawing path. Then, the context is moved to the point that the mouse point using the event’s offsetX and offsetY properties. After that, you wire an event listener to the mousemove event. In the paint function that is invoked while the mouse is being moved, you move the context to the new point and then use the stroke function to draw the line between the previous point and the current point. When the mouse button is released, the pointerUp function is called. In the pointerUp function, you draw the last line to the end point and remove the event listener to the mousemove event listener. Removing the mousemove event listener will prevent the continuation of the drawing when you hover on the canvas element.

Getting the Signature Image Data as Byte Array

Once the signature is drawn on the canvas, you will probably want to extract it. This can be done using the context’s getImageData which returns the data drawn in the canvas. The return type of the function call has a data property which holds a byte array representing the canvas’ pixels. The following function can help to retrieve the signature:

function getSignatureImage() {
    return ctx.getImageData(0, 0, canvas.width, canvas.height).data;
}

The Whole Control Implementation

Lets wrap all the previous functions into a JavaScript control. Here is the control’s implementation:

(function (ns) {
    "use strict";
 
    ns.SignatureControl = function (options) {
        var containerId = options && options.canvasId || "container",
            callback = options && options.callback || {},
            label = options && options.label || "Signature",
            cWidth = options && options.width || "300px",
            cHeight = options && options.height || "300px",
            btnClearId,
            btnAcceptId,
            canvas,
            ctx;
 
        function initCotnrol() {
            createControlElements();
            wireButtonEvents();
            canvas = document.getElementById("signatureCanvas");
            canvas.addEventListener("mousedown", pointerDown, false);
            canvas.addEventListener("mouseup", pointerUp, false);
            ctx = canvas.getContext("2d");            
        }
 
        function createControlElements() {            
            var signatureArea = document.createElement("div"),
                labelDiv = document.createElement("div"),
                canvasDiv = document.createElement("div"),
                canvasElement = document.createElement("canvas"),
                buttonsContainer = document.createElement("div"),
                buttonClear = document.createElement("button"),
                buttonAccept = document.createElement("button");
 
            labelDiv.className = "signatureLabel";
            labelDiv.textContent = label;
 
            canvasElement.id = "signatureCanvas";
            canvasElement.clientWidth = cWidth;
            canvasElement.clientHeight = cHeight;
            canvasElement.style.border = "solid 2px black";
 
            buttonClear.id = "btnClear";
            buttonClear.textContent = "Clear";
 
            buttonAccept.id = "btnAccept";
            buttonAccept.textContent = "Accept";
 
            canvasDiv.appendChild(canvasElement);
            buttonsContainer.appendChild(buttonClear);
            buttonsContainer.appendChild(buttonAccept);
 
            signatureArea.className = "signatureArea";
            signatureArea.appendChild(labelDiv);
            signatureArea.appendChild(canvasDiv);
            signatureArea.appendChild(buttonsContainer);
 
            document.getElementById(containerId).appendChild(signatureArea);
        }
 
        function pointerDown(evt) {
            ctx.beginPath();
            ctx.moveTo(evt.offsetX, evt.offsetY);
            canvas.addEventListener("mousemove", paint, false);
        }
 
        function pointerUp(evt) {
            canvas.removeEventListener("mousemove", paint);
            paint(evt);
        }
 
        function paint(evt) {
            ctx.lineTo(evt.offsetX, evt.offsetY);
            ctx.stroke();
        }
 
        function wireButtonEvents() {
            var btnClear = document.getElementById("btnClear"),
                btnAccept = document.getElementById("btnAccept");
            btnClear.addEventListener("click", function () {
                ctx.clearRect(0, 0, canvas.width, canvas.height);
            }, false);
            btnAccept.addEventListener("click", function () {
                callback();
            }, false);
        }
 
        function getSignatureImage() {
            return ctx.getImageData(0, 0, canvas.width, canvas.height).data;
        }
 
        return {
            init: initCotnrol,
            getSignatureImage: getSignatureImage
        };
    }
})(this.ns = this.ns || {});

First, you create a scope for the control using a JavaScript namespace. In the namespace, you declare a constructor function for the SignatureControl. The control can get a list of options which can help to configure it’s appearance and behavior. For example, the callback option is called when you click on the accept button. The control will expose two functions – init and getSignatureImage. The init function will be responsible to initialize all the elements, to wire events listeners to the control’s buttons and to wire the event listeners to the mouse events. The getSignatureImage function will be responsible to retrieve the signature byte array.

Using the Control in a HTML Page

After you have the control, lets see how to use it inside a web page. The following web page shows how to use the control:

<!doctype html>
<html>
<head>
    <title>Signature</title>
    <link href="signature.css" rel="stylesheet" type="text/css" />
    <script type="text/javascript" src="signature.js"></script>  
    <script type="text/javascript">
        function loaded() {
            var signature = new ns.SignatureControl({ containerId: 'container', callback: function () {
                    alert('hello');
                } 
            });
            signature.init();
        }
 
        window.addEventListener('DOMContentLoaded', loaded, false);
    </script>  
</head>
<body>
    <div id="container">        
    </div>
</body>
</html>

When the DOM content finish loading, you create a signature object using its constructor function and some options. Then, all you have to do is call the init function to create the control and enable its functionality. If you want to retrieve the signature you can use the following code:

var signatureByteArray = signature.getSignatureImage();

Here is a screenshot of the control in action:

The Signature Control

Summary

This article showed you how to create a control to capture signatures. In order to use the same functionality with Windows 8 touch events, all you have to do is to replace the call for mouse events with their corresponding touch events (for example mouseup will turn into MSPointerUp). 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Lead sparXys
Israel Israel
Gil Fink is a web development expert and ASP.Net/IIS Microsoft MVP. He is the founder and owner of sparXys. He is currently consulting for various enterprises and companies, where he helps to develop Web and RIA-based solutions. He conducts lectures and workshops for individuals and enterprises who want to specialize in infrastructure and web development. He is also co-author of several Microsoft Official Courses (MOCs) and training kits, co-author of "Pro Single Page Application Development" book (Apress) and the founder of Front-End.IL Meetup. You can read his publications at his website: http://www.gilfink.net

Comments and Discussions

 
QuestionIs there a way to support touch screens? Pin
Jeff Ellis7-May-21 10:40
Jeff Ellis7-May-21 10:40 
QuestionError Msg - Please help Pin
ILOVETOFU22-Dec-19 23:22
ILOVETOFU22-Dec-19 23:22 
QuestionSignature Control Pin
Member 1321550723-May-17 2:05
Member 1321550723-May-17 2:05 
QuestionI face an issue Pin
Member 819533427-Sep-16 9:17
Member 819533427-Sep-16 9:17 
AnswerRe: I face an issue Pin
Member 1318526213-May-17 13:15
Member 1318526213-May-17 13:15 
QuestionAwesome !!! Pin
Member 1258986717-Jun-16 5:02
Member 1258986717-Jun-16 5:02 
SuggestionSmooth curves Pin
Member 120451938-Oct-15 21:20
Member 120451938-Oct-15 21:20 
QuestionHow to call the javascript in a web form in ASP.Net Pin
Member 1151984312-May-15 8:49
Member 1151984312-May-15 8:49 
QuestionPlease advise on how to save to sql database Pin
Uriel Tinashe Patsanza9-Jun-14 21:46
Uriel Tinashe Patsanza9-Jun-14 21:46 
AnswerRe: Please advise on how to save to sql database Pin
Gil Fink12-Aug-14 1:08
Gil Fink12-Aug-14 1:08 
GeneralRe: Please advise on how to save to sql database Pin
kkakadiya2-Feb-18 1:56
kkakadiya2-Feb-18 1:56 
Questionreading byte array Pin
raghum1126-Mar-13 0:16
raghum1126-Mar-13 0:16 
AnswerRe: reading byte array Pin
Gil Fink30-Mar-13 0:45
Gil Fink30-Mar-13 0:45 
QuestionThe asp .net application created from this code is not working in Ipad Safari Pin
rahulkasar8-Jan-13 5:38
rahulkasar8-Jan-13 5:38 
Hi,

I have created an asp .net web application from this code and tried to access the url from an ipad its didnt worked . Can you please help me in this. Thanks a lot

Thanks,
AnswerRe: The asp .net application created from this code is not working in Ipad Safari Pin
Marco Peruzzi16-Feb-13 5:01
Marco Peruzzi16-Feb-13 5:01 
AnswerRe: The asp .net application created from this code is not working in Ipad Safari Pin
Miller Nguyen2-Nov-14 14:33
Miller Nguyen2-Nov-14 14:33 
GeneralRe: The asp .net application created from this code is not working in Ipad Safari Pin
Pallavi.varade24-Nov-14 22:22
Pallavi.varade24-Nov-14 22:22 
QuestionNice control!! Pin
Akshay Srinivasan215-Aug-12 11:19
Akshay Srinivasan215-Aug-12 11:19 
GeneralMy vote of 5 Pin
Sasha Goldshtein1-Aug-12 4:40
Sasha Goldshtein1-Aug-12 4:40 
GeneralRe: My vote of 5 Pin
Gil Fink1-Aug-12 19:43
Gil Fink1-Aug-12 19:43 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.