Draw over image on canvas
by katalin_2003
HTML
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/signature_pad.umd.min.js"></script>
<div id="signature-pad" class="signature-pad">
<div class="signature-pad--body">
<canvas>
Update your browser to support the canvas element!
</canvas>
</div>
<div class="signature-pad--footer">
<div class="description">Sign above</div>
<div class="signature-pad--actions">
<form action="#" enctype="multipart/form-data">
<label for="fileupload" id="buttonlabel">
<span role="button" aria-controls="filename" tabindex="0">
Choose a background image
</span>
</label>
<input type="file" id="fileupload" accept="image/*">
</form>
<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>
CSS
*,
*::before,
*::after {
box-sizing: border-box;
}
body {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
height: 100vh;
width: 100%;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
margin: 0;
padding: 32px 16px;
background-color: #b3b3b3;
font-family: Helvetica, Sans-Serif;
}
.signature-pad {
position: relative;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
font-size: 10px;
width: 100%;
height: 100%;
max-width: 700px;
max-height: 460px;
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;
padding: 16px;
}
.signature-pad::before,
.signature-pad::after {
position: absolute;
z-index: -1;
content: "";
width: 40%;
height: 10px;
bottom: 10px;
background: transparent;
box-shadow: 0 8px 12px rgba(0, 0, 0, 0.4);
}
.signature-pad::before {
left: 20px;
-webkit-transform: skew(-3deg) rotate(-3deg);
transform: skew(-3deg) rotate(-3deg);
}
.signature-pad::after {
right: 20px;
-webkit-transform: skew(3deg) rotate(3deg);
transform: skew(3deg) rotate(3deg);
}
.signature-pad--body {
position: relative;
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
border: 1px solid #f4f4f4;
}
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;
}
.signature-pad--footer {
color: #C3C3C3;
text-align: center;
font-size: 1.2em;
margin-top: 8px;
}
.signature-pad--actions {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack:...
JavaScript
const wrapper = document.getElementById("signature-pad");
const clearButton = wrapper.querySelector("[data-action=clear]");
const changeColorButton = wrapper.querySelector("[data-action=change-color]");
const undoButton = wrapper.querySelector("[data-action=undo]");
const savePNGButton = wrapper.querySelector("[data-action=save-png]");
const saveJPGButton = wrapper.querySelector("[data-action=save-jpg]");
const saveSVGButton = wrapper.querySelector("[data-action=save-svg]");
const canvas = wrapper.querySelector("canvas");
const fileSelector = document.getElementById('fileupload');
// https://medium.com/the-everyday-developer/detect-file-mime-type-using-magic-numbers-and-javascript-16bc513d4e1e
const verifyAndSetPictureAsBackground = (event) => {
const file = event.target.files[0];
const fReader = new FileReader();
fReader.onloadend = (e) => {
if (e.target.readyState === FileReader.DONE) {
const uint = new Uint8Array(e.target.result);
let bytes = [];
uint.forEach((byte) => bytes.push(byte.toString(16)));
const hex = bytes.join('').toUpperCase();
if (!(getMimeType(hex) === 'image/png' || getMimeType(hex) === 'image/gif' || getMimeType(hex) === 'image/jpeg')) {
alert('Please choose a picture(.png, .gif, or .jpeg)');
// https://stackoverflow.com/a/35323290/1904223
file = null;
fileSelector.value = '';
if (!/safari/i.test(navigator.userAgent)) {
fileSelector.type = '';
fileSelector.type = 'file';
}
}
if (file) {
const dataURL = window.URL.createObjectURL(file);
signaturePad.fromDataURL(dataURL);
console.log(signaturePad);
}
}
}
fReader.readAsArrayBuffer(file.slice(0, 4));
}
const getMimeType = (signature) => {
switch (signature) {
case '89504E47':
return 'image/png'
case '47494638':
return 'image/gif'
case 'FFD8FFDB':
case 'FFD8FFE0':
...