JavaScript Signature Capture Panel
March 25, 2014 Leave a comment
I am attempting to teach myself some more JavaScript. To that end I decided to replicate some of the projects I did in WPF/C# in HTML5/JavaScript. One of the 1st ‘hello world’ projects I did in WPF was creating a signature panel – so it seemed like a good place to start. The original blog post is here. The original WPF project took advantage of the InkCanvas class. Below is a code snippet of the how the events were captured in the original project:
- private void inkSignature_MouseDown(object sender, MouseButtonEventArgs e)
- {
- IsCapturing = true;
- glyph = new Glyph();
- }
- private void inkSignature_MouseUp(object sender, MouseButtonEventArgs e)
- {
- IsCapturing = false;
- _signature.Glyphs.Add(glyph);
- startPoint = new Point();
- endPoint = new Point();
- }
- private void inkSignature_MouseMove(object sender, MouseEventArgs e)
- {
- if (IsCapturing)
- {
- if (startPoint.X == 0 && startPoint.Y == 0 && endPoint.X == 0 && endPoint.Y == 0)
- {
- endPoint = new Point(e.GetPosition(this).X, e.GetPosition(this).Y);
- }
- else
- {
- startPoint = endPoint;
- endPoint = new Point(e.GetPosition(this).X, e.GetPosition(this).Y);
- Line line = new Line(startPoint, endPoint);
- glyph.Lines.Add(line);
- }
- }
- }
To have the same effect in the browser, I swapped out the InkCanvas with the Canvas tag.
- <canvas id="myCanvas" width="578" height="200" style="border:solid"></canvas>
- <br />
- <button id="resultButton" onclick="showSignature()"></button>
I then stubbed out the ‘mousedown’, ‘mouseup’, and ‘mousemove’ events to see if I was hooked up to them correctly and they were firing as expected:
- <body>
- <script>
- canvas.addEventListener('mousemove', function (event) {
- }, false);
- canvas.addEventListener('mousedown', function (event) {
- alert("mousedown");
- }, false);
- canvas.addEventListener('mouseup', function (event) {
- alert("mouseup");
- }, false);
- </script>
- </body>
I then thought about how to implement the InkCanvas code in JavaScript so I added some variables that all of the examples of StackOverflow use:
- var canvas = document.getElementById('myCanvas');
- var context = canvas.getContext('2d');
I then needed the function to calculate the mouse position relative to the signature panel (versus the screen). This was also pretty common on StackOverflow:
- function getMousePosition(canvas, event) {
- var rectangle = canvas.getBoundingClientRect();
- return {
- x: event.clientX – rectangle.left,
- y: event.clientY – rectangle.top
- };
- };
Finally, I could implement the WPF-equivalent logic. First was the variables to maintain state:
- var isCapturing = false;
- var startX = 0;
- var startY = 0;
- var endX = 0;
- var endY = 0;
- var signature = [];
- var glyph = [];
And then the 3 event handlers:
- canvas.addEventListener('mousemove', function (event) {
- if (isCapturing) {
- var mousePosition = getMousePosition(canvas, event);
- if (startX === 0 && startY === 0 && endX === 0 && endY === 0) {
- endX = mousePosition.x;
- endY = mousePosition.y;
- }
- else {
- startX = endX;
- startY = endY;
- endX = mousePosition.x;
- endY = mousePosition.y;
- context.beginPath();
- context.moveTo(startX, startY);
- context.lineTo(endX, endY);
- context.stroke()
- glyph.push(startX, startY, endX, endY);
- }
- }
- }, false);
- canvas.addEventListener('mousedown', function (event) {
- isCapturing = true;
- glyph = [];
- }, false);
- canvas.addEventListener('mouseup', function (event) {
- isCapturing = false;
- signature.push(glyph);
- var startX = 0;
- var startY = 0;
- var endX = 0;
- var endY = 0;
- }, false);
When I ran it, I <almost> got it right:
The problem is that the mouseup event was not resetting the starting value of the next point to 0, so the signature was coming out as 1 long line. After sleeping on it (my pattern is write bugs at night, fix them in the morning), I realized I just had to reset the start and end coordinates on mouseup and then inspect in the mousemove. Here is the complete final code:
- <!DOCTYPE html>
- <html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <title></title>
- </head>
- <body>
- <canvas id="myCanvas" width="578" height="200" style="border:solid"></canvas>
- <br />
- <button id="resultButton" onclick="showSignature()"></button>
- <script>
- function showSignature() {
- alert(signature.length);
- };
- </script>
- <script>
- var canvas = document.getElementById('myCanvas');
- var context = canvas.getContext('2d');
- var isCapturing = false;
- var startX = 0;
- var startY = 0;
- var endX = 0;
- var endY = 0;
- var signature = [];
- var glyph = [];
- function getMousePosition(canvas, event) {
- var rectangle = canvas.getBoundingClientRect();
- return {
- x: event.clientX – rectangle.left,
- y: event.clientY – rectangle.top
- };
- };
- canvas.addEventListener('mousemove', function (event) {
- if (isCapturing) {
- var mousePosition = getMousePosition(canvas, event);
- if (endX === 0 && endY === 0) {
- endX = mousePosition.x;
- endY = mousePosition.y;
- }
- else {
- startX = endX;
- startY = endY;
- endX = mousePosition.x;
- endY = mousePosition.y;
- context.beginPath();
- context.moveTo(startX, startY);
- context.lineTo(endX, endY);
- context.stroke()
- glyph.push(startX, startY, endX, endY);
- }
- }
- }, false);
- canvas.addEventListener('mousedown', function (event) {
- isCapturing = true;
- glyph = [];
- var mousePosition = getMousePosition(canvas, event);
- var startX = mousePosition.x;
- var startY = mousePosition.y;
- }, false);
- canvas.addEventListener('mouseup', function (event) {
- isCapturing = false;
- signature.push(glyph);
- startX = 0;
- startY = 0;
- endX = 0;
- endY = 0;
- }, false);
- </script>
- </body>
- </html>
And here it is in action:
Now all I have to do is put the points into the same data structures that I used in the WPF project: Signature –> Glyphs[] –> Lines[] –> Line.StartPoint && Line.EndPoint.