signature.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. const wrapper = document.getElementById("signature-pad");
  2. const clearButton = wrapper.querySelector("[data-action=clear]");
  3. //const changeBackgroundColorButton = wrapper.querySelector("[data-action=change-background-color]");
  4. //const changeColorButton = wrapper.querySelector("[data-action=change-color]");
  5. //const changeWidthButton = wrapper.querySelector("[data-action=change-width]");
  6. const undoButton = wrapper.querySelector("[data-action=undo]");
  7. const savePNGButton = wrapper.querySelector("[data-action=save-png]");
  8. //const saveJPGButton = wrapper.querySelector("[data-action=save-jpg]");
  9. //const saveSVGButton = wrapper.querySelector("[data-action=save-svg]");
  10. //const saveSVGWithBackgroundButton = wrapper.querySelector("[data-action=save-svg-with-background]");
  11. const canvas = wrapper.querySelector("canvas");
  12. const signaturePad = new SignaturePad(canvas, {
  13. // It's Necessary to use an opaque color when saving image as JPEG;
  14. // this option can be omitted if only saving as PNG or SVG
  15. backgroundColor: 'rgb(255, 255, 255)'
  16. });
  17. // Adjust canvas coordinate space taking into account pixel ratio,
  18. // to make it look crisp on mobile devices.
  19. // This also causes canvas to be cleared.
  20. function resizeCanvas() {
  21. // When zoomed out to less than 100%, for some very strange reason,
  22. // some browsers report devicePixelRatio as less than 1
  23. // and only part of the canvas is cleared then.
  24. const ratio = Math.max(window.devicePixelRatio || 1, 1);
  25. // This part causes the canvas to be cleared
  26. canvas.width = canvas.offsetWidth * ratio;
  27. canvas.height = canvas.offsetHeight * ratio;
  28. canvas.getContext("2d").scale(ratio, ratio);
  29. // This library does not listen for canvas changes, so after the canvas is automatically
  30. // cleared by the browser, SignaturePad#isEmpty might still return false, even though the
  31. // canvas looks empty, because the internal data of this library wasn't cleared. To make sure
  32. // that the state of this library is consistent with visual state of the canvas, you
  33. // have to clear it manually.
  34. //signaturePad.clear();
  35. // If you want to keep the drawing on resize instead of clearing it you can reset the data.
  36. signaturePad.fromData(signaturePad.toData());
  37. }
  38. // On mobile devices it might make more sense to listen to orientation change,
  39. // rather than window resize events.
  40. window.onresize = resizeCanvas;
  41. resizeCanvas();
  42. function download(dataURL, filename) {
  43. const blob = dataURLToBlob(dataURL);
  44. const url = window.URL.createObjectURL(blob);
  45. const a = document.createElement("a");
  46. a.style = "display: none";
  47. a.href = url;
  48. a.download = filename;
  49. document.body.appendChild(a);
  50. a.click();
  51. window.URL.revokeObjectURL(url);
  52. }
  53. // One could simply use Canvas#toBlob method instead, but it's just to show
  54. // that it can be done using result of SignaturePad#toDataURL.
  55. function dataURLToBlob(dataURL) {
  56. // Code taken from https://github.com/ebidel/filer.js
  57. const parts = dataURL.split(';base64,');
  58. const contentType = parts[0].split(":")[1];
  59. const raw = window.atob(parts[1]);
  60. const rawLength = raw.length;
  61. const uInt8Array = new Uint8Array(rawLength);
  62. for (let i = 0; i < rawLength; ++i) {
  63. uInt8Array[i] = raw.charCodeAt(i);
  64. }
  65. return new Blob([uInt8Array], { type: contentType });
  66. }
  67. clearButton.addEventListener("click", () => {
  68. signaturePad.clear();
  69. });
  70. undoButton.addEventListener("click", () => {
  71. const data = signaturePad.toData();
  72. if (data) {
  73. data.pop(); // remove the last dot or line
  74. signaturePad.fromData(data);
  75. }
  76. });
  77. /*
  78. changeBackgroundColorButton.addEventListener("click", () => {
  79. const r = Math.round(Math.random() * 255);
  80. const g = Math.round(Math.random() * 255);
  81. const b = Math.round(Math.random() * 255);
  82. const color = "rgb(" + r + "," + g + "," + b +")";
  83. signaturePad.backgroundColor = color;
  84. const data = signaturePad.toData();
  85. signaturePad.clear();
  86. signaturePad.fromData(data);
  87. });
  88. changeColorButton.addEventListener("click", () => {
  89. const r = Math.round(Math.random() * 255);
  90. const g = Math.round(Math.random() * 255);
  91. const b = Math.round(Math.random() * 255);
  92. const color = "rgb(" + r + "," + g + "," + b +")";
  93. signaturePad.penColor = color;
  94. });
  95. changeWidthButton.addEventListener("click", () => {
  96. const min = Math.round(Math.random() * 100) / 10;
  97. const max = Math.round(Math.random() * 100) / 10;
  98. signaturePad.minWidth = Math.min(min, max);
  99. signaturePad.maxWidth = Math.max(min, max);
  100. });
  101. */
  102. savePNGButton.addEventListener("click", () => {
  103. if (signaturePad.isEmpty()) {
  104. alert("Please provide a signature first.");
  105. } else {
  106. const dataURL = signaturePad.toDataURL();
  107. download(dataURL, "signature.png");
  108. }
  109. });
  110. /*
  111. saveJPGButton.addEventListener("click", () => {
  112. if (signaturePad.isEmpty()) {
  113. alert("Please provide a signature first.");
  114. } else {
  115. const dataURL = signaturePad.toDataURL("image/jpeg");
  116. download(dataURL, "signature.jpg");
  117. }
  118. });
  119. saveSVGButton.addEventListener("click", () => {
  120. if (signaturePad.isEmpty()) {
  121. alert("Please provide a signature first.");
  122. } else {
  123. const dataURL = signaturePad.toDataURL('image/svg+xml');
  124. download(dataURL, "signature.svg");
  125. }
  126. });
  127. saveSVGWithBackgroundButton.addEventListener("click", () => {
  128. if (signaturePad.isEmpty()) {
  129. alert("Please provide a signature first.");
  130. } else {
  131. const dataURL = signaturePad.toDataURL('image/svg+xml', {includeBackgroundColor: true});
  132. download(dataURL, "signature.svg");
  133. }
  134. });
  135. */