Canvas---Canvas版画图、圆角矩形、圆形、矩形、图形填充、mvc模式从新整合代码版



使用Canvas实现画图程序。(转载注明出处与做者啊)javascript

本次主要更新了一下功能:css

新添加图形:矩形,圆形,圆角矩形html

实现了描边颜色与填充颜色的选择java

使用相似mvc的模式从新整理了代码。json


M层: 特殊图形的路径规划代码组成,只包含路径规划,无样式设计。canvas

V层:设计样式,调用路径规划代码,实际绘制图案。在这里完成填充、描边。mvc

C层:判断选择框内容,调用不一样的实际绘制代码ide

其余:事件处理与辅助计算函数等函数


不足:感受代码结构仍是不够好,果真是我没用面向对象么?ui

下面是效果图:

下面是源代码:

①本体HTML5代码

<!docutype html>
<!--date 2015-1-27-- by MIKUScallion 首发于CSDN>
<html>
    <head>
        <meta http-equiv="content-type" content="text/html; charset=utf-8" />
        <style type="text/css">
            canvas{
                background-color: rgb(247, 247, 247);
                margin-top: 10px;
            }
        </style>
    </head>
    <body>
        <div class="controls">
            图案类型<select id="lineTypeSelectBox">
            <option value="solid">实线</option>
            <option value="dashed">虚线</option>
            <option value="arc">圆形</option>
            <option value="rect">矩形</option>
            <option value="roundedRect">圆角矩形</option>
            </select>
            描边颜色<select id="strokeColorSelectBox">
                    <option value="black">black</option>
                    <option value="red">red</option>
                    <option value="blue">blue</option>
                    <option value="rgba(255, 0, 0, 0)">null</option>
            </select>
            填充颜色<select id="fillColorSelectBox">
                    <option value="rgba(255, 0, 0, 0)">null</option>
                    <option value="#7dce96">green</option>
                    <option value="#db884b">orange</option>
                    <option value="#d88ba9">pink</option>
            </select>
            描边宽度<select id="lineWidthSelectBox">
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="4">4</option>
            </select>
            
            网格线<input  id="gridCheckBox" type="checkbox" checked="checked" >
            坐标轴<input  id="axesCheckBox" type="checkbox" checked="checked" >
            辅助线<input  id="guideWiresCheckBox" type="checkbox" checked="checked" >
            <input type="button" value="清除画布" id="eraseAllButton">
        </div>
        <!--canvas默认大小300*150-->
        <canvas id="canvas" width="800" height="500">
        </canvas>
    </body>
        <!--导入特殊图形路径规划js文件-->
        <script type = "text/javascript" src ="mikuCanvasShapePath.js"></script>
        <!--导入主要绘图js文件-->
        <script type = "text/javascript" src ="mikuCanvasPaintAppMain.js"></script>
</html>

②特殊图形路径规划代码

