Creating a functioning signature widget: problems and solutions

The initial task

Two weeks ago there was a hackathon at Ekreative. The task for my team was to make a system for automatically generating work report documents for use between a contractor and their client. When finished, the system should be able to print a document with the signature included on both sides.

Choosing a plugin

While choosing which plugin to use for including a signature electronically we tried several different options (jSignature, Signature Pad, jQuery UI Signature), but eventually we decided to go with this Signature Pad plugin. The key feature of this plugin is that it is sensitive to the speed of the stroke, which determines how thick the line produced will be. In this article I’ll describe some problems encountered while using this canvas.

Initialization:

var canvas = document.createElement('canvas');
$(canvas)
 .width(500)
 .height(200);
$(canvas).appendTo(signatureWidget.find('.signature-canvas'));
var signaturePad = new SignaturePad(canvas, {
 penColor: "rgb(48, 70, 243)"
});

1. On different devices pixel ratios differ (http://mydevice.io/devices/)
If we try to use our canvas for signatures on a retina screen we find that the stroke speed no longer corresponds to the speed at which lines are created.
The solution? – scale

function resizeCanvas() {
 var ratio = Math.max(window.devicePixelRatio || 1, 1);
 $(".signature-canvas canvas").each(function () {
 this.width = this.offsetWidth * ratio;
 this.height = this.offsetHeight * ratio;
 this.getContext("2d").scale(ratio, ratio);
 });
}

2.Signatures can now be written on any device but the resulting pictures have different sizes. For example if the size of the canvas-object is 500px x 200px , and the signature is created on iPad 3, we resize it to 1000px x 400px. When the signature is complete it will be sized 1000px x 400px.

The problem then is to return a ready for use signature into the widget such that while editing we’ll at least be see it.

Let’s suppose that we are editing it on a PC with a 1920*1080 display where the pixel ratio = 1, in this case the picture simply doesn’t fit in our canvas object.

The solution that I found, is that instead of returning the picture to the widget, it could be set as the background.

Finally when editing a specific signature, it is unacceptable to paint anything.

 var objcanvas = $(obj).find('.signature-canvas');
 var c = document.createElement("canvas");
 var ratio = Math.max(window.devicePixelRatio || 1, 1);
 c.width = img.width;
 c.height = img.height;
 var ctx = c.getContext("2d");
 ctx.globalAlpha = 0.5;
 ctx.drawImage(img,0,0);
 ctx.scale(ratio, ratio);
 $(objcanvas).attr('style', 'background: url('+c.toDataURL()+'); background-repeat: no-repeat; background-size: cover;');

In this case the signature becomes translucent. That’s why we put it through a new transparency canvas by a factor of 0.5. If we don’t do that, the following should be enough:

$(objcanvas).attr('style', 'background: url('+base64+'); background-repeat: no-repeat; background-size: cover;');

3. It is necessary to point out that the background isn’t put directly on the canvas, but on its parent element (if we put it on the canvas, the object grows to the size of the picture. We observed this bug in Safari).

4. Even with base64 in the src, the picture doesn’t render instantly and even downloading base64 requires time, and we can’t define the pictures size straight away. That’s why we should take the steps described above only after the pictures have been downloaded.

The code :

function addBackground(obj, base) {
 var objcanvas = $(obj).find('.signature-canvas');
 
var ib = new Image();
 ib.src = base;
ib.addEventListener('load', function () {
 var c = document.createElement("canvas");
 var ratio = Math.max(window.devicePixelRatio || 1, 1);
 c.width = this.width;
 c.height = this.height;
 var ctx = c.getContext("2d");
 ctx.globalAlpha = 0.5;
 ctx.drawImage(this,0,0);
 ctx.scale(ratio, ratio);
 $(objcanvas).attr('style', 'background: url('+c.toDataURL()+'); background-repeat: no-repeat; background-size: cover;');
 })
}

The result:

blogsignaturess

The full code is here.