Skip to content

Instantly share code, notes, and snippets.

@bencentra
Last active July 19, 2024 12:42
Show Gist options
  • Save bencentra/91350fe91c377c1ca574 to your computer and use it in GitHub Desktop.
Save bencentra/91350fe91c377c1ca574 to your computer and use it in GitHub Desktop.
Example of using HTML5 canvas with both mouse and (single) touch input
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="keywords" content="">
<meta name="author" content="">
<title>E-Signature</title>
<!-- Styles -->
<link rel="stylesheet" href="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
<style>
body {
padding-top: 20px;
padding-bottom: 20px;
}
#sig-canvas {
border: 2px dotted #CCCCCC;
border-radius: 5px;
cursor: crosshair;
}
#sig-dataUrl {
width: 100%;
}
</style>
</head>
<body>
<!-- Content -->
<div class="container">
<div class="row">
<div class="col-md-12">
<h1>E-Signature</h1>
<p>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">
Get a better browser, bro.
</canvas>
</div>
</div>
<div class="row">
<div class="col-md-12">
<button class="btn btn-primary" id="sig-submitBtn">
Submit Signature
</button>
<button class="btn btn-default" id="sig-clearBtn">
Clear Signature
</button>
</div>
</div>
<br/>
<div class="row">
<div class="col-md-12">
<textarea id="sig-dataUrl" class="form-control" rows="5">
Data URL for your signature will go here!
</textarea>
</div>
</div>
<br/>
<div class="row">
<div class="col-md-12">
<img id="sig-image" src="" alt="Your signature will go here!"/>
</div>
</div>
</div>
<!-- Scripts -->
<script src="https://code.jquery.com/jquery-2.1.0.min.js"></script>
<script src="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<!--<script src="https://code.angularjs.org/snapshot/angular.min.js"></script>-->
<script>
(function() {
// Get a regular interval for drawing to the screen
window.requestAnimFrame = (function (callback) {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimaitonFrame ||
function (callback) {
window.setTimeout(callback, 1000/60);
};
})();
// Set up the canvas
var canvas = document.getElementById("sig-canvas");
var ctx = canvas.getContext("2d");
ctx.strokeStyle = "#222222";
ctx.lineWith = 2;
// Set up the UI
var sigText = document.getElementById("sig-dataUrl");
var sigImage = document.getElementById("sig-image");
var clearBtn = document.getElementById("sig-clearBtn");
var submitBtn = document.getElementById("sig-submitBtn");
clearBtn.addEventListener("click", function (e) {
clearCanvas();
sigText.innerHTML = "Data URL for your signature will go here!";
sigImage.setAttribute("src", "");
}, false);
submitBtn.addEventListener("click", function (e) {
var dataUrl = canvas.toDataURL();
sigText.innerHTML = dataUrl;
sigImage.setAttribute("src", dataUrl);
}, false);
// Set up mouse events for drawing
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);
// Set up touch events for mobile, etc
canvas.addEventListener("touchstart", function (e) {
mousePos = getTouchPos(canvas, e);
var touch = e.touches[0];
var mouseEvent = new MouseEvent("mousedown", {
clientX: touch.clientX,
clientY: touch.clientY
});
canvas.dispatchEvent(mouseEvent);
}, false);
canvas.addEventListener("touchend", function (e) {
var mouseEvent = new MouseEvent("mouseup", {});
canvas.dispatchEvent(mouseEvent);
}, false);
canvas.addEventListener("touchmove", function (e) {
var touch = e.touches[0];
var mouseEvent = new MouseEvent("mousemove", {
clientX: touch.clientX,
clientY: touch.clientY
});
canvas.dispatchEvent(mouseEvent);
}, false);
// 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);
// Get the position of the mouse relative to the canvas
function getMousePos(canvasDom, mouseEvent) {
var rect = canvasDom.getBoundingClientRect();
return {
x: mouseEvent.clientX - rect.left,
y: mouseEvent.clientY - rect.top
};
}
// Get the position of a touch relative to the canvas
function getTouchPos(canvasDom, touchEvent) {
var rect = canvasDom.getBoundingClientRect();
return {
x: touchEvent.touches[0].clientX - rect.left,
y: touchEvent.touches[0].clientY - rect.top
};
}
// Draw to the canvas
function renderCanvas() {
if (drawing) {
ctx.moveTo(lastPos.x, lastPos.y);
ctx.lineTo(mousePos.x, mousePos.y);
ctx.stroke();
lastPos = mousePos;
}
}
// Clear the canvas
function clearCanvas() {
canvas.width = canvas.width;
}
// Allow for animation
(function drawLoop () {
requestAnimFrame(drawLoop);
renderCanvas();
})();
})();
</script>
</body>
</html>
@ranasikandar
Copy link

in chrome mobile mode there is an error at e.preventDefault();
"Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/5093566007214080"

in opera mini the scroll is enable even drawing the canvas.

@Proibito04
Copy link

Good Job!!!

@ergocortex
Copy link

For working with Chrome Mobile replace all preventDefault calls :

document.body.addEventListener("touchstart", function (e) { if (e.target == canvas) { e.preventDefault(); } }, false);

with

document.body.addEventListener("touchstart", function (e) { if (e.target == canvas) { e.preventDefault(); } }, {passive: false});

@jlanssie
Copy link

I don't quite understand how these parts work:

// Get a regular interval for drawing to the screen
			window.requestAnimFrame = (function (callback) {
				return window.requestAnimationFrame || 
							window.webkitRequestAnimationFrame ||
							window.mozRequestAnimationFrame ||
							window.oRequestAnimationFrame ||
							window.msRequestAnimaitonFrame ||
							function (callback) {
							 	window.setTimeout(callback, 1000/60);
							};
			})();

and

			// Allow for animation
			(function drawLoop () {
				requestAnimFrame(drawLoop);
				renderCanvas();
			})();

Can you explain how it works and what is does?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment