完成版移动web版手写板
这个手写板历经4周写完,测试完,基本没有问题。。只是基本。。
主要功能:
1. 手绘、手写 。主要功能。 因为公司只需要移动端版本,所以将第二版中兼容的电脑端给删除了。所以如果有需要,可以直接参考第二版 。第二版中电脑端已无重大bug,可以参考使用。
2. 撤销、反撤销 。 我没有用canvas自带的撤销函数,因为没有反撤销的函数,所以索性就自己写了两个函数。
3. 双指滑动 。 双指滑动移动canvas , 以防canvas不够大 。我把冒泡等事件都屏蔽了,所以移动端本身自带的一切效果都实现不了。( 话说UC浏览器好多其他奇奇怪怪的手指触碰事件,比如双指滑动是前进和后退,手指还可以实现页面放大缩小 )
4. 清空画布 。 其实就是一句话 gc.clearRect(0, 0, board.width, board.height); 。
5. 保存 。 带自动截取最小大小图片效果 。
我还测了一堆的浏览器,虽说不多,也不少了,主流都在这里。应该..应该..应该可能好像貌似是没有bug了。。
如果引用,希望还能署上我的博客网站。O(∩_∩)O谢谢
话不多说,上代码:
demo.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="maximum-scale=1.0,minimum-scale=1.0,user-scalable=0,width=device-width,initial-scale=1.0"/> <title>title</title> <link rel="stylesheet" type="text/css" href="api.css"/> //注:这个css是清除样式使用的,无其他意义 <link rel="stylesheet" type="text/css" href="index.css"/> <style> html,body{ height: 100%; width: 100%; background-color: #ccc; } .header{ margin: 0 auto; font-size: 40px; text-align: center; } .pic{ max-height: 100px; max-width: 300px; background-color: #fff; } </style> </head> <body> <button class="a" style='width: 200px;height: 100px;background-color: blue;'>请点击</button> <div><img class="pic" alt="图片" src=''/></div> <div style='white-space: pre-wrap; width: 320px;height: auto; overflow: hidden;'> aaaaaaaaaaaaaa aaaaaaaaaaaaaaa ssssssssss ccccccccccccc ffffffffffffffffffffffffffffffffffffffffffffff vvvvvvvvvvvvvvvvvvvv bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ddddddd ddddddddddddddddddddd ddddddddddddddddddddddddddddddddddddd ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff bbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb bbbbbbb bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn nnnnnnnnnnnnnnnnnnnnnnnnnnnnn nnnnnnnnnn </div> </body> <script type="text/javascript" src="jquery-2.1.1.js"></script> <script type="text/javascript" src="index.js"></script> <script> $(function(){ $('.a').on('click',function(){ var src = showHandWrite(); $('.pic').attr('src', src); }); }); </script> </html>
index.js
$(function(){ NV = {}; var UA = navigator.userAgent.toLowerCase(); try { NV = (UA.indexOf('ucbrowser')>-1)?'UC浏览器': //ucbrowser (UA.indexOf('dolphin')>-1)?'海豚浏览器': //dolphinbrowsercn (UA.indexOf('huohou')>-1)?'火猴浏览器': //huohoubrowser (UA.indexOf('sogou')>-1)?'搜狗浏览器': //sogoumse/sogoumobilebrowser (UA.indexOf('opr')>-1)?'欧朋浏览器': //opr (UA.indexOf('qqbrowser')>-1)?'QQ浏览器': //mqqbrowser (UA.indexOf('liebao')>-1)?'猎豹浏览器': //liebaofast (UA.indexOf('2345')>-1)?'2345浏览器': //mb2345browser (UA.indexOf('miui')>-1)?'小米浏览器': //miuibrowser (UA.indexOf('baidu')>-1)?'百度浏览器': (UA.indexOf('aoyou')>-1)?'遨游浏览器': (UA.indexOf('theworld')>-1)?'世界之窗浏览器': (UA.indexOf('worldchrome')>-1)?'世界之窗极速浏览器': (UA.indexOf('greenbrowser')>-1)?'绿色浏览器': (UA.indexOf('360ee')>-1)?'360极速浏览器': (UA.indexOf('360se')>-1)?'360安全浏览器': '未知或无壳'; }catch(e){} //alert('浏览器UA='+UA+'\n\n浏览器外壳=' + NV); }); function showHandWrite(){ //使往下拉的时候不会因为页面过长,滑出画布范围。 $('body').css('overflow','hidden'); $('html').css('overflow','hidden'); //添加画布等进页面 var node = $('<div class="writeHand">'+ '<div class="headOfWriteHand">'+ '<div class="cancel"><img src="image/goback.png"/></div>'+ '<div class="reset"><img src="image/new.png"/></div>'+ '<div class="prev"><img src="image/back.png"/></div>'+ '<div class="next"><img src="image/go.png"/></div>'+ '<div class="save"><img src="image/ok.png"/></div>'+ '</div>'+ '<div class="mainOfWriteHand" id="mainOfWriteHand">'+ '<div class="boardContent"><canvas id="board" class="board">您的浏览器不支持</canvas></div>'+ '</div>'+ '<canvas id="boardNew" class="board" style="display: none;">您的浏览器不支持</canvas>'+ '</div>'); $('body').append(node); //设置画布等大小 setWindow(); //滑动,点击事件绑定。 onBind(); }; /* * 设置画布等大小 */ function setWindow(){ var myWidth = $(document).width(), myHeight = $(document).height(); // alert(myWidth+'||'+myHeight); $('.boardContent').width( myWidth * 3 ); $('.boardContent').height( myHeight * 2 ); var board = document.getElementById('board'); board.width = myWidth * 3; //gcWidth; board.height = myHeight * 2; //gcHeight; $('.mainOfWriteHand').width( myWidth ); $('.mainOfWriteHand').height( myHeight - 50 ); $('.mainOfWriteHand').scrollLeft( myWidth ); $('.mainOfWriteHand').scrollTop( myHeight / 2 ); }; /* * 滑动,点击事件绑定。 */ function onBind(){ var board = $('#board')[0], gc = board.getContext('2d'), //第一块画布,用来画 boardNew = $('#boardNew')[0], gcNew = boardNew.getContext('2d'), //第二块画布,用来储存.因为toDataURL和getImageData不能同时用.所以专门分了开来 doc = document.documentElement, body = document.body, main = document.getElementById('mainOfWriteHand'), boardContent = document.getElementById('boardContent'), myWidth = $(document).width(), myHeight = $(document).height(); gc.strokeStyle = 'rgb(0,0,0)'; //第一块画布笔触颜色 gc.lineWidth = 3; //第一块画布笔触像素 var recede = [], //存放历史笔画数组 forward = [], //存放可以反撤销笔画的数组 currentPath = [], //目前的笔画集合 currentPoint; //当前坐标点对象 var scrollX,scrollY; var doubleBeforeX = null, doubleBeforeY = null; if( NV == '海豚浏览器' ){ gc.clearRect(0, 0, board.width, board.height); }; main.ontouchstart = function(event) { event.preventDefault(); currentPath.length = 0; gc.closePath(); var e = window.event || event; if (e.touches[1]) { var doubleBeforeX = parseInt( e.touches[1].client - this.offsetLeft ), doubleBeforeY = parseInt( e.touches[1].clientY - this.offsetTop ); }else{ scrollX = main && main.scrollLeft || doc && doc.scrollLeft || body && body.scrollLeft || 0; scrollY = main && main.scrollTop || doc && doc.scrollTop || body && body.scrollTop || 0; var ex = e.touches[0].clientX + scrollX, ey = e.touches[0].clientY + scrollY, startX = ex - this.offsetLeft, startY = ey - this.offsetTop; currentPoint = { "x" : startX, "y" : startY }; currentPath.push(currentPoint); gc.beginPath(); //丢弃任何当前定义的路径并且开始一条新的路径 gc.moveTo(startX, startY); }; }; main.ontouchmove = function(event) { var e = window.event || event; if ( e.touches[1] ) { gc.closePath(); currentPath.length = 0; var lineX = parseInt( e.touches[1].clientX - this.offsetLeft ), lineY = parseInt( e.touches[1].clientY - this.offsetTop ); if( doubleBeforeX != null ){ var moveX = doubleBeforeX - lineX, moveY = doubleBeforeY - lineY; if( moveX >= 5 || moveX <=5 || moveY >= 5 || moveY <=5 ){ var scrollX1 = main && main.scrollLeft || doc && doc.scrollLeft || body && body.scrollLeft || 0, scrollY1 = main && main.scrollTop || doc && doc.scrollTop || body && body.scrollTop || 0; $('.mainOfWriteHand').scrollLeft( scrollX1 + moveX ); $('.mainOfWriteHand').scrollTop( scrollY1 + moveY ); doubleBeforeX = lineX; doubleBeforeY = lineY; }; }else{ doubleBeforeX = lineX; doubleBeforeY = lineY; }; console.log('-----------------------------------------------------------'); console.log(scrollX1+'||'+scrollY1); console.log(moveX+'||'+moveY); console.log(doubleBeforeX+'||'+doubleBeforeY); } else { if( currentPath.length ){ event.preventDefault(); var ex = e.touches[0].clientX + scrollX, ey = e.touches[0].clientY + scrollY, lineX = ex - this.offsetLeft, lineY = ey - this.offsetTop; gc.lineTo(lineX, lineY); gc.stroke(); //记录每一步坐标点 currentPoint = { "x" : lineX, "y" : lineY }; currentPath.push(currentPoint); }; }; }; //鼠标移出画布和鼠标抬起事件,处理方式相同 main.ontouchend = board.ontouchcancel = function(event) { event.preventDefault(); if (currentPath.length > 1) {//三个像素点以上才放进去 recede.push(currentPath.slice(0)); forward.length = 0; }; currentPath.length = 0; doubleBeforeX = null, doubleBeforeY = null; gc.closePath(); }; $('.headOfWriteHand').on('click', 'div', function(e) { var classN = $(this).attr("class"); switch(classN) { case 'cancel': var yesOrNo = confirm("你确定要退出?"); if( yesOrNo == true ){ $('.writeHand').remove(); $('body').css('overflow','auto'); $('html').css('overflow','auto'); }; break; case 'reset': var yesOrNo = confirm("你确定要重置?"); if( yesOrNo == true ){ gc.clearRect(0, 0, board.width, board.height); recede.length = 0; //记得在这里把记录的数组清空 }; break; case 'prev': if (recede.length != 0) { forward.push(recede.pop()); gc.clearRect(0, 0, board.width, board.height); if (recede.length > 0) { for (var i = 0; i < recede.length; i++) { gc.beginPath(); var currentX = recede[i][0].x, currentY = recede[i][0].y; gc.moveTo(currentX, currentY); for (var j = 0; j < recede[i].length; j++) { currentX = recede[i][j].x; currentY = recede[i][j].y; gc.lineTo(currentX, currentY); gc.stroke(); event.preventDefault(); }; gc.closePath(); }; }; }; break; case 'next': if (forward.length != 0) { gc.beginPath(); var leng = forward.length - 1; gc.moveTo(forward[leng][0].x, forward[leng][0].y); for (var i = 1; i < forward[leng].length; i++) { gc.lineTo(forward[leng][i].x, forward[leng][i].y); gc.stroke(); event.preventDefault(); }; recede.push(forward.pop()); gc.closePath(); } break; case 'save': var smallX, smallY, bigX, bigY; if (recede.length) { if (recede[0][0].x > recede[0][1].x) { smallX = recede[0][1].x; bigX = recede[0][0].x; } else { bigX = recede[0][1].x; smallX = recede[0][0].x; }; if (recede[0][0].y > recede[0][1].y) { smallY = recede[0][1].y; bigY = recede[0][0].y; } else { bigY = recede[0][1].y; smallY = recede[0][0].y; }; }; for (var i = 0; i < recede.length; i++) { for (var j = 0; j < recede[i].length; j++) { smallX = (smallX < recede[i][j].x ) ? smallX : recede[i][j].x; bigX = (bigX > recede[i][j].x ) ? bigX : recede[i][j].x; smallY = (smallY < recede[i][j].y ) ? smallY : recede[i][j].y; bigY = (bigY > recede[i][j].y ) ? bigY : recede[i][j].y; }; }; var gcWidth = bigX - smallX, gcHeight = bigY - smallY; if( recede.length == 0 ){ alert('请写上答案'); }else{ if( NV == '搜狗浏览器' ){ var imgData=gc.getImageData(0, 0, 1000, 1000);//这句话并没有执行 gc.putImageData(imgData, 0, 0); var image = new Image(); image.src = board.toDataURL("image/png", 0.5); }else{ var imgData = gc.getImageData(smallX, smallY, gcWidth, gcHeight); boardNew.width = gcWidth; boardNew.height = gcHeight; gcNew.putImageData(imgData, 0, 0); var image = new Image(); image.src = boardNew.toDataURL("image/png", 0.5); }; //dom.attr('src', image.src); $('.writeHand').remove(); $('body').css('overflow','auto'); $('html').css('overflow','auto'); return image.src; break; } } }); };
index.css
.writeHand { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } .headOfWriteHand { display: -webkit-box; /* OLD - iOS 6-, Safari 3.1-6 */ display: -moz-box; /* OLD - Firefox 19- (buggy but mostly works) */ display: -ms-flexbox; /* TWEENER - IE 10 */ display: -webkit-flex; /* NEW - Chrome */ display: flex; /* NEW, Spec - Opera 12.1, Firefox 20+ */ padding-left: 8%; position: fixed; top: 0; left: 0; z-index: 999; height: 50px; width: 92%; background-color: #23c6c8; } .headOfWriteHand div { -webkit-box-flex: 1; /* OLD - iOS 6-, Safari 3.1-6 */ -moz-box-flex: 1; /* OLD - Firefox 19- */ width: 20%; /* For old syntax, otherwise collapses. */ -webkit-flex: 1; /* Chrome */ -ms-flex: 1; /* IE 10 */ flex: 1; /* NEW, Spec - Opera 12.1, Firefox 20+ */ /*margin-left: 4%; margin-right: 4%;*/ padding: 5px; vertical-align: middle; line-height: 40px; height: 40px; } .headOfWriteHand img { margin-top: 8px; height: 25px; } .mainOfWriteHand { position: relative; top: 50px; height: auto; width: auto; overflow: auto; -webkit-overflow-scrolling : touch; background-color: rgba(255,255,255,0.7); } .boardContent{} canvas {}
api.css
html{font-family:sans-serif}a,body,center,cite,code,dd,del,div,dl,dt,em,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hr,html,img,input,label,legend,li,mark,ol,p,section,span,strong,textarea,time,ul,var{margin:0;border:0;padding:0;font-style:normal;color:#323232}body,html{-webkit-touch-callout:none;-webkit-text-size-adjust:none;-webkit-tap-highlight-color:transparent;-webkit-user-select:none;background-color:#fff}article,aside,details,fieldset,figcaption,figure,footer,header,main,nav,section{display:block}a,button,cite,code,del,em,img,label,mark,small,strong,textarea,time,var{display:inline-block}footer,header,section{position:relative}ol,ul{list-style:none}button,input,textarea{border:0;margin:0;padding:0;font-size:1em;line-height:1em;background-color:transparent}span{display:inline-block}a:active,a:hover{outline:0}a,a:visited{text-decoration:none}.wordWrap,label{word-wrap:break-word;word-break:break-all}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}.clearfix:after{content:' ';display:block;clear:both;visibility:hidden;line-height:0;height:0}.loading_more{display:block;height:1.5em;width:100%}.loading_more:before{background-image:url(../image/loading_more.gif) 30% center;background-repeat:no-repeat;background-size:contain;text-align:center}.loading_more:after{content:'加载更多'}

浙公网安备 33010602011771号