Capturing a sketch drawn with mouse / touch gestures

6:40 PM 10/23/2018

So.. can you forge John Doe's signature below?

No, this page doesn't intend to support forgery; it only tries to capture a sketch, drawn either with mouse or touch gestures on HTML5 Canvas element. Period.

User's sketch / drawing could then be saved to database for I don't know... just an instant, black and white art collection, maybe?

Removing the hint totally from screen however, gives me an idea to have different kind of login mechanism... now not only the pixels of the signature but also the timing, speed and acceleration; all must be recorded and processed to a backend to make a secure login... whoaa, that's out of this page scope I guess:)

Sorry, your browser doesn't support HTML5 canvas.

HTML

<form id="test" class="with-hint">
	<fieldset>
		<canvas width="300" height="150">Sorry, your browser doesn't support HTML5 canvas.</canvas>
		<label>Name:<input name="name" type="text" value="John Doe" size="8" autocomplete="off"></label>
		<label>Hint:<input name="hint" type="checkbox" checked autocomplete="off"></label>
		<input name="sign" type="hidden" value="">
		<button type="reset">Reset</button>
		<button type="submit">Submit</button>
	</fieldset>
</form>
		

CSS

#test{
	max-width: 50em;
}
#test.with-hint fieldset{
	background: url(john-doe.png) no-repeat top left / 100% auto;
}
#test canvas{
	display: block;
	width: 100%;
	height: auto;
	background-color: rgba(255,255,255,0.9);
}
		

Javascript

var
	//elements
	test = document.getElementById('test'),
	canv = test.getElementsByTagName('canvas')[0],
	ctx = canv.getContext('2d'),
	
	gesture = {
		type: -1,		//no mouse no touch
		rect: null, //measured (scaled) canvas dimension due to device screen change
		scale: 1		//ratio of actual to measured canvas coordinate system
	};

//moving gesture event handler

function onMove(ev){
	if(gesture.type > -1){
		var //measure gesture
			left = gesture.type === 0 ? 
				Math.round(gesture.scale * (ev.clientX - gesture.rect.left)) : 
				Math.round(gesture.scale * (ev.touches[0].clientX - gesture.rect.left)),
			top = gesture.type === 0 ? 
				Math.round(gesture.scale * (ev.clientY - gesture.rect.top)) : 
				Math.round(gesture.scale * (ev.touches[0].clientY - gesture.rect.top));
		
		//draw gesture
		ctx.lineTo(left, top);
		ctx.stroke();
	}
}

//install mouse and touch event handlers on canvas and HTML

canv.addEventListener('mousedown', function(ev){
	if(gesture.type === -1){
		//gesture type is a mouse down
		gesture.type = 0;

		//canvas dimension measured here, as user may rotate screen from landscape to portrait or vice versa
		gesture.rect = canv.getBoundingClientRect();
		gesture.scale = canv.width / gesture.rect.width;

		var//measure mouse
			left = Math.round(gesture.scale * (ev.clientX - gesture.rect.left)),
			top = Math.round(gesture.scale * (ev.clientY - gesture.rect.top));
		
		//start drawing mouse
		ctx.beginPath();
		ctx.moveTo(left, top);

		//install mouse movement event listener
		document.documentElement.addEventListener('mousemove', onMove, false);
	}
}, false);

document.documentElement.addEventListener('mouseup', function(){
	if(gesture.type === 0){
		//no mouse
		gesture.type = -1;
		
		//uninstall mouse movement event listener
		document.documentElement.removeEventListener('mousemove', onMove, false);
	}
}, false);

canv.addEventListener('touchstart', function(ev){
	if(gesture.type === -1){
		//gesture type is a touch down
		gesture.type = 1;
		
		//canvas dimension measured here, as user may rotate screen from landscape to portrait or vice versa
		gesture.rect = canv.getBoundingClientRect();
		gesture.scale = canv.width / gesture.rect.width;
		
		var //measure touch
			left = Math.round(gesture.scale * (ev.touches[0].clientX - gesture.rect.left)),
			top = Math.round(gesture.scale * (ev.touches[0].clientY - gesture.rect.top));

		//start drawing touch
		ctx.beginPath();
		ctx.moveTo(left, top);

		//install touch movement event listener
		document.documentElement.addEventListener('touchmove', onMove, false);
	}
}, false);

document.documentElement.addEventListener('touchend', function(){
	if(gesture.type === 1){
		//no touch
		gesture.type = -1;
		
		//uninstall touch movement event listener
		document.documentElement.removeEventListener('touchmove', onMove, false);
	}
}, false);

//install click handler on clear canvas button

test.addEventListener('reset', function(){
	ctx.clearRect(0, 0, canv.width, canv.height);
}, false);

//install form (before) submission handler

test.addEventListener('submit', function(){
	this.sign.value = canv.toDataURL();
}, false);

//install hint click handler

test.hint.addEventListener('change', function(){
	if(this.checked){
		if(test.className.search(/\bwith-hint\b/) === -1)
			test.className = test.className.trim() + ' with-hint';
	}
	else
		test.className = test.className.replace(/\bwith-hint\b/, '');
}, false);
		

Comments