//虚线
function dashedLinePP(context,x1,y1,x2,y2,dashLength){
    //水平长度
    var deltaX = x2 - x1;
    //垂直长度
    var deltaY = y2 - y1;
    //虚线数量
    var numDashed = Math.floor(
        Math.sqrt(deltaX*deltaX+deltaY*deltaY)/dashLength
    );
    
    //开始路径规划
    context.beginPath();
    for(var i=0; i<numDashed;i++){
    //这种写法太强大了
    //(deltaX/numDashed)是指虚线的长度
    context[i%2===0 ? "moveTo":"lineTo"](x1+(deltaX/numDashed)*i,y1+(deltaY/numDashed)*i);
    }
}
//圆角矩形
function roundedRectPP(context,cornerX,cornerY,width,height,cornerRadius){
    //开始路径规划
    context.beginPath();
    //设置开始位置(不是顶点是为了避免产生小尾巴)
    //为了避免产生小尾巴作判断
    if(width > 0){
        context.moveTo(cornerX+cornerRadius,cornerY);    
    }else{
        context.moveTo(cornerX-cornerRadius,cornerY);   
    }
    //右上角
    context.arcTo(cornerX+width,cornerY,cornerX+width,cornerY+height,cornerRadius);
    //右下角(注意:因为arcTo的移动,当前点已经到:cornerX+width,cornerY+cornerRadius,因此不要再设置moveTo)
    context.arcTo(cornerX+width,cornerY+height,cornerX,cornerY+height,cornerRadius);
    //左下角
    context.arcTo(cornerX,cornerY+height,cornerX,cornerY,cornerRadius);
    //左上角
    if(width > 0){
        context.arcTo(cornerX,cornerY,cornerX+cornerRadius,cornerY,cornerRadius);   
    }else{
        context.arcTo(cornerX,cornerY,cornerX-cornerRadius,cornerY,cornerRadius); 
    }
}
//网格线
function gridPP(context,stepx,stepy){
    //开始路径规划
    context.beginPath();
    //绘制x轴网格
    //注意:canvas在两个像素的边界处画线
    //因为定位机制,1px的线会变成2px
    //因而要+0.5
    for(var i=stepx+0.5;i<context.canvas.width;i=i+stepx){
        //开启路径
        //context.beginPath();
        context.moveTo(i,0);
        context.lineTo(i,context.canvas.height);
        }
    //绘制y轴网格
    for(var i=stepy+0.5;i<context.canvas.height;i=i+stepy){
        //context.beginPath();
        context.moveTo(0,i);
        context.lineTo(context.canvas.width,i);
        }
};
//坐标轴
function axesPP(context,axesMargin,htSpace,vtSpace,tickLong){
    function mikuLoc(locX,locY){
        this.x = locX;
        this.y = locY;
    }
    var originLoc = new mikuLoc(axesMargin, context.canvas.height-axesMargin);
    var axesW = context.canvas.width  - (axesMargin*2);
    var axesH = context.canvas.height - (axesMargin*2);
    context.beginPath();
    //x,y轴规划
    horizontalAxisPP();
    verticalAxisPP();
    //x,y轴标签规划
    verticalAxisTicksPP();
    horizontalAxisTicksPP();
    
    function horizontalAxisPP(){
        context.moveTo(originLoc.x, originLoc.y);
        context.lineTo(originLoc.x + axesW, originLoc.y);
    }
    function verticalAxisPP(){
        context.moveTo(originLoc.x, originLoc.y);
        context.lineTo(originLoc.x, originLoc.y - axesH);
    }
    
    function verticalAxisTicksPP(){
        var deltaX;
        //当前垂直tick的y轴坐标
        var nowTickY =originLoc.y-vtSpace;
        for(var i=1;i<=(axesH/vtSpace);i++){
            if(i%5 === 0){
                deltaX=tickLong;
            }else {
                deltaX=tickLong/2;   
            }
            //移动到当前的tick起点
            context.moveTo(originLoc.x-deltaX,nowTickY);
            context.lineTo(originLoc.x+deltaX,nowTickY);
            nowTickY=nowTickY-vtSpace;
        }
    }
    function horizontalAxisTicksPP(){
        var deltaY;
        var nowTickX = originLoc.x+htSpace;
         for(var i=1;i<=(axesW/htSpace);i++){
            if(i%5 === 0){
                deltaY = tickLong;
            }else{
                deltaY = tickLong/2;   
            }
            context.moveTo(nowTickX,originLoc.y+deltaY);
            context.lineTo(nowTickX,originLoc.y-deltaY);
            nowTickX = nowTickX + htSpace;
        }
    }
};
//辅助定位线
function guideWiresPP(context,x,y){
    context.beginPath();
    drawHorizontalLine(y);
    drawVerticalLine(x);
    
    function drawHorizontalLine(y){
        context.moveTo(0,y+0.5);
        context.lineTo(context.canvas.width,y+0.5);
    }
    function drawVerticalLine(x){
        context.moveTo(x+0.5,0);
        context.lineTo(x+0.5,context.canvas.height);
    }
}

③绘图软件主代码

