Home » Jquery » javascript – Canvas Signature on Signature Pad turns out fully black when downloading as JPG, PNG

javascript – Canvas Signature on Signature Pad turns out fully black when downloading as JPG, PNG

Posted by: admin February 22, 2020 Leave a comment

Questions:

I am using Signature pad js from szimek for making canvas signature pad. I have used 3 canvas at a time. After some research I got a link from Codepen for using multiple instance. Its working though, but the problem arise when the signature needs to be downloaded. After download the PNG and JPG, the images get fully black. Sharing the codes below.

var wrapper1 = document.getElementById("signature-pad-1"),
    canvas1 = wrapper1.querySelector("canvas"),
    signaturePad1;

var wrapper2 = document.getElementById("signature-pad-2"),
    canvas2 = wrapper2.querySelector("canvas"),
    signaturePad2;

function resizeCanvas(canvas) {
    var ratio =  window.devicePixelRatio || 1;
    canvas.width = canvas.offsetWidth * ratio;
    canvas.height = canvas.offsetHeight * ratio;
    canvas.getContext("2d").scale(ratio, ratio);
}

function clear1() { signaturePad1.clear(); }
function clear2() { signaturePad2.clear(); }

function save() {
    if (signaturePad1.isEmpty() || signaturePad2.isEmpty())
        alert("Error: Please sign both pads!");
    else
        alert("Success!");
}


resizeCanvas(canvas1);
signaturePad1 = new SignaturePad(canvas1);

resizeCanvas(canvas2);
signaturePad2 = new SignaturePad(canvas2);

$("#clear1").click(clear1);
$("#clear2").click(clear2);
$("#save").click(save);


var savePNGButton1 = wrapper1.querySelector("[data-action=save-png]");
var saveJPGButton1 = wrapper1.querySelector("[data-action=save-jpg]");
var saveSVGButton1 = wrapper1.querySelector("[data-action=save-svg]");

var savePNGButton2 = wrapper2.querySelector("[data-action=save-png]");
var saveJPGButton2 = wrapper2.querySelector("[data-action=save-jpg]");
var saveSVGButton2 = wrapper2.querySelector("[data-action=save-svg]");


// One could simply use Canvas#toBlob method instead, but it's just to show
// that it can be done using result of SignaturePad#toDataURL.
function dataURLToBlob(dataURL) {
  // Code taken from https://github.com/ebidel/filer.js
  var parts = dataURL.split(';base64,');
  var contentType = parts[0].split(":")[1];
  var raw = window.atob(parts[1]);
  var rawLength = raw.length;
  var uInt8Array = new Uint8Array(rawLength);

  for (var i = 0; i < rawLength; ++i) {
    uInt8Array[i] = raw.charCodeAt(i);
  }

  return new Blob([uInt8Array], { type: contentType });
}

function download(dataURL, filename) {
  if (navigator.userAgent.indexOf("Safari") > -1 && navigator.userAgent.indexOf("Chrome") === -1) {
    window.open(dataURL);
  } else {
    var blob = dataURLToBlob(dataURL);
    var url = window.URL.createObjectURL(blob);

    var a = document.createElement("a");
    a.style = "display: none";
    a.href = url;
    a.download = filename;

    document.body.appendChild(a);
    a.click();

    window.URL.revokeObjectURL(url);
  }
}

savePNGButton1.addEventListener("click", function (event) {
  if (signaturePad1.isEmpty()) {
    alert("Please provide a signature first.");
  } else {
    var dataURL = signaturePad1.toDataURL();
    download(dataURL, "signature.png");
  }
});

saveJPGButton1.addEventListener("click", function (event) {
  if (signaturePad1.isEmpty()) {
    alert("Please provide a signature first.");
  } else {
    var dataURL = signaturePad1.toDataURL("image/jpeg");
    download(dataURL, "signature.jpg");
  }
});

saveSVGButton1.addEventListener("click", function (event) {
  if (signaturePad1.isEmpty()) {
    alert("Please provide a signature first.");
  } else {
    var dataURL = signaturePad1.toDataURL('image/svg+xml');
    download(dataURL, "signature.svg");
  }
});

savePNGButton2.addEventListener("click", function (event) {
  if (signaturePad2.isEmpty()) {
    alert("Please provide a signature first.");
  } else {
    var dataURL = signaturePad2.toDataURL();
    download(dataURL, "signature.png");
  }
});

saveJPGButton2.addEventListener("click", function (event) {
  if (signaturePad2.isEmpty()) {
    alert("Please provide a signature first.");
  } else {
    var dataURL = signaturePad2.toDataURL("image/jpeg");
    download(dataURL, "signature.jpg");
  }
});

saveSVGButton2.addEventListener("click", function (event) {
  if (signaturePad2.isEmpty()) {
    alert("Please provide a signature first.");
  } else {
    var dataURL = signaturePad2.toDataURL('image/svg+xml');
    download(dataURL, "signature.svg");
  }
});
	body {
  font-family: Helvetica, Sans-Serif;

  -moz-user-select: none;
  -webkit-user-select: none;
  -ms-user-select: none;
}

