完成版移动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:'加载更多'}

 

posted @ 2015-12-04 11:38  木头耕田  阅读(1640)  评论(0)    收藏  举报