convas的真實用法

原文鏈接:http://my.oschina.net/codespring/blog/397464

概要:用js控制convas模仿windows上的多選、單選、拖動控制

功能包括:鼠標點擊單選、拖動多選、ctrl+單擊組合效果、對選中的單個或多個canvas圖層通過鼠標拖動、方向鍵移動、delete刪除圖層等。

html頁面代碼:

<!DOCTYPE>
<html>
 
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<!--[if lt IE 9]><script language="javascript" type="text/javascript" src="/jqplot/excanvas.min.js"></script><![endif]-->
<script type="text/javascript">
    var list=[];
    var currentC;
    var selected=[];
    var _e={};
    var showTimer;
    var keyBoardTimer;
    var move=false;
    var hit=false;
    var inZoom=false;
    var exist=false;
    var ctrlDown=false;
    var directDown='';
    var cricle = function(x,y,r){
        this.x=x;
        this.y=y;
        this.r=r;
        this.angle=10;
        this.posB={};
 
        this.isCurrent=false;
        /*如果傳遞了flag參數,表示正在執行鼠標拖選操作,必須忽略是否有圖層被選中的判斷*/
        this.drawC=function(ctx,x,y,posA){
            ctx.save();
            ctx.beginPath();
            ctx.moveTo(this.x,this.y-this.r);
            ctx.arc(this.x,this.y,this.r,2*Math.PI,0,true);
            /*判斷元素是否在選區中*/
            for(var i=selected.length;i--;){
                if(this===selected[i]){
                    exist=true;
                    console.log('exits');
                }
            }
            console.log('exitfor');
            /*1.非鼠標拖選,此時posA爲空,
            *2.頁面加載時x,y沒有傳值.(鼠標擡起) 
            */
            if (!posA && x && y && this.isCurrent) {
                    ctx.fillStyle = '#ff0000';
                    currentC=this;
                    this.isCurrent=true;
                    if(selected.length>0){
                        console.log(selected.length);
                         
                        if(!exist){
                            console.log('notexits');
                            selected.push(this);
                        }
                        exist=false;/*判斷過後,重置元素是否在選區中的狀態*/
                    }else{
                        selected.push(this);
                    }
            }else{
                 
                if(posA){/*拖選過程中,碰撞檢測*/
                    //console.log('1');
                    /*如果是圓posB:{正對角線兩點座標,中心點座標,旋轉角度}*/
                    this.posB={'x1':(this.x-this.r),'y1':(this.y-this.r),'x2':(this.x+this.r),'y2':(this.y+this.r),'x3':(this.x+this.r),'y3':(this.y-this.r),'x4':(this.x-this.r),'y4':(this.y+this.r),'x':this.x,'y':this.y,'angle':this.angle};
                    /*如果是矩形posB:{正對角線兩點座標,副對角線兩點座標,中心點座標,旋轉角度}*/
                    //this.posB={x1,y1,x2,y2,x3,y3,x4,y4,x,y,angle}
                    var flag=testCollision(posA,this.posB);
                    if(flag){
                        //console.log('2');
                         
                        if(selected.length>0){
                             
                            if(!exist){
                                console.log('notexits');
                                selected.push(this);
                            }
                            exist=false;/*判斷過後,重置元素是否在選區中的狀態*/
                        }else{
                            selected.push(this);
                        }
                         
                        console.log('selected',selected.length);
                        ctx.fillStyle = '#ff0000';
                    }else{
                        //console.log('3');
                        ctx.fillStyle = '#999999';
                    }
                }else{/*既沒拖選,也沒選中。(頁面初始加載,鼠標擡起)*/
                    if(exist){
                        ctx.fillStyle = '#ff0000';
                    }else{
                        ctx.fillStyle = '#999999';
                    }
                    exist=false;/*判斷過後,重置元素是否在選區中的狀態*/
                }
 
            }
            ctx.fill();
            ctx.restore();
        }
        /*判斷是否點中元素,點中了就改變當前元素*/
        this.testHit=function(ctx,x,y){
            /*先清空上一次的當前元素*/
            if(currentC){
                currentC.isCurrent=false;
                currentC=null;
            }
            ctx.save();
            ctx.beginPath();
            ctx.moveTo(this.x,this.y-this.r);
            ctx.arc(this.x,this.y,this.r,2*Math.PI,0,true);
            if (ctx.isPointInPath(x, y)){
                hit=true;
                currentC=this;
                this.isCurrent=true;
 
            }
            ctx.restore();
        }
     
    }
    function draw(action){
        console.log('draw func');
        var canvas = document.getElementById('tutorial');
        canvas.height = canvas.height;//這是個什麼技巧,可以清空畫布
        if (canvas.getContext){
            var ctx = canvas.getContext('2d');
            console.log(['directDown',directDown]);
            if(action===undefined){/*如果是頁面第一次加載*/
                for(var i=0;i<10;i++){
                    var c=new cricle(20*i,20*i,5*i);
                    c.drawC(ctx);//在一張畫布上反覆畫
                    list.push(c);
                }
            }else{/*如果是鍵盤控制移動*/
                console.log('delete');
                for(var i=0;i<list.length;i++){
                    var c=list[i];
                    c.drawC(ctx);
                }
            }
        }
    }
     
    function reDraw(e,posA){//有flag參數表示拖放選擇操作
        console.log('reDraw func');
        if(e){
            e=e||event;
            var canvas = document.getElementById('tutorial');
            var x = e.clientX - canvas.offsetLeft;
            var y = e.clientY - canvas.offsetTop;
        }
        /*在每次拖選移動前重置selected,以保證拿到實時的選中元素*/
        if(posA){
            selected=[];
        }
        canvas.height = canvas.height;//這是個什麼技巧,可以清空畫布
        //var ctx = canvas.getContext('2d');
        //ctx.clearRect(0,0,canvas.width,canvas.height);
        if (canvas.getContext){
            var ctx = canvas.getContext('2d');
            for(var i=0;i<list.length;i++){
                var c=list[i];
                if(posA){
                    c.drawC(ctx,x,y,posA);//拖選時
                }else{
                    console.log('ininin');
                    c.drawC(ctx,x,y);//非拖選時:拖動選區、元素、重畫以顯示當前元素
                }
            }
        }
    }
     
    function show(e){
        console.log('show func');
         
        move=true;
        e=e||event;
        var canvas = document.getElementById('tutorial');
        var ctx = canvas.getContext('2d');
        var x = e.clientX - canvas.offsetLeft;
        var y = e.clientY - canvas.offsetTop;
        var _x = _e.clientX - canvas.offsetLeft;
        var _y = _e.clientY - canvas.offsetTop;
        //showTimer=setInterval(function(e){reDraw(e);console.log('showTimer');},10,_e);//在鼠標移動時不斷重畫
        /*如果有選中的圖層,則只改變圖層中心點的座標,由定時器showTimer控制重畫所有圖層
        *如果沒有選中的圖層,則清除showTimer定時器,執行鼠標畫出跟隨矩形效果
        */
 
        if(!hit){
            console.log('not hit');
            console.log('dragrect');
            //clearInterval(showTimer);
            dragRect(e,_e,__e);
        }else{
            if(selected.length>1){/*如果有選區*/
                if(inZoom){
                    /*拖動當前選區*/
                    console.log('length>1 inzoom');
                    for(var i=selected.length;i--;){
                        var c=selected[i];
                        c.x=c.x+(x-_x);
                        c.y=c.y+(y-_y);
                    }
                    reDraw(e);
                }else{
                    /*清空選區,拖動當前元素*/
                    console.log('length>1 notinzoom');
                    selected=[];
                    if(currentC){
                        currentC.x=currentC.x+(x-_x);
                        currentC.y=currentC.y+(y-_y);
                        reDraw(e)
                    }
                }
            }else{/*如果沒有選區*/
                /*拖動當前元素*/
                console.log('length<=1 inzoom');
                if(currentC){
                    currentC.x=currentC.x+(x-_x);
                    currentC.y=currentC.y+(y-_y);
                    reDraw(e)
                }
            }
        }
        _e=e;
    }
    function dragRect(e,_e,__e){
        console.log('dragRect func');
        var pos={};
        var canvas = document.getElementById('tutorial');
        canvas.style.zIndex='100';
        //鼠標當前位置
        var x = e.clientX - canvas.offsetLeft;
        var y = e.clientY - canvas.offsetTop;
        //鼠標移動的前一步位置
        var _x = _e.clientX - canvas.offsetLeft;
        var _y = _e.clientY - canvas.offsetTop;
        //鼠標按下時的位置
        var __x = __e.clientX - canvas.offsetLeft;
        var __y = __e.clientY - canvas.offsetTop;
        pos.x1=x;
        pos.y1=y;
        pos.x2=__x;
        pos.y2=__y;
        reDraw(e,pos);
        var context=canvas.getContext("2d");
        //context.save();
 
//      context.clearRect(__x,__y,(_x-__x),(_y-__y));
//      reDraw(e,pos);
//      context.fillStyle="rgba(10%,25%,10%,0.1)";  //填充的顏色
//      context.strokeStyle="000";  //邊框顏色
//      context.linewidth=1;  //邊框寬
//      context.fillRect(x,y,(__x-x),(__y-y));  //填充顏色 x y座標 寬 高
        //context.strokeRect(x,y,(__x-x),(__y-y));  //填充邊框 x y座標 寬 高
        //context.restore();
         
        context.beginPath();
        context.setLineDash([5,2]);
        context.rect(__x,__y,(_x-__x),(_y-__y));
        context.stroke();
 
    }
 
    window.onload=function(){
         
        var canvas = document.getElementById('tutorial');
        draw();//頁面載入畫出每個圓時,(x,y)都是在路徑上,非路徑內
         
        canvas.onmousedown=function(e){
            move=false;
            e=e||event;
            var x = e.clientX - canvas.offsetLeft;
            var y = e.clientY - canvas.offsetTop;
            //if(currentC)  currentC.isCurrent=false;//路徑不能保存,對象還是存在的
            //currentC=null;//清空選中狀態
            /*判斷是否點中,點中則改變了當前元素*/
            if (canvas.getContext){
                var ctx = canvas.getContext('2d');
                for(var i=list.length;i--;){
                    var c=list[i];
                    c.testHit(ctx,x,y);
                    if(hit) break;
                }
            }
            /*判斷點中元素是否在選區中,只有selected長度大於0時纔有選區概念*/
            if(selected.length>0){
                for(var i=selected.length;i--;){
                    var c=selected[i];
                    if(currentC===selected[i]){
                        inZoom=true;
                        break;
                    }
                }
            }
            /*如果點中了並且沒有按下ctrl鍵*/
            if(hit && !ctrlDown){
                 
                if(inZoom){/*如果點中元素在選區中*/
                    console.log(selected.length);
                    console.log('inZoom');
                    /*按下鼠標,只改變當前選中元素(在檢測hit時已經做了),擡起鼠標時重畫:清空選區,顯示當前元素*/
 
                }else{/*如果點中元素不在選區中*/
                    /*按下鼠標立刻重畫:清空選區,顯示當前元素*/
                    console.log('notinZoom');
                    selected=[];
                    reDraw(e);
                }
            }else{
                /*沒點中,或者按下了ctrl鍵,就什麼都不做,留給鼠標擡起事件*/
                //selected=[];
                 
            }
             
            _e=e;
            __e=e;
             
            //showTimer=setInterval(function(e){reDraw(e);},10,_e);//在鼠標移動時不斷重畫
            canvas.onmousemove=show;//根據鼠標移動不斷改變圓心位置
 
            document.onmouseup=function(){
                /*如果沒有移動鼠標,就清除選區,最後的重畫只需畫出當前元素*/
                if(!move){
                    if(!hit){/*如果點空了,清除當前元素、清除選區*/
                        if(currentC){
                            currentC.isCurrent=false;
                            currentC=null;
                        }
                        selected=[];
                    }else{/*如果點中了*/
                        hit=false;/*重置點中狀態*/
                        if(ctrlDown){/*如果按下了ctrl*/
                            if(!inZoom){/*如果點中元素不在selected裏,就添加,否則從selected裏刪除,並清空當前元素*/
                                selected.push(currentC);
                            }else{
                                if(currentC){
                                    currentC.isCurrent=false;
                                    currentC=null;
                                }
                                console.log(['splice',selected.length]);
                                selected.splice(i,1);
                                console.log(['splice',selected.length]);
                            }
                        }else{/*如果沒有按下ctrl,直接清空selected,保留當前元素*/
                            selected=[];
                        }
                    }
                    inZoom=false;
                     
                    reDraw(e);
                }else{
                    move=false;/*如果移動了,重置移動狀態*/
                    hit=false;/*如果移動了,重置點中狀態*/
                    inZoom=false;/*如果移動了,重置是否在選區中的狀態*/
                     
                }
                //如果有移動,則不會清除選中狀態
                //不管是否移動,解除鼠標移動的監聽事件
                canvas.onmousemove=null;//只允許在鼠標按下後監聽鼠標移動事件
                //reDraw(e);
                clearInterval(showTimer);//鼠標擡起後停止重畫
                console.log('up');
            }
 
        }
        document.onkeydown=function(e){
            var ev = window.event || e;
             
            console.log(['keydown',ev.keyCode]);
            switch(ev.keyCode){
                case 17:
                     
                    ctrlDown=true;
                    break;
                case 37:
                    ev.preventDefault();
                    directDown=true;
                    keyboardMove(-1,0);
                    break;
                case 38:
                    directDown=true;
                    ev.preventDefault();
                    keyboardMove(0,-1);
                    break;
                case 39:
                    directDown=true;
                    ev.preventDefault();
                    keyboardMove(1,0);
                    break;
                case 40:
                    directDown=true;
                    ev.preventDefault();
                    keyboardMove(0,1);
                    break;
                case 46:
                    keyboardDelete();
                    break;
            }
        }
        document.onkeyup=function(e){
            var ev=window.event || e;
            //ev.preventDefault();
            console.log(['keyup',ev.keyCode]);
            switch(ev.keyCode){
                case 17:
                    ctrlDown=false;
                    break;
                case 37:
                case 38:
                case 39:
                case 40:
                    directDown=false;
                    break;
                case 46:
 
            }
        }
    }
    /*鍵盤方向鍵控制移動*/
    function keyboardMove(x,y){
        if(selected.length>0){
            for(var i=selected.length;i--;){
                var c=selected[i];
                c.x+=x;
                c.y+=y;
            }
        }else if(currentC){
            currentC.x+=x;
            currentC.y+=y;
        }
        draw('direct');
    }
    /*delete鍵刪除元素*/
    function keyboardDelete(){
        /*刪除選區中的元素*/
        var tempList=[];
        if(selected.length>0){
            for(var j=list.length;j--;){
                var flag=true;
                for(var i=selected.length;i--;){
                    if(list[j]===selected[i]){
                        flag=false;
                        break
                    }                   
                }
                if(flag){
                    tempList.push(list[j]);
                }
            }
            list=tempList;
        }else if(currentC){
            for(var j=list.length;j--;){
                if(list[j]===currentC){
                    list.splice(j,1);
                    break
                }
            }
        }
        draw('delete');
    }
    /*碰撞檢測*/
    function testCollision(posA,posB){
        //console.log(['posA',posA]);
        //console.log(['posB',posB]);
        var crossA=lineSpace(posA.x1,posA.y1,posA.x2,posA.y2);
        var crossB=lineSpace(posB.x1,posB.y1,posB.x2,posB.y2);
        var centerB={};
        var crossPos={};
        var centerA={};
        var max={};
        var min={};
        var sh={};
        var flag=false;
        centerA.x=((posA.x1-posA.x2)>0)?(posA.x1-(posA.x1-posA.x2)/2):(posA.x2-(posA.x2-posA.x1)/2);
        centerA.y=((posA.y1-posA.y2)>0)?(posA.y1-(posA.y1-posA.y2)/2):(posA.y2-(posA.y2-posA.y1)/2);
        var centerAToB=lineSpace(centerA.x,centerA.y,posB.x,posB.y);
        if(centerAToB>((crossA+crossB)/2)){
            //console.log('4');
            return flag;
        }
        if(posB.angle===0){
            //console.log('5');
            /*只需比較兩矩形的對角點*/
            /*crossPos:{目標矩形:左上點,右下點,鼠標矩形:左上點,右下點}*/
            if(posA.x1>posA.x2 && posA.y1>posA.y2){
                /*左上到右下*/
                crossPos={'x1':posB.x1,'y1':posB.y1,'x2':posB.x2,'y2':posB.y2,'x3':posA.x2,'y3':posA.y2,'x4':posA.x1,'y4':posA.y1};
                flag=simpleTest(crossPos);
                return flag;
            }else if(posA.x1<posA.x2 && posA.y1<posA.y2){
                /*右下到左上*/
                //console.log('6');
                crossPos={'x1':posB.x1,'y1':posB.y1,'x2':posB.x2,'y2':posB.y2,'x3':posA.x1,'y3':posA.y1,'x4':posA.x2,'y4':posA.y2};
                flag=simpleTest(crossPos);
                //console.log(flag);
                return flag;
            }else if(posA.x1<posA.x2 && posA.y1>posA.y2){
                /*右上到左下*/
                var x1=posA.x1;
                var y1=posA.y2;
                var x2=posA.x2;
                var y2=posA.y1;
                crossPos={'x1':posB.x1,'y1':posB.y1,'x2':posB.x2,'y2':posB.y2,'x3':x1,'y3':y1,'x4':x2,'y4':y2};
                flag=simpleTest(crossPos);
                return flag;
 
            }else if(posA.x1>posA.x2 && posA.y1<posA.y2){
                /*左下到右上*/
                var x1=posA.x2;
                var y1=posA.y1;
                var x2=posA.x1;
                var y2=posA.y2;
                crossPos={'x1':posB.x1,'y1':posB.y1,'x2':posB.x2,'y2':posB.y2,'x3':x1,'y3':y1,'x4':x2,'y4':y2};
                flag=simpleTest(crossPos);
                return flag;
            }
 
        }else{
            //console.log('分離定軸定理');
            /*使用分離定軸定理解決*/
            /*目標矩形四個點到x、y軸的距離,取最大值*/
            max.Bx1=Math.max(posB.x1,posB.x2,posB.x3,posB.x4);
            max.By1=Math.max(posB.y1,posB.y2,posB.y3,posB.y4);
            min.Bx1=Math.min(posB.x1,posB.x2,posB.x3,posB.x4);
            min.By1=Math.min(posB.y1,posB.y2,posB.y3,posB.y4);
            max.Ax1=Math.max(posA.x1,posA.x2);
            max.Ay1=Math.max(posA.y1,posA.y2);
            min.Ax1=Math.min(posA.x1,posA.x2);
            min.Ay1=Math.min(posA.y1,posA.y2);
 
 
            if(max.Bx1<min.Ax1 || max.Ax1<min.Bx1 || max.By1<min.Ay1 || max.Ay1<min.By1){
                return false;
            }
 
             
            /*鼠標拖拽矩形四個點到目標矩形兩條垂直邊的距離*/
            /*使用B14的法向量*/
            sh.x=posB.y4-posB.y1;
            sh.y=posB.x1-posB.x4;
            sh.x1=posA.x1-posA.x3;
            sh.y1=posA.y1-posA.y3;
            var lineA1=getShadow(sh);
            sh.x1=posA.x1-posA.x4;
            sh.y1=posA.y1-posA.y4;
            var lineA2=getShadow(sh);
            sh.x1=posA.x2-posA.x3;
            sh.y1=posA.y2-posA.y3;
            var lineA3=getShadow(sh);
            sh.x1=posA.x2-posA.x4;
            sh.y1=posA.y2-posA.y4;
            var lineA4=getShadow(sh);
            var maxB=lineSpace(posB.x1,posB.y1,posB.x3,posB.y3);
            var maxA=Math.max(lineA1,lineA2,lineA3,lineA4);
            var minA=Math.min(lineA1,lineA2,lineA3,lineA4);
            if(maxB<minA || maxA<0){
                return false;
            }
            /*使用B13的法向量*/
            sh.x=posB.y3-posB.y1;
            sh.y=posB.x1-posB.x3;
            sh.x1=posA.x1-posA.x3;
            sh.y1=posA.y1-posA.y3;
            var lineA1=getShadow(sh);
            sh.x1=posA.x1-posA.x4;
            sh.y1=posA.y1-posA.y4;
            var lineA2=getShadow(sh);
            sh.x1=posA.x2-posA.x3;
            sh.y1=posA.y2-posA.y3;
            var lineA3=getShadow(sh);
            sh.x1=posA.x2-posA.x4;
            sh.y1=posA.y2-posA.y4;
            var lineA4=getShadow(sh);
            var maxB=lineSpace(posB.x1,posB.y1,posB.x4,posB.y4);
            var maxA=Math.max(lineA1,lineA2,lineA3,lineA4);
            var minA=Math.min(lineA1,lineA2,lineA3,lineA4);
            if(maxB<minA || maxA<0){
                return false;
            }
 
 
            return true;
        }
 
 
    }
    /*shadow:{法向量:x,y,投影向量:x1,y1}*/
    function getShadow(sh){
        var temp1,temp2,length;
        temp1=sh.x*sh.y1+sh.y*sh.x1;
        temp2=Math.sqrt(sh.x*sh.x+sh.y*sh.y);
        length=temp1/temp2;
        return length;
    }
    function simpleTest(crossPos){
        //console.log(['crossPos',crossPos]);
        /*左上點*/
        var maxx=Math.max(crossPos.x1,crossPos.x3);
        var maxy=Math.max(crossPos.y1,crossPos.y3);
        /*右下點*/
        var minx=Math.min(crossPos.x2,crossPos.x4);
        var miny=Math.min(crossPos.y2,crossPos.y4);
        if(maxx>minx || maxy>miny){
            return false;
        }else{
            return true;
        }
    }
 
    function pointToLine(x0,y0,x1,y1,x2,y2){
        var space=0;
        var a=lineSpace(x1,y1,x2,y2);//線段長度
        var b=lineSpace(x0,y0,x1,y1);//(x1,y1)到點的距離
        var c=lineSpace(x0,y0,x2,y2);//(x2,y2)到點的距離
         
        if(c+b===a){
            space=0;
            return space;
        }
        if(a<=0.000001){
            alert('線段太短了');
            return;
        }
        var p=(a+b+c)/2;
        var s=Math.sqrt(p*(p-a)*(p-b)*(p-c));
        space=2*s/a;
        return space;
    }
    function lineSpace(x1,y1,x2,y2){
        var lineLength=Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
        return lineLength;
    }
</script>
<style type="text/css">
  canvas { border: 1px solid black; }
</style>
</head>
<body style="background:#eeeeee;">
    <canvas id="tutorial" width="1000" height="550" style="z-index:100;display:block;position:absolute;"></canvas>
</body>
</html>
非人類的科技!!!!! 已收藏,誰都別攔我!!!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章