//Vars--------------------------------------------------------
        var canvas =document.getElementById("canvas"),
            context =canvas.getContext("2d"),
            //正在绘制的绘图表面变量
            drawingSurfaceImageData,
            //鼠标按下相关对象
            mousedown = {},
            //橡皮筋矩形对象
            rubberbandRect = {},
            //当前鼠标坐标
            loc ={},
            //拖动标识变量
            dragging = false;
        
        //控件
        //擦除画布的控制
        var eraseAllButton = document.getElementById("eraseAllButton");
        //坐标轴的控制
        var axesCheckBox = document.getElementById("axesCheckBox");
        //网格线的控制
        var gridCheckBox = document.getElementById("gridCheckBox");
        //辅助线的控制
        var guideWiresCheckBox = document.getElementById("guideWiresCheckBox"); 
        //线条颜色的控制
        var strokeColorSelectBox =document.getElementById("strokeColorSelectBox");
        //线条样式的控制
        var lineTypeSelectBox = document.getElementById("lineTypeSelectBox");
        //线条宽度的控制
        var lineWidthSelectBox = document.getElementById("lineWidthSelectBox");
        //图案填充色控制
        var fillColorSelectBox = document.getElementById("fillColorSelectBox");
//辅助函数--------------------------------------------------------------------------
        //将窗口坐标转换为Canvas坐标
        function windowToCanvas(x,y){
            //获取canvas元素的边距对象
            var bbox = canvas.getBoundingClientRect();
            //返回一个坐标对象
            //相似json的一种写法
            return {
                    x : x - bbox.left*(canvas.width/bbox.width),
                    y : y - bbox.top*(canvas.height/bbox.height)
                };
        }

        //保存当前绘图表面数据
        function saveDrawingSurface(){
          //从上下文中获取绘图表面数据
          drawingSurfaceImageData = context.getImageData(0,0,canvas.width,canvas.height);
        }

        //还原当前绘图表面
        function restoreDrawingSurface(){
          //将绘图表面数据还原给上下文
          context.putImageData(drawingSurfaceImageData,0,0);
        }

        //更新橡皮筋矩形
        function updateRubberbandRectangle(){
            //得到矩形的宽
            rubberbandRect.width = Math.abs(loc.x - mousedown.x);
            //得到矩形的高
            rubberbandRect.height = Math.abs(loc.y - mousedown.y);
            //得到矩形顶点的位置(left,top)
            //若是鼠标按下的点(起点)在当前点的的左侧
            //这里画一下图就懂了
            if(loc.x > mousedown.x){
                rubberbandRect.left = mousedown.x;
            }else{
                rubberbandRect.left = loc.x;
            }
            if(loc.y > mousedown.y){
                rubberbandRect.top = mousedown.y;
            }else{
                rubberbandRect.top = loc.y;
            }
        }

        //绘制橡皮筋矩形的对角线(控制器)
        function drawRubberbandShape(){
            //获取当前线条类型
            var lineType = lineTypeSelectBox.value;
            //获取当前线条颜色
            var lineColor = strokeColorSelectBox.value;
            //获取当前线条宽度
            var lineWidth = lineWidthSelectBox.value;
            //获取当前填充颜色
            var fillColor = fillColorSelectBox.value;
            
            //有改变context画笔属性就要作画笔保护
            context.save();
            context.strokeStyle = lineColor;
            context.lineWidth = lineWidth;
            context.fillStyle = fillColor;
            
            //注意路径规划、样式规划分开
            if(lineType === "solid"){
                drawLineWithRubberbandRect();
            }else if(lineType === "dashed"){
                drawDashedLineWithRubberbandRect(5);
            }else if(lineType === "arc"){
                drawArcWithRubberbandRect();   
            }else if(lineType === "rect"){
                drawRectWithRubberbandRect();
            }else if(lineType === "roundedRect"){
                drawRoundedRectWithRubberbandRect(20);
            }
            context.restore();
        }

        //更新橡皮筋矩形+对角线
        function updateRubberband(){
            updateRubberbandRectangle();
            drawRubberbandShape();
        }

        //初始化函数
        function initialization(){
            //清除画布
            context.clearRect(0,0,canvas.width,canvas.height);
            //绘制网格与坐标的颜色是默认的
            if(axesCheckBox.checked){
               drawAxes(40);
            }
            if(gridCheckBox.checked){
               drawGrid(10,10);
            }
        }