.m-signature-pad {
  position: relative;
  font-size: 10px;
  width: 400px;
  height: 400px;
  border: 1px solid #e8e8e8;
  background-color: #fff;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.27), 0 0 40px rgba(0, 0, 0, 0.08) inset;
  border-radius: 4px;
}

.m-signature-pad:before, .m-signature-pad:after {
	position: absolute;
    z-index: -1;
    content: "";
	width: 40%;
	height: 10px;
	left: 20px;
	bottom: 10px;
	background: transparent;
	-webkit-transform: skew(-3deg) rotate(-3deg);
	-moz-transform: skew(-3deg) rotate(-3deg);
	-ms-transform: skew(-3deg) rotate(-3deg);
	-o-transform: skew(-3deg) rotate(-3deg);
	transform: skew(-3deg) rotate(-3deg);
	box-shadow: 0 8px 12px rgba(0, 0, 0, 0.4);
}

.m-signature-pad:after {
	left: auto;
	right: 20px;
	-webkit-transform: skew(3deg) rotate(3deg);
	-moz-transform: skew(3deg) rotate(3deg);
	-ms-transform: skew(3deg) rotate(3deg);
	-o-transform: skew(3deg) rotate(3deg);
	transform: skew(3deg) rotate(3deg);
}

.m-signature-pad--body {
  position: absolute;
  left: 20px;
  right: 20px;
  top: 20px;
  bottom: 20px;
  border: 1px solid #f4f4f4;
}

.m-signature-pad--body canvas {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    border-radius: 4px;
    box-shadow: 0 0 5px rgba(0, 0, 0, 0.02) inset;
	background-color: #fff;
  }
  
  .btn-grp{display:block; width:100%;}
  .signature-pad--footer{position:absolute; bottom:0; left:0; right:0;}

@media screen and (max-width: 1024px) {
  .m-signature-pad {
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    width: 50%;
    height: auto;
    min-width: 100px;
    min-height: 100px;
    margin: 5%;
  }
  #github {
    display: none;
  }
}

@media screen and (min-device-width: 768px) and (max-device-width: 1024px) {
  .m-signature-pad {
    margin: 10%;
  }
}

@media screen and (max-height: 320px) {
  .m-signature-pad--body {
    left: 0;
    right: 0;
    top: 0;
    bottom: 32px;
  }
  .m-signature-pad--footer {
    left: 20px;
    right: 20px;
    bottom: 4px;
    height: 28px;
  }
  .m-signature-pad--footer
    .description {
      font-size: 1em;
      margin-top: 1em;
    }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/signature_pad/1.3.2/signature_pad.min.js"></script>

<div id="signature-pad-1" class="m-signature-pad">
  <div class="m-signature-pad--body">
    <canvas></canvas>
  </div>
  <div class="signature-pad--footer">
      <div class="signature-pad--actions">
        <div>
          <button type="button" class="button clear" data-action="clear">Clear</button>
          <button type="button" class="button" data-action="change-color">Change color</button>
          <button type="button" class="button" data-action="undo">Undo</button>

        </div>
        <div>
          <button type="button" class="button save" data-action="save-png">Save as PNG</button>
          <button type="button" class="button save" data-action="save-jpg">Save as JPG</button>
          <button type="button" class="button save" data-action="save-svg">Save as SVG</button>
        </div>
      </div>
    </div>
</div>

<button id="clear1" onclick="clear(1);">Clear</button>


<div id="signature-pad-2" class="m-signature-pad">
  <div class="m-signature-pad--body">
    <canvas></canvas>
  </div>
  <div class="signature-pad--footer">
      <div class="signature-pad--actions">
        <div>
          <button type="button" class="button clear" data-action="clear">Clear</button>
          <button type="button" class="button" data-action="change-color">Change color</button>
          <button type="button" class="button" data-action="undo">Undo</button>

        </div>
        <div>
          <button type="button" class="button save" data-action="save-png">Save as PNG</button>
          <button type="button" class="button save" data-action="save-jpg">Save as JPG</button>
          <button type="button" class="button save" data-action="save-svg">Save as SVG</button>
        </div>
      </div>
    </div>
</div>
<button id="clear2" onclick="clear(2);">Clear</button>
<br />
<button id="save">Save</button>
How to&Answer:

The problem is that you’re not setting the background colour, so the JPG is defaulting to black, which means that the whole image is black, because it can’t handle transparency.

It’s not a problem for PNG or SVG, which is why these work as expected.

To fix it set the background colour of the signature pad to white (taken from the signature pad example):

var signaturePad = new SignaturePad(canvas, {
  // It's Necessary to use an opaque color when saving image as JPEG;
  // this option can be omitted if only saving as PNG or SVG
  backgroundColor: 'rgb(255, 255, 255)'
});

To apply the above to your code:

signaturePad1 = new SignaturePad(canvas1, {backgroundColor: 'rgb(255, 255, 255)'});