JavaScript Signature Capture Panel

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:

  1. private void inkSignature_MouseDown(object sender, MouseButtonEventArgs e)
  2. {
  3.     IsCapturing = true;
  4.     glyph = new Glyph();
  5.  
  6. }
  7.  
  8. private void inkSignature_MouseUp(object sender, MouseButtonEventArgs e)
  9. {
  10.     IsCapturing = false;
  11.     _signature.Glyphs.Add(glyph);
  12.     startPoint = new Point();
  13.     endPoint = new Point();
  14.  
  15. }
  16.  
  17. private void inkSignature_MouseMove(object sender, MouseEventArgs e)
  18. {
  19.     if (IsCapturing)
  20.     {
  21.         if (startPoint.X == 0 && startPoint.Y == 0 && endPoint.X == 0 && endPoint.Y == 0)
  22.         {
  23.             endPoint = new Point(e.GetPosition(this).X, e.GetPosition(this).Y);
  24.         }
  25.         else
  26.         {
  27.             startPoint = endPoint;
  28.             endPoint = new Point(e.GetPosition(this).X, e.GetPosition(this).Y);
  29.             Line line = new Line(startPoint, endPoint);
  30.             glyph.Lines.Add(line);
  31.         }
  32.  
  33.     }
  34.  
  35. }

To have the same effect in the browser, I swapped out the InkCanvas with the Canvas tag.

  1. <canvas id="myCanvas" width="578" height="200" style="border:solid"></canvas>
  2. <br />
  3. <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:

  1. <body>
  2.  
  3.     <script>
  4.         canvas.addEventListener('mousemove', function (event) {
  5.         }, false);
  6.  
  7.         canvas.addEventListener('mousedown', function (event) {
  8.             alert("mousedown");
  9.         }, false);
  10.  
  11.         canvas.addEventListener('mouseup', function (event) {
  12.             alert("mouseup");
  13.         }, false);
  14.     </script>
  15.  
  16. </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:

  1. var canvas = document.getElementById('myCanvas');
  2. 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:

  1. function getMousePosition(canvas, event) {
  2.     var rectangle = canvas.getBoundingClientRect();
  3.     return {
  4.         x: event.clientX – rectangle.left,
  5.         y: event.clientY – rectangle.top
  6.     };
  7. };

Finally, I could implement the WPF-equivalent logic.  First was the variables to maintain state:

  1. var isCapturing = false;
  2. var startX = 0;
  3. var startY = 0;
  4. var endX = 0;
  5. var endY = 0;
  6. var signature = [];
  7. var glyph = [];

And then the 3 event handlers:

  1. canvas.addEventListener('mousemove', function (event) {
  2.     if (isCapturing) {
  3.         var mousePosition = getMousePosition(canvas, event);
  4.  
  5.         if (startX === 0 && startY === 0 && endX === 0 && endY === 0) {
  6.             endX = mousePosition.x;
  7.             endY = mousePosition.y;
  8.         }
  9.         else {
  10.             startX = endX;
  11.             startY = endY;
  12.             endX = mousePosition.x;
  13.             endY = mousePosition.y;
  14.  
  15.             context.beginPath();
  16.             context.moveTo(startX, startY);
  17.             context.lineTo(endX, endY);
  18.             context.stroke()
  19.  
  20.             glyph.push(startX, startY, endX, endY);
  21.         }
  22.     }
  23. }, false);
  24.  
  25. canvas.addEventListener('mousedown', function (event) {
  26.     isCapturing = true;
  27.     glyph = [];
  28. }, false);
  29.  
  30. canvas.addEventListener('mouseup', function (event) {
  31.     isCapturing = false;
  32.     signature.push(glyph);
  33.     var startX = 0;
  34.     var startY = 0;
  35.     var endX = 0;
  36.     var endY = 0;
  37. }, false);

When I ran it, I <almost> got it right:

image

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:

  1. <!DOCTYPE html>
  2. <html xmlns="http://www.w3.org/1999/xhtml"&gt;
  3. <head>
  4.     <title></title>
  5. </head>
  6. <body>
  7.     <canvas id="myCanvas" width="578" height="200" style="border:solid"></canvas>
  8.     <br />
  9.     <button id="resultButton" onclick="showSignature()"></button>
  10.  
  11.  
  12.     <script>
  13.         function showSignature() {
  14.             alert(signature.length);
  15.         };
  16.     </script>
  17.  
  18.     <script>
  19.         var canvas = document.getElementById('myCanvas');
  20.         var context = canvas.getContext('2d');
  21.         var isCapturing = false;
  22.         var startX = 0;
  23.         var startY = 0;
  24.         var endX = 0;
  25.         var endY = 0;
  26.         var signature = [];
  27.         var glyph = [];
  28.  
  29.         function getMousePosition(canvas, event) {
  30.             var rectangle = canvas.getBoundingClientRect();
  31.             return {
  32.                 x: event.clientX – rectangle.left,
  33.                 y: event.clientY – rectangle.top
  34.             };
  35.         };
  36.  
  37.         canvas.addEventListener('mousemove', function (event) {
  38.             if (isCapturing) {
  39.                 var mousePosition = getMousePosition(canvas, event);
  40.  
  41.                 if (endX === 0 && endY === 0) {
  42.                     endX = mousePosition.x;
  43.                     endY = mousePosition.y;
  44.                 }
  45.                 else {
  46.                     startX = endX;
  47.                     startY = endY;
  48.                     endX = mousePosition.x;
  49.                     endY = mousePosition.y;
  50.  
  51.                     context.beginPath();
  52.                     context.moveTo(startX, startY);
  53.                     context.lineTo(endX, endY);
  54.                     context.stroke()
  55.  
  56.                     glyph.push(startX, startY, endX, endY);
  57.                 }
  58.             }
  59.         }, false);
  60.  
  61.         canvas.addEventListener('mousedown', function (event) {
  62.             isCapturing = true;
  63.             glyph = [];
  64.  
  65.             var mousePosition = getMousePosition(canvas, event);
  66.             var startX = mousePosition.x;
  67.             var startY = mousePosition.y;
  68.         }, false);
  69.  
  70.         canvas.addEventListener('mouseup', function (event) {
  71.             isCapturing = false;
  72.             signature.push(glyph);
  73.  
  74.             startX = 0;
  75.             startY = 0;
  76.             endX = 0;
  77.             endY = 0;
  78.         }, false);
  79.     </script>
  80.  
  81. </body>
  82. </html>

And here it is in action:

image

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.