//实际绘图函数------------------------------------------------------------------------------
        //利用橡皮筋矩形绘制直线
        function drawLineWithRubberbandRect(){
            context.beginPath();
            context.moveTo(mousedown.x,mousedown.y);
            context.lineTo(loc.x,loc.y);
            context.stroke();
        }

        //利用橡皮筋矩形绘制圆形
        function drawArcWithRubberbandRect(){
            //圆的半径
            var radius;
            var h = rubberbandRect.height;
            var w = rubberbandRect.width;
            //利用勾股定理计算斜边,就是半径
            radius=Math.sqrt(h*h+w*w);
            //实际绘制
            context.beginPath();
            context.arc(mousedown.x,mousedown.y,radius,0,Math.PI*2,false);
            context.stroke();
            context.fill();
        }

        //利用橡皮筋矩形绘制矩形(就是画本身)
        function drawRectWithRubberbandRect(){
            context.beginPath();
            context.rect(rubberbandRect.left,rubberbandRect.top,rubberbandRect.width,rubberbandRect.height);
            context.stroke();
            context.fill();
        }

        //利用橡皮筋矩形绘制虚线
        function drawDashedLineWithRubberbandRect(dashLength){
            //路线规划
            dashedLinePP(context,mousedown.x,mousedown.y,loc.x,loc.y,dashLength);
            context.stroke();
        }

        //利用橡皮筋矩形绘制圆角矩形
        function drawRoundedRectWithRubberbandRect(radius){
            //用橡皮筋矩形的数据,作圆角矩形的路径规划
            roundedRectPP(context,rubberbandRect.left,rubberbandRect.top,rubberbandRect.width,rubberbandRect.height,radius);
            context.stroke();
            context.fill();
        }

        //绘制网格线
        function drawGrid(stepx,stepy){
            //样式设置
            context.save();
            context.strokeStyle = "lightgray";
            context.lineWidth = 0.5;
            gridPP(context,stepx,stepy);
            context.stroke();
            context.restore();
        }

        //绘制坐标轴
        function drawAxes(axesMargin){
            context.save();
            context.strokeStyle = "#a2a2a2";
            context.lineWidth = 1;
            axesPP(context,40,10,10,10);
            context.stroke();
            context.restore();
        }

        //绘制辅助定位线
        function drawGuideWires(x,y){
            context.save();
            context.strokeStyle = "red";
            context.lineWidth = 0.5;
            guideWiresPP(context,x,y);
            context.stroke();
            context.restore();
        }

//Event Hander-----------------------------------------------------

        canvas.onmousedown = function(e){
           loc =windowToCanvas(e.clientX,e.clientY);
           e.preventDefault();
           saveDrawingSurface();
           mousedown.x = loc.x;
           mousedown.y = loc.y;
           dragging = true;
        }
        
        canvas.onmousemove = function(e){
            if(dragging){
                e.preventDefault();
                loc = windowToCanvas(e.clientX,e.clientY);
                restoreDrawingSurface();
                updateRubberband();
            }
            if(dragging&&guideWiresCheckBox.checked){
                drawGuideWires(loc.x,loc.y);   
            }
        }
        
        canvas.onmouseup = function(e){
            loc = windowToCanvas(e.clientX,e.clientY);
            restoreDrawingSurface();
            updateRubberband();
            dragging = false;
        }
        
        //须要擦除的操做须要从新初始化
        eraseAllButton.onclick = function(e){
            context.clearRect(0,0,canvas.width,canvas.height);
            initialization();
            saveDrawingSurface();
        }
        
        axesCheckBox.onchange = function(e){
            initialization();
        }
        
        gridCheckBox.onchange = function(e){
            initialization();
        }
        
//Mian----------------------------------------------
        
        initialization();