Skip to content

Instantly share code, notes, and snippets.

@michitheonlyone
Last active June 18, 2024 03:10
Show Gist options
  • Save michitheonlyone/c1a92d4169e674da5b5edfedac2325cc to your computer and use it in GitHub Desktop.
Save michitheonlyone/c1a92d4169e674da5b5edfedac2325cc to your computer and use it in GitHub Desktop.
Signature Pad for Symfony Forms (it can be used in any website to create digital signatures with a pen on touchscreen
{#
Signature Pad for Symfony Forms
© 2024 by Michael Ihde wemida.com
https://github.com/wemida
Variables to use with include:
{% include with ... %} => see below example
{{ sig_title ?? 'E-Signature' }}
{{ sig_info_text ?? 'Sign in the canvas below and save your signature as an image!' }}
{{ sig_canvas_id ?? 'sig-canvas' }}
{{ sig_save_btn_id ?? 'sig-submitBtn' }}
{{ sig_save_btn_name ?? 'Save signature' }}
{{ sig_clear_btn_id ?? 'sig-clearBtn' }}
{{ sig_clear_btn_name ?? 'Clear signature' }}
{{ sig_data_output_textarea_id ?? 'sig-dataUrl' }}
{{ sig_data_output_textarea_name ?? 'sig-dataUrl' }}
{{ sig_image_output ?? 'sig-image' }}
{% include 'create_customer_report/_signaturepad.html.twig' with {
'sig_title': 'Signatur',
'sig_info_text': '',
'sig_canvas_id': 'sig-canvas-reporter',
'sig_save_btn_id': 'sig-btn-save-reporter',
'sig_save_btn_name': 'Signatur speichern',
'sig_clear_btn_id': 'sig-btn-clear-reporter',
'sig_clear_btn_name': 'Signatur löschen',
'sig_data_output_textarea_id': 'sig-data-reporter',
'sig_data_output_textarea_name': 'sig-data-reporter',
'sig_image_output': 'sig-image-reporter'
'form_widget_function': form.reporterSignature
} %}
#}
<style>
#{{ sig_canvas_id ?? 'sig-canvas' }} {
border: 2px dotted #CCCCCC;
border-radius: 15px;
cursor: crosshair;
}
</style>
<!-- Content -->
<div class="container">
<div class="row">
<div class="col-md-12">
<h1>{{ sig_title ?? 'E-Signature' }}</h1>
<p>{{ sig_info_text ?? 'Sign in the canvas below and save your signature as an image!' }}</p>
</div>
</div>
<div class="row">
<div class="col-md-12">
{# <canvas id="sig-canvas" width="620" height="160">#}
<canvas id="{{ sig_canvas_id ?? 'sig-canvas' }}" width="620" height="160">
Get a better browser, bro!
</canvas>
</div>
</div>
<div class="row">
<div class="col-md-12">
{# These buttons must be any tag except button to prevent formsubmitting! #}
<div class="btn btn-info" id="{{ sig_save_btn_id ?? 'sig-submitBtn' }}">{{ sig_save_btn_name ?? 'Save signature' }}</div>
<div class="btn btn-warning" id="{{ sig_clear_btn_id ?? 'sig-clearBtn' }}">{{ sig_clear_btn_name ?? 'Clear signature' }}</div>
</div>
</div>
<br/>
<div class="row">
{# This textfield is only for demo and can be hidden to submit the signature via form #}
<div class="col-md-12">
{# TODO convert into custom formfield to prevent this hassle!! #}
{{ form_widget(form_widget_function) }}
{# {{ form_widget(form.customerSignature) }}#}
{# <input type="hidden" id="{{ sig_data_output_textarea_id ?? 'sig-dataUrl' }}" name="{{ sig_data_output_textarea_name ?? 'sig-dataUrl' }}" />#}
</div>
</div>
<br/>
<div class="row">
<div class="col-md-12">
<img id="{{ sig_image_output ?? 'sig-image' }}" src="{{ form_widget_function.vars.value ?? '' }}" alt="Your signature will go here!"/>
</div>
</div>
</div>
<script>
(function() {
window.requestAnimFrame = (function(callback) {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimaitonFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
var canvas = document.getElementById("{{ sig_canvas_id ?? 'sig-canvas' }}");
var ctx = canvas.getContext("2d");
ctx.strokeStyle = "#222222";
ctx.lineWidth = 4;
var drawing = false;
var mousePos = {
x: 0,
y: 0
};
var lastPos = mousePos;
canvas.addEventListener("mousedown", function(e) {
drawing = true;
lastPos = getMousePos(canvas, e);
}, false);
canvas.addEventListener("mouseup", function(e) {
drawing = false;
}, false);
canvas.addEventListener("mousemove", function(e) {
mousePos = getMousePos(canvas, e);
}, false);
// Add touch event support for mobile
canvas.addEventListener("touchstart", function(e) {
}, false);
canvas.addEventListener("touchmove", function(e) {
var touch = e.touches[0];
var me = new MouseEvent("mousemove", {
clientX: touch.clientX,
clientY: touch.clientY
});
canvas.dispatchEvent(me);
}, false);
canvas.addEventListener("touchstart", function(e) {
mousePos = getTouchPos(canvas, e);
var touch = e.touches[0];
var me = new MouseEvent("mousedown", {
clientX: touch.clientX,
clientY: touch.clientY
});
canvas.dispatchEvent(me);
}, false);
canvas.addEventListener("touchend", function(e) {
var me = new MouseEvent("mouseup", {});
canvas.dispatchEvent(me);
}, false);
function getMousePos(canvasDom, mouseEvent) {
var rect = canvasDom.getBoundingClientRect();
return {
x: mouseEvent.clientX - rect.left,
y: mouseEvent.clientY - rect.top
}
}
function getTouchPos(canvasDom, touchEvent) {
var rect = canvasDom.getBoundingClientRect();
return {
x: touchEvent.touches[0].clientX - rect.left,
y: touchEvent.touches[0].clientY - rect.top
}
}
function renderCanvas() {
if (drawing) {
ctx.moveTo(lastPos.x, lastPos.y);
ctx.lineTo(mousePos.x, mousePos.y);
ctx.stroke();
lastPos = mousePos;
}
}
// Prevent scrolling when touching the canvas
document.body.addEventListener("touchstart", function(e) {
if (e.target == canvas) {
e.preventDefault();
}
}, false);
document.body.addEventListener("touchend", function(e) {
if (e.target == canvas) {
e.preventDefault();
}
}, false);
document.body.addEventListener("touchmove", function(e) {
if (e.target == canvas) {
e.preventDefault();
}
}, false);
(function drawLoop() {
requestAnimFrame(drawLoop);
renderCanvas();
})();
function clearCanvas() {
// must be dumb to reset the canvas
canvas.width = canvas.width;
var ctx = canvas.getContext("2d");
ctx.strokeStyle = "#222222";
ctx.lineWidth = 4;
}
// Set up the UI
var sigText = document.getElementById("{{ sig_data_output_textarea_id ?? 'sig-dataUrl' }}");
var sigImage = document.getElementById("{{ sig_image_output ?? 'sig-image' }}");
var clearBtn = document.getElementById("{{ sig_clear_btn_id ?? 'sig-clearBtn' }}");
var submitBtn = document.getElementById("{{ sig_save_btn_id ?? 'sig-submitBtn' }}");
clearBtn.addEventListener("click", function(e) {
clearCanvas();
sigText.value = "Data URL for your signature will go here!";
sigImage.setAttribute("src", "");
}, false);
submitBtn.addEventListener("click", function(e) {
var dataUrl = canvas.toDataURL();
sigText.value = dataUrl;
sigImage.setAttribute("src", dataUrl);
}, false);
})();
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment