原生js實現Canvas實現拖拽式繪圖,支持畫筆、線條、箭頭、三角形和圓形等等圖形繪製功能

1、實現的功能
一、基於oop思想構建,支持座標點、線條(由座標點組成,包含方向)、多邊形(由多個座標點組成)、圓形(包含圓心座標點和半徑)等實體canvas

二、原生JavaScript實現,不依賴任何第三方js庫和插件數組

三、多圖形繪製(支持畫筆、線條、箭頭、三角形、矩形、平行四邊形、梯形以及多邊形和圓形繪製)瀏覽器

四、拖拽式繪製(鼠標移動過程當中不斷進行canvas重繪)ssh

五、圖片繪製(做爲背景圖片時重繪會發生閃爍現象,暫時有點問題,後面繼續完善)異步

五、清空繪製功能ide

六、新版本優化繪製性能(使用共享座標變量數組,減小了大量的對象建立操做)工具

七、新版本支持箭頭繪製功能oop

2、完整實現代碼
DrawingTools =(function(){性能

//公共方法
            var getDom=function(id){return document.getElementById(id)};
            var isNull=function(s){return s==undefined||typeof(s)=='undefined'||s==null||s=='null'||s==''||s.length<1};
            var hideDefRM=function(){document.oncontextmenu=function(){return false}};//屏蔽瀏覽器默認鼠標事件
            /**繪圖容器*/
            var cbtCanvas;
            /**繪圖對象*/
            var cxt;
            /**繪製的圖形列表*/
            var shapes=new Array();

            var graphkind={'cursor':0,'pen':1,'line':2,'trian':3,'rect':4,'poly':5,'circle':6,'arrow':21,'parallel':41,'trapezoid':42};
            //背景圖片繪製配置
            var bgPictureConfig={
                pic:null,//背景圖片地址或路徑
                repaint:true,//是否做爲永久背景圖,每次清除時會進行重繪
            };
            //加載並繪製圖片(src:圖片路徑或地址),默認重繪背景圖
            var loadPicture=function(src){
                if(isNull(bgPictureConfig.repaint)||bgPictureConfig.repaint){bgPictureConfig.pic=src}
                var img = new Image();
                img.onload = function(){cxt.drawImage(img,0,0)}
                img.src =src;
            }

            //繪圖基礎配置
            var paintConfig={lineWidth:1,//線條寬度,默認1
                strokeStyle:'red',//畫筆顏色,默認紅色
                fillStyle:'red',//填充色
                lineJoin:"round",//線條交角樣式,默認圓角
                lineCap:"round",//線條結束樣式,默認圓角
            };
            //從新載入繪製樣式
            var resetStyle=function(){
                cxt.strokeStyle=paintConfig.strokeStyle;
                cxt.lineWidth=paintConfig.lineWidth;
                cxt.lineJoin=paintConfig.lineJoin;
                cxt.lineCap=paintConfig.lineCap;
                cxt.fillStyle=paintConfig.fillStyle;
            }

            //鼠標圖形
            var cursors=['crosshair','pointer'];
            /** 切換鼠標樣式*/
            var switchCorser=function(b){
                cbtCanvas.style.cursor=((isNull(b)?isDrawing():b)?cursors[0]:cursors[1]);
            }
            //操做控制變量組
            var ctrlConfig={
                kind:0,//當前繪畫分類
                isPainting:false,//是否開始繪製
                startPoint:null,//起始點
                cuGraph:null,//當前繪製的圖像
                cuPoint:null,//當前臨時座標點,肯定一個座標點後從新構建
                cuAngle:null,//當前箭頭角度
                vertex:[],//座標點
            }
            /**獲取當前座標點*/
            var getCuPoint=function(i){
                return ctrlConfig.cuPoint[i];
            }
            /**設置當前座標點*/
            var setCuPoint=function(p,i){
                return ctrlConfig.cuPoint[i]=p;
            }
            /**設置當前臨時座標點值*/
            var setCuPointXY=function(x,y,i){
                if(isNull(ctrlConfig.cuPoint)){
                    var arr=new Array();
                    arr[i]=new Point(x,y);
                    ctrlConfig.cuPoint=arr;
                }else if(isNull(ctrlConfig.cuPoint[i])){
                    setCuPoint(new Point(x,y),i);
                }else{
                    ctrlConfig.cuPoint[i].setXY(x,y);
                }
                return getCuPoint(i);
            }

            /**是否正在繪製*/
            var isDrawing=function (){
                return ctrlConfig.isPainting;
            }
            /**開始繪製狀態*/
            var beginDrawing=function(){
                ctrlConfig.isPainting=true;
            }
            /**結束繪製狀態*/
            var stopDrawing=function(){
                ctrlConfig.isPainting=false;
            }
            /**是否有開始座標點*/
            var hasStartPoint=function(){
                return !isNull(ctrlConfig.startPoint);
            }
            /**設置當前繪製的圖形*/
            var setCuGraph=function(g){
                ctrlConfig.cuGraph=g;
            }
            /**獲取當前繪製的圖形*/
            var getCuGraph=function(){
                return ctrlConfig.cuGraph;
            }
            /**設置開始座標點(線條的起始點,三角形的頂點,圓形的圓心,四邊形的左上角或右下角,多邊形的起始點)*/
            var setStartPoint=function(p){
                ctrlConfig.startPoint=p;
            }
            /**獲取開始座標點*/
            var getStartPoint=function(){
                return ctrlConfig.startPoint;
            }

            /**清空所有*/
            var clearAll=function(){
                cxt.clearRect(0,0,cbtCanvas.width,cbtCanvas.height);
            }
            /**重繪*/
            var repaint=function(){
                clearAll();
                /*
                if(bgPictureConfig.repaint){
                    loadPicture(bgPictureConfig.pic);
                }*/
            }

            /**點(座標,繪圖的基本要素,包含x,y座標)*/
            var Point=(function(x1,y1){
                var x=x1,y=y1;
                return{
                    set:function(p){
                        x=p.x,y=p.y;
                    },
                    setXY:function(x2,y2){
                        x=x2;y=y2;
                    },
                    setX:function(x3){
                        x=x3;
                    },
                    setY:function(y3){
                        y=y3;
                    },
                    getX:function(){
                        return x;
                    },
                    getY:function(){
                        return y;
                    }
                }
            });
            /**多角形(三角形、矩形、多邊形),由多個點組成*/
            var Poly=(function(ps1){
                var ps=isNull(ps1)?new Array():ps1;
                var size=ps.length;
                return{
                    set:function(ps2){
                        ps=ps2;
                    },
                    getSize:function(){
                        return size;
                    },
                    setPoint:function(p,i){
                        if(isNull(p)&&isNaN(i)){
                            return;
                        }
                        ps[i]=p;
                    },
                    setStart:function(p1){
                        if(isNull(ps)){
                            ps=new Array();
                            return ps.push(p1);
                        }else{
                            ps[0]=p1;
                        }
                    },
                    add:function(p){
                        if(isNull(ps)){
                            ps=new Array();
                        }
                        return ps.push(p);
                    },
                    pop:function(){
                        if(isNull(ps)){
                            return;
                        }
                        return ps.pop();
                    },
                    shift:function(){
                        if(isNull(ps)){
                            return;
                        }
                        return ps.shift;
                    },
                    get:function(){
                        if(isNull(ps)){
                            return null;
                        }
                        return ps;
                    },
                    draw:function(){
                        cxt.beginPath();
                        for(i in ps){
                            if(i==0){
                                cxt.moveTo(ps[i].getX(),ps[i].getY());
                            }else{
                                cxt.lineTo(ps[i].getX(),ps[i].getY());
                            }
                        }
                        cxt.closePath();
                        cxt.stroke();
                    }
                }
            });
            /*線條(由兩個點組成,包含方向)*/
            var Line=(function(p1,p2,al){
                var start=p1,end=p2,angle=al;

                var drawLine=function(){
                    cxt.beginPath();
                    cxt.moveTo(p1.getX(),p1.getY());
                    cxt.lineTo(p2.getX(),p2.getY());
                    cxt.stroke();
                }
                //畫箭頭
                var drawArrow=function() {
                    var vertex =ctrlConfig.vertex;
                    var x1=p1.getX(),y1=p1.getY(),x2=p2.getX(),y2=p2.getY();
                    var el=50,al=15;
                    //計算箭頭底邊兩個點(開始點,結束點,兩邊角度,箭頭角度)
                    vertex[0] = x1,vertex[1] = y1, vertex[6] = x2,vertex[7] = y2;
                    //計算起點座標與X軸之間的夾角角度值
                    var angle = Math.atan2(y2 - y1, x2 - x1) / Math.PI * 180;
                    var x = x2 - x1,y = y2 - y1,length = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
                    if (length < 250) {
                        el/=2,al/2;
                    }else if(length<500){
                        el*=length/500,al*=length/500;
                    }
                    vertex[8] = x2 - el * Math.cos(Math.PI / 180 * (angle + al));
                    vertex[9] = y2- el * Math.sin(Math.PI / 180 * (angle + al));
                    vertex[4] = x2- el* Math.cos(Math.PI / 180 * (angle - al));
                    vertex[5] = y2 - el * Math.sin(Math.PI / 180 * (angle - al));
                    //獲取另外兩個頂點座標
                    x=(vertex[4]+vertex[8])/2,y=(vertex[5]+vertex[9])/2;
                    vertex[2] = (vertex[4] + x) / 2;
                    vertex[3] = (vertex[5] + y) / 2;
                    vertex[10] = (vertex[8] +x) / 2;
                    vertex[11] = (vertex[9] +y) / 2;
                    //計算完成,開始繪製
                    cxt.beginPath();
                    cxt.moveTo(vertex[0], vertex[1]);
                    cxt.lineTo(vertex[2], vertex[3]);
                    cxt.lineTo(vertex[4], vertex[5]);
                    cxt.lineTo(vertex[6], vertex[7]);
                    cxt.lineTo(vertex[8], vertex[9]);
                    cxt.lineTo(vertex[10], vertex[11]);
                    cxt.closePath();
                    cxt.fill();
                    cxt.stroke();
                }
                return{
                    setStart:function(s){
                        start=s;
                    },
                    setEnd:function(e){
                        end=e;
                    },
                    getStart:function(){
                        return start;
                    },
                    getEnd:function(){
                        return end;
                    },
                    draw:function(){
                        if(angle){
                            drawArrow();
                        }else{
                            drawLine();
                        }
                    }
                }
            });
            /**圓形(包含圓心點和半徑)*/
            var Circle=(function(arr){
                //包含起始點(圓心)和結束點,以及圓半徑
                var startPoint=arr.start,endPoint=arr.end,radius=arr.radius;
                /*繪製圓*/
                var drawCircle=function(){
                    cxt.beginPath();
                    var x=startPoint.getX();
                    var y=startPoint.getY();
                    if(isNull(radius)){
                        radius=calculateRadius(startPoint,endPoint);
                    }
                    //x,y,半徑,開始點,結束點,順時針/逆時針
                    cxt.arc(x,y,radius,0,Math.PI*2,false); // 繪製圓
                    cxt.stroke();
                }
                //計算圓半徑
                var calculateRadius=function(p1,p2){
                    var width=p2.getX()-p1.getX();
                    var height=p2.getY()-p1.getY();
                    //若是是負數
                    if(width<0||height<0){
                        width=Math.abs(width);
                    }
                    //計算兩點距離=平方根(width^2+height^2)
                    c=Math.sqrt(Math.pow(width,2)+Math.pow(height,2));
                    return c;
                }
                return{
                    set:function(params){
                        startPoint=params.start;
                        endPoint=params.end;
                        radius=params.radius;
                    },
                    setPoint:function(p1){
                        p=p1;
                    },
                    getPoint:function(){
                        return p;
                    },
                    setRadius:function(r1){
                        radius=r1;
                    },
                    getRadius:function(){
                        return radius;
                    },
                    calcRadius:calculateRadius,
                    //繪製
                    draw:drawCircle,
                }
            });
            /**繪製線條工具方法*/
            var drawLine=function(p){
                cxt.beginPath();
                cxt.moveTo(startPosition.getX(),startPosition.getY());
                cxt.lineTo(p.getX(),p.getY());
                cxt.stroke();
            }

            /**繪製三角形工具方法*/
            var drawTrian=function(ps){
                cxt.beginPath();
                var a=ps.get();
                cxt.moveTo(a[0].getX(),a[0].getY());
                cxt.lineTo(a[1].getX(),a[1].getY());
                cxt.lineTo(a[2].getX(),a[2].getY());
                cxt.closePath();
                cxt.stroke();
            }

            /**繪製矩形工具方法*/
            var drawRect=function(p2){
                var p=getStartPoint();
                var width=p.getX()-p2.getX();
                var height=p.getY()-p2.getY();
                cxt.beginPath();
                cxt.strokeRect(x,y,width,height);//繪製矩形
            }

            /*繪製多邊形工具方法*/
            var drawpolygon=function(ps){
                if(ps.length>1){//保證只有兩個座標點纔是矩形
                    cxt.beginPath();
                    var p=ctrlConfig.startPoint;
                    var x=p.getX();
                    var y=p.getY();
                    cxt.moveTo(x,y);
                    for(p1 in ps){
                        cxt.lineTo(p1.getX(),p1.getY());
                    }
                    cxt.stroke();
                }
            }

            /*繪製圓角矩形工具方法*/
            var  drawRoundedRect=function(x,y,width,height,radius){
                cxt.beginPath();
                cxt.moveTo(x,y+radius);
                cxt.lineTo(x,y+height-radius);
                cxt.quadraticCurveTo(x,y+height,x+radius,y+height);
                cxt.lineTo(x+width-radius,y+height);
                cxt.quadraticCurveTo(x+width,y+height,x+width,y+height-radius);
                cxt.lineTo(x+width,y+radius);
                cxt.quadraticCurveTo(x+width,y,x+width-radius,y);
                cxt.lineTo(x+radius,y);
                cxt.quadraticCurveTo(x,y,x,y+radius);
                cxt.stroke();
            }
            /*繪製圓工具方法*/
            var drawCircle=function(c){
                var p=c.getPoint();//座標點
                var x=p.getX();
                var y=p.getY();
                var r=c.getRadius();
                cxt.beginPath();
                //x,y,半徑,開始點,結束點,順時針/逆時針
                cxt.arc(x,y,r,0,Math.PI*2,false); // 繪製圓
                cxt.stroke();
            }
            //計算圓半徑工具方法
            var calculateRadius=function(p1,p2){
                var width=p2.getX()-p1.getX();
                var height=p2.getY()-p1.getY();
                //若是是負數
                if(width<0||height<0){
                    width=Math.abs(width);
                }
                //計算兩點距離=平方根(width^2+height^2)
                c=Math.sqrt(Math.pow(width,2)+Math.pow(height,2));
                return c;
            }

            //鼠標按鍵點擊(首次點擊肯定開始座標點,拖動鼠標不斷進行圖形重繪)
            var mouseDown = function(e){
                var btnNum = e.button;
                if(btnNum==0){
                    console.log("選擇:"+ctrlConfig.kind);
                    //設置起始點
                    switch(ctrlConfig.kind){
                        case graphkind.pen://畫筆(不鬆開鼠標按鍵一直畫)
                            beginDrawing();//開始繪製
                            cxt.beginPath();
                            cxt.moveTo(e.offsetX,e.offsetY);
                            break;
                        case graphkind.poly://多邊形
                            var p=new Point(e.offsetX,e.offsetY);
                            if(isDrawing()){
                                getCuGraph().add(p);//添加到
                            }else{//第一次肯定開始座標
                                beginDrawing();//開始繪製
                                setStartPoint(p);
                                var poly=new Poly();
                                poly.add(p);
                                setCuGraph(poly);//設置當前繪製圖形
                            }
                            break;
                        case graphkind.line://線條
                        case graphkind.arrow://方向
                        case graphkind.trian://三角形
                        case graphkind.rect://矩形
                        case graphkind.parallel://平行四邊形
                        case graphkind.trapezoid://梯形
                            beginDrawing();//開始繪製
                            var p=new Point(e.offsetX,e.offsetY);
                            setStartPoint(p);
                            var poly=new Poly();
                            poly.add(p);
                            setCuGraph(poly);//設置當前繪製圖形
                            break;
                        case graphkind.circle://圓
                            console.log("肯定圖形繪製開始座標點:"+e.offsetX+","+e.offsetY);//點擊肯定圖形的開始座標點
                            beginDrawing();//開始繪製
                            var p=new Point(e.offsetX,e.offsetY);
                            setStartPoint(p);
                            var circle= new Circle({'start':p});
                            setCuGraph(circle);
                            break;
                        case ctrlConfig.cursor: //手型鼠標
                        default://默認是手型鼠標,不容許繪製
                    }
                }else if(btnNum==2){
                    console.log("右鍵因爲結束多邊形繪製");
                    if(isDrawing()){
                        if(ctrlConfig.kind==graphkind.poly){
                            repaint();
                            getCuGraph().draw();
                            stopDrawing();//結束繪製
                        }
                    }
                }
                hideDefRM();//屏蔽瀏覽器默認事件
            }
            //鼠標移動(拖動,根據鼠標移動的位置不斷重繪圖形)
            var mouseMove = function(e){
                if(isDrawing()&&hasStartPoint()){//檢查是否開始繪製,檢查是否有開始座標點
                    //畫筆不須要重繪
                    if(ctrlConfig.kind>1){
                        repaint();//重繪
                    }
                    var p=setCuPointXY(e.offsetX,e.offsetY,0);//設置共享的臨時座標點,用於防止重複建立對象
                    switch(ctrlConfig.kind){
                        case graphkind.pen://畫筆(一直畫)
                            cxt.lineTo(e.offsetX,e.offsetY);
                            cxt.stroke();
                            break;
                        case graphkind.poly://多邊形
                            var poly=getCuGraph(poly);
                            var size=poly.getSize();
                            poly.setPoint(p,(size-1));
                            poly.draw();
                            break;
                        case graphkind.line://線條
                            var line=new Line(getStartPoint(),p,false);
                            ctrlConfig.cuGraph=line;
                            line.draw();
                            break;
                        case graphkind.arrow://方向
                            var line=new Line(getStartPoint(),p,true);
                            ctrlConfig.cuGraph=line;
                            line.draw();
                            break;
                        case graphkind.trian://三角形
                            var lu=getStartPoint();
                            var x2=p.getX();
                            var x1=lu.getX();
                            //三角形左邊的點座標計算方法:(x1-(x2-x1),y2)
                            var x3=x1-(x2-x1);
                            var l=setCuPointXY(x3,p.getY(),1);//設置共享的臨時座標點,用於防止重複建立對象
                            var poly=getCuGraph();//獲取當前圖形
                            poly.set([lu,p,l]);
                            poly.draw();//即時繪製
                            break;
                        case graphkind.parallel://平行四邊形
                            var lu=getStartPoint();
                            var x3=p.getX();
                            var x1=lu.getX();
                            //平行四邊形兩個未知座標點計算方法:(x1-(x3-x1),y3),(x1+(x3-x1),y1)
                            var x2=x3+(x3-x1);
                            var x4=x1-(x3-x1);
                            var ld=setCuPointXY(x2,lu.getY(),1);//設置共享的臨時座標點,用於防止重複建立對象
                            var ru=setCuPointXY(x4,p.getY(),2);//設置共享的臨時座標點,用於防止重複建立對象
                            var poly=getCuGraph();//獲取當前圖形
                            poly.set([lu,ru,p,ld]);
                            poly.draw();//即時繪製
                            break;
                        case graphkind.trapezoid://梯形
                            var lu=getStartPoint();
                            var x3=p.getX();
                            var x1=lu.getX();
                            //梯形兩個未知座標點計算方法:(x3-(x3-x1)/2,y1),(x1-(x3-x1)/2,y3)
                            var x2=x3-(x3-x1)/2;
                            var x4=x1-(x3-x1)/2;
                            var ld=setCuPointXY(x2,lu.getY(),1);
                            var ru=setCuPointXY(x4,p.getY(),2);
                            var poly=getCuGraph();
                            poly.set([lu,ru,p,ld]);
                            poly.draw();
                            break;
                        case graphkind.rect://矩形
                            var lu=getStartPoint();
                            //矩形右上角和左上角座標計算方法
                            var ld=setCuPointXY(lu.getX(),p.getY(),1);
                            var ru=setCuPointXY(p.getX(),lu.getY(),2);
                            var poly=getCuGraph();
                            poly.set([lu,ru,p,ld]);
                            poly.draw();
                            break;
                        case graphkind.circle://圓
                            var circle=getCuGraph();//獲取當前圖形
                            circle.set({'start':getStartPoint(),'end':p});
                            circle.draw();//即時繪製
                            break;
                    }
                }
            }
            //鼠標按鍵鬆開
            var mouseUp = function(e){
                if(isDrawing()){
                    //console.log("鬆開鼠標按鍵:"+e.offsetX+","+e.offsetY);
                    //畫筆不須要重繪
                    if(ctrlConfig.kind>1){
                        repaint();
                        getCuGraph().draw();
                    }
                    if(ctrlConfig.kind!=graphkind.poly){//多邊形繪製鼠標按鍵鬆開不結束繪製,多邊形只有右鍵點擊才能結束繪製
                        stopDrawing();//結束繪製
                    }
                }
            }

            //鼠標移出
            var mouseOut = function(e){
                console.log("鼠標移出繪製區域"+e.offsetX+","+e.offsetY);
                if(isDrawing()){
                    console.log("中止繪製");
                    if(ctrlConfig.kind>1){
                        repaint();
                        getCuGraph().draw();
                    }
                    stopDrawing();//中止繪製
                }
            }

            return{
                isNull:isNull,
                getDom:getDom,
                clear:function(){
                    stopDrawing();//中止繪製
                    repaint();
                },
                /**初始化*/
                init:function(params){
                    cbtCanvas=getDom(params.id);
                    //瀏覽器是否支持Canvas
                    if (cbtCanvas.getContext){
                        /**繪圖對象*/
                        cxt=cbtCanvas.getContext("2d");
                        cbtCanvas.onmousedown = mouseDown;
                        cbtCanvas.onmouseup = mouseUp;
                        cbtCanvas.onmousemove = mouseMove;
                        cbtCanvas.onmouseout = mouseOut;
                        resetStyle();//載入樣式
                        return true;
                    }else{
                        return false;
                    }
                },
                /**設置背景圖片*/
                setBgPic:loadPicture,
                /**選擇圖形類型*/
                begin:function(k){
                    console.log("選擇繪製圖形:"+k);
                    if(isNaN(k)){//若是不是數字,先轉換爲對應字符
                        ctrlConfig.kind=kind[k];
                    }else{
                        ctrlConfig.kind=k;
                    }
                    switchCorser(true);//切換鼠標樣式
                },
                /*手型,並中止繪圖*/
                hand:function(){
                    ctrlConfig.kind=0;
                    stopDrawing();//中止繪製
                    switchCorser(false);//切換鼠標樣式
                }
            }
        })

3、使用方式
一、圖形類型優化

0:鼠標,1:畫筆,2:線條,3:三角形,4:矩形,5:多邊形,6:圓形,21:箭頭,41:平行四邊形,42:梯形
var graphkind={'cursor':0,'pen':1,'line':2,'trian':3,'rect':4,'poly':5,'circle':6,'arrow':21,'parallel':41,'trapezoid':42};

二、初始化以及使用背景圖片和畫筆選擇

var drawUtil=new DrawingTools();
//初始化,(若是瀏覽器不支持H5,會初始化失敗,返回false)
if(drawUtil.init({'id':'calibrationCanvas'})){

//加載圖片
var imgsrc='圖片地址';
if(!drawUtil.isNull(imgsrc)){
    drawUtil.setBgPic(imgsrc,true);//設置背景圖片(異步加載圖片)
}

}
drawUtil.begin(1);//選擇畫筆
二、繪製箭頭

drawUtil.begin(21);

相關文章
相關標籤/搜索