I am attempting to make a signature pad for a website that works by using the mouse or a touch screen device like an iPhone. I was successful in getting the pad to work with a mouse but I am having some weird issues when it comes to using the touch screen on an iPhone. The signature pad itself is all done client side by recording the move to, line to, and new line positions in 3 separate arrays and then building the line using an svg tag in a div segment while the user is drawing.
The issue I am having is that when using a touch screen everything works fine for the first line you draw but if you take your finger of the screen and the place it back on to draw a new line the system stops registering the movement of the users finger. Here is the sample of the code.
Javascript:
var moved;
//Load Event Listeners
function LoadList(){
document.getElementById('SigCan').addEventListener('touchstart',function(e){e.preventDefault();TrackNow();},false);
document.getElementById('SigCan').addEventListener('touchmove',function(e){e.preventDefault();touchMove(e);},false);
document.getElementById('SigCan').addEventListener('touchend',function(e){e.preventDefault();StopTrack();},false);
document.getElementById('SigCan').addEventListener('touchenter',function(e){e.preventDefault();TrackNow();},false);
}
function touchMove(e){
e.preventDefault;
//var sigobj = e.changedTouches[0];
//var length;
//length = length - 1
//alert(length);
getMouseXY(e.changedTouches[0]);
}
//Set value to isSig to true (mainly for mouse movement to track when button is pressed down
function TrackNow(){
document.getElementById('<%=isSig.ClientId%>').value = 'True';
}
//Set new line flag and reset moved flag. Reset isSig value
function StopTrack(){
if(moved == 1){
//alert('Stopped');
document.getElementById('<%=isSig.ClientId%>').value = 'False';
newline = 1;
moved = 0;
}
}
// Main function to retrieve mouse x-y pos.s, fill textboxes for server side code.
function getMouseXY(e) {
var signon;
var obj;
signon = document.getElementById('<%=isSig.ClientId%>').value;
if (signon == 'True'){
OX = tempX;
OY = tempY;
if (OX == 0){
OX = tempX;
}
if (OY == 0){
OY = tempY;
}
obj = document.getElementById('SigCan');
tempX = e.clientX - document.getElementById('SigCan').offsetLeft;
tempY = e.clientY - document.getElementById('SigCan').offsetTop;
//Remove all offsets
if(obj.offsetParent){
do{
tempX -= obj.offsetLeft;
tempY -= obj.offsetTop;
}while (obj = obj.offsetParent)
}
//Fill newline array, reset new line flag, set the old X & Y to current X & Y so line starts and new position.
if (newline == 1){
newobj[n] = OX + ',' + OY;
OX = tempX;
OY = tempY;
newline = 0;
n = n + 1;
}
//Fill moveto and lineto arrays
mtarray[i] = OX + "," + OY;
ltarray[i] = tempX + "," + tempY;
//Fill textboxes to be used in server side code
i = i + 1;
if (mtarray[1] != '') {
//document.getElementById('<%=mtAr.ClientId%>').value = tempX + "," + tempY;
document.getElementById('<%=mtAr.ClientId%>').value = mtarray.join('|');
document.getElementById('<%=ltAr.ClientId%>').value = ltarray.join('|');
document.getElementById('<%=nobj.ClientId%>').value = newobj.join('|');
}
mtarray[0] = ltarray[0];
//set moved flag to 1 so touchend code only runs after finger has been moved
moved = 1;
//Build svg and insert into inner html of the div segment
return DrawSig();
}
return true;
}
function DrawSig(){
var inhtml;
sigpath = ''
//check browser
ubrow = BrowserDetect.browser + ' ' + BrowserDetect.version;
//get path information
sigpath = BuildPath();
//if using IE 8 or 7 insert vml into inner HTML of div
if(ubrow == 'Explorer 8'||ubrow == 'Explorer 7'){
document.namespaces.add('v', 'urn:schemas-microsoft-com:vml', "#default#VML");
inhtml = "<v:group style='position:absolute;antialias:true;height:100px;width:500px' coordsize='500,100' coordorigin='0,0'><v:shape style='postition:absolute;height:100px;width:500px' strokeweight = '3pt' ><v:stroke joinstyle='round' endcap='round'/>";
inhtml = inhtml + "<v:path v ='" + sigpath + " '/>";
inhtml = inhtml + "</v:shape></v:group>";
document.getElementById('SigCan').innerHTML = inhtml;
//document.getElementById('ctl00_mtAr').value = inhtml
}
//if using any other browser insert svg into inner HTML of div
else{
inhtml = "<svg><g fill='none' stroke='black' stroke-width='4'><path d='" + sigpath + "'/></g></svg>";
document.getElementById('SigCan').innerHTML = inhtml;
//document.getElementById('<%=mtAr.ClientId%>').value = 'Working as it should';
}
return false;
}
function BuildPath(){
var path;
//Build vml path for ie 7 & 8
if(ubrow == 'Explorer 8'||ubrow == 'Explorer 7'){
path = 'M ' + mtarray[0] + ' L ' + ltarray[0];
for(var p = 1;p < i; p++){
path = path + ' M ' + mtarray[p] +' L ' + ltarray[p];
}
}
//Build svg path for other browsers
else{
path = ' M ' + mtarray[0].replace(',',' ') + ' L ' + ltarray[0].replace(',',' ');
for(var p = 1;p < i; p++){
path = path + ' M ' + mtarray[p].replace(',',' ') + ' L ' + ltarray[p].replace(',',' ');
}
}
return path;
}
HTML:
<body>
<table>
<tr>
<td colspan=3>
<div id ="SigCan" style="width:500px;height:100px;border:1px solid #c3c3c3;background:white;cursor:crosshair" onmousemove = "return getMouseXY(event);" onmousedown = "return TrackNow();" onmouseup = "return StopTrack();">
</div>
<script>
LoadList();
</script>
<asp:image runat = "Server" id ="SigImg"/>
</td>
</tr>
<tr>
<td><asp:textbox runat="Server" id="mtAr"/></td>
<td><asp:textbox runat="Server" id="ltAr"/><asp:textbox runat="Server" id ="nobj"/></td>
<td><asp:hiddenfield runat="Server" id="isSig"/></td>
</tr>
<tr>
<td><asp:button runat="Server" id = "btnSave" text="Save Signature" autopostback = "False"/><asp:checkbox id="chkVerify" runat="Server" text="I verify the above signature is mine." visible="false"/></td>
<td><asp:button id ="btnVerify" runat="server" text="Verify" visible = "false" onclick="VerifySig" /></td>
<td align="right"><asp:button id="btnClear" runat="Server" text = "Clear Signature" OnClientClick="return ClearSig();"/></td>
<td><asp:button id="Sig" runat="Server" OnClientClick="return SigP();" text="Give Me sig Path"/></td>
</tr>
</table>
</body>
I know a canvas object would probably be easier but I need to have it work by mouse on computers that are running IE8 and on touch screens. So far I haven't been able to find the fix but I have narrowed down the issue to this line of code document.getElementById('SigCan').innerHTML = inhtml;
If I comment out this piece of code everything works fine but that is the code that puts the SVG path into the div segment so I need it.
The only other thing I have found is that when this error is happening it appears that the touchend event doesn't run after the first line is drawn for some reason I have to double tap the screen to get the touchend to register. It gets to the point that when it stops responding I can press the screen again and it records it as another touch point. I am very new to touch screen and am only an amateur when it comes to Javascript so I was wondering if anyone could help.
Edit 1: I haven't found a fix yet but I did find out what is actually happening. For some reason when you remove your finger from the touch screen and then put it back on the touchmove event only fires once and never fires again no matter where you move the finger. For some reason if you take your finger off the screen while this is happening the touchend event doesn't run but it will run if you tap the screen in the target area.
Okay found the issue. Apparently when setting the innerHTML of a DIV to an SVG the target of the touch event the second time around became the SVG itself which doesn't have touch events. After several attempts to fix it the one that work the best was to place another DIV over the current DIV containing the SVG and then have all of the touch events trigger on the new DIV element. I had to change the position both div's to absolute so new div could be placed over the old one. This lead to design issues but those were easily fixed. Everything seems to work now.