Sign here please

Occasionally you might need the user to add a signature to a document. There are several services for this, but you can do it just as easy with HTML5 canvas and a bit of Javascript. Once the signature is on the screen, you can then save it using Javascript and a WebMethod in your code behind. As an example of this, have a look at this page on our website. Here's how your signature app might look like:

Create the drawing area

With HTML5 we have the canvas object. It is basically a blank sheet where the user can draw upon. Simple add a <canvas></canvas> in your HTML and that's it. Well, actually, it's not that simple. You will need a bit of Javascript as well. As you can see in the example, there are a few things you need to do.

First you need to get the canvas itself and its context. You can use this for example by ID to get the canvas and then take the context from there.

const canvas = document.querySelector('#signature');
const context = canvas.getContext('2d');

Now you can style the canvas object with CSS, but setting the width and height through CSS to make it bigger will lead to a pixelated drawing. So you will have to set the width and height through the canvas attributes using Javascript.

canvas.height = '250';
canvas.width = '500';

You can use CSS to set the background color as well. And this works just fine on the screen. However, if you want to save the canvas as a PNG file, the CSS is ignored and you will have an all black image. Therefor you need to specify the color using the canvas fillStyle. And then fill the canvas with that color using the canvas fillRect. 

context.fillStyle = "#EEEEEE";
context.fillRect(0, 0, canvas.width, canvas.height);

Do the drawing

From the moment the user clicks on the drawing area and moves the mouse, until the moment releases the mouse the drawing is taking place. So for this we will use the mousedown, mousemove and mouseup events. Now we don't want to record every mousemove event. Only after the mousedown and until the mouseup event. So to distinguish here, we need a variable:

let drawing = false;

Next we can add some event listerers for the mousedown, mousemove and mouseup events from the canvas:

canvas.addEventListener("mousedown", start);
canvas.addEventListener("mouseup", stop);
canvas.addEventListener("mousemove", sign);

Now when the user starts drawing by clicking the mouse on the canvas, we can set our variable to true. The sign() function is also called. The reason for that is that a dot (like on the i) should be drawn as well, even if there is no mousemove event.

function start(e) {
  drawing = true;
  sign(e);
}

When the user stops drawing, we can set the variable to false. We also start a new path for drawing here. If we would not do that, the next line will automatically connect to the end point of the first line, but we want a separate line after the user lifts the mouse from the canvas.

function stop() {
  drawing = false;
  context.beginPath();
}

When the mouse moves but the variable is set to false, we simple do nothing. When it is set to true we specify the lineWidth in pixels. And the lineCap is set to round. If not, it would automatically make straight lines. After that we use the e.clientX and e.clientY to get the mouse position. Now since the mouse position is relative to the whole page and not just the canvas, the line will not follow the cursor exactly. So by using the offSetLeft and offSetTop of the canvas we can adjust for that and make it a smooth animation.

function sign(e) {
  if (!drawing) return;
  context.lineWidth = 7;
  context.lineCap = "round";
  context.lineTo(e.clientX - canvas.offsetLeft, e.clientY - canvas.offsetTop);
  context.stroke();
  context.beginPath();
  context.moveTo(e.clientX - canvas.offsetLeft, e.clientY - canvas.offsetTop);
}

Saving the canvas as a PNG

Since the canvas is on the client side, you cannot directly use a submit action to save it as a PNG. So for that I created a WebMethod in the code behind and then used Javascript to do a post to that webservice. You can look in the source code from the example to see how it is done. Since you cannot see the code behind, here's how that looks like.

Let's wrap it up

That's it for this post. Check out the demo for this on our website. Feel free to leave a comment or send a question to andre@silveressence.net. Let me know if you got inspired and may the source be with you!