精华内容
参与话题
问答
  • 使用HTML5画布

    2017-03-19 22:21:33
    一、第一步:使用canvas控件确定画布 二、第二步:使用javascript绘制图形 1、脚本标签 2、步骤: 1)第一步:获取界面的画布 var canvas=document.getElementById("myCanvas");document代表从整个文档中...

    使用HTML5画布:

    一、第一步:使用canvas控件确定画布

    二、第二步:使用javascript绘制图形

    1、脚本标签

      <script type="text/javascript">
      </script>

    2、步骤:

    1)第一步:获取界面的画布

    var canvas=document.getElementById("myCanvas");
    document代表从整个文档中搜索;getElementById按照控件id来获取控件;var canvas定义一个变量来接收获取到的canvas控件

    2)第二步:获取画布的上下文对象(相当于画笔)

    var ctx=canvas.getContext("2d");
    2D代表绘制图形为2D
    3)第三步:绘制图形

    a. 在图形的开始和结束需要:

        ctx.beginPath();//开启路径
        .....//绘制具体的操作
        ctx.closePath();
    b.绘制直线:
        ctx.moveTo(20,20);//起点坐标:将上下文对象移动到40,40这个点
        ctx.lineTo(50,40);//终点坐标:将上下文对象移动到100,40这个点
    c.绘制矩形:

        ctx.rect(90,40,40,30);//100,100是左上角坐标,80,50指宽度和高度
        ctx.strokeStyle="red";//指定边框颜色
        ctx.stroke();//实现方式:绘制边界
        ctx.fillStyle="yellow";//填充内部颜色
        ctx.fill();
        //其他写法
        ctx.strokeRect(90,80,40,30);//绘制空心的矩形
        ctx.fillRect(100,120,60,50);//绘制实心矩形
    d.绘制圆形

        ctx.arc(100,180,20,0,Math.PI*2,false);
    其中,100,180圆心坐标,20是半径,0,Math.PI*2画一个整圆;false代表顺时针方向画圆,true代表逆时针方向画圆,起点固定在三点钟方向,HTML5画圆的实现方式:

    示例一:

    <!doctype html>
    <html>
      <head>
        <title>画布-canvas</title>
      </head>
    <body>
      <canvas id="myCanvas" width="300" height="300" style="border:1px solid red;"></canvas>
      <!--以下代码代表使用JavaScript绘制图形-->
      <script type="text/javascript">
        //第一步:获取界面的画布
        var canvas=document.getElementById("myCanvas");
        //第二步:获取画布的上下文对象
        var ctx=canvas.getContext("2d");
        //第三步:绘制图形
        //1、绘制直线
        ctx.beginPath();//开启路径
        ctx.moveTo(20,20);//起点坐标:将上下文对象移动到40,40这个点
        ctx.lineTo(50,40);//终点坐标:将上下文对象移动到100,40这个点
        //2、绘制三角形
        ctx.lineTo(80,50);
        ctx.lineTo(20,20);
        ctx.stroke();//实现绘制
        ctx.closePath();
        //3、绘制矩形
        ctx.beginPath();
        ctx.rect(90,40,40,30);//100,100是左上角坐标,80,50指宽度和高度
        ctx.strokeStyle="red";//指定边框颜色
        ctx.stroke();//实现方式:绘制边界
        ctx.fillStyle="yellow";//填充内部颜色
        ctx.fill();
        ctx.closePath();
        //其他写法
        ctx.beginPath();
        ctx.strokeRect(90,80,40,30);//绘制空心的矩形
        ctx.closePath();
        ctx.beginPath();
        ctx.fillRect(100,120,60,50);//绘制实心矩形
        ctx.closePath();
        //4、绘制圆形
        ctx.beginPath();
        ctx.arc(100,180,20,0,Math.PI*2,false);//100,180圆心坐标,20是半径,0,Math.PI*2画一个整圆
        ctx.stroke();
        ctx.closePath();
        ctx.beginPath();
        ctx.arc(100,220,20,0,Math.PI,false);//100,220圆心坐标,20是半径,0,Math.PI*2画一个半圆
        ctx.stroke();
        ctx.closePath();//回到起点
        ctx.beginPath();
        ctx.arc(200,220,20,0,Math.PI,true);//100,220圆心坐标,20是半径,0,Math.PI*2画一个半圆
        ctx.closePath();
        ctx.stroke();
      </script>
    </body>
    </html>
    效果如下:


    示例2:实现笑脸

    <!doctype html>
    <html>
      <head>
        <title>画布-canvas</title>
      </head>
    <body>
      <canvas id="myCanvas" width="200" height="200" style="border:1px solid red;"></canvas>
      <script type="text/javascript">
        var canvas=document.getElementById("myCanvas");
        var ctx=canvas.getContext("2d");
        //脸部
        ctx.beginPath();
        ctx.arc(100,100,60,0,Math.PI*2,false);
        ctx.stroke();
        ctx.closePath();
        //嘴巴
        ctx.beginPath();
        ctx.arc(100,115,20,0,Math.PI,false);
        ctx.closePath();
        ctx.stroke();
        //左眼睛
        ctx.beginPath();
        ctx.arc(80,85,12,0,Math.PI*2,false);
        ctx.stroke();
        ctx.closePath();
        //左眼珠
        ctx.beginPath();
        ctx.arc(80,85,5,0,Math.PI*2,false);
        ctx.stroke();
        ctx.fillStyle="black";
        ctx.fill();
        ctx.closePath();
        //右眼睛
        ctx.beginPath();
        ctx.arc(120,85,12,0,Math.PI*2,false);
        ctx.stroke();
        ctx.closePath();
        //右眼珠
        ctx.beginPath();
        ctx.arc(120,85,5,0,Math.PI*2,false);
        ctx.stroke();
        ctx.fillStyle="black";
        ctx.fill();
        ctx.closePath();
      </script>
    </body>
    </html>
    效果如下:


    三、画布实现艺术字的效果

    方法如下:

    <!doctype html>
    <html>
      <head>
        <title>画布-canvas</title>
      </head>
    <body>
      <canvas id="myCanvas" width="250" height="250" style="border:1px solid red;"></canvas>
      <script type="text/javascript">
        var canvas=document.getElementById("myCanvas");
        var ctx=canvas.getContext("2d");
        ctx.font="26px 宋体";//设置字体
        ctx.strokeText("青白江樱花节",10,50,150);//10,60为起点坐标;240代表字显示的宽度
        //添加渐变颜色 蓝色-->黄色-->红色
        //定义线性渐变,从左到右进行渐变,0,0指渐变的起点坐标,150,0是指渐变的终点坐标
        var gradient=ctx.createLinearGradient(0,0,150,0);
        gradient.addColorStop(0,"blue");
        gradient.addColorStop(0.5,"yellow");
        gradient.addColorStop(1,"red");
        //使用渐变:把颜色设置为渐变
        ctx.strokeStyle=gradient;
        ctx.strokeText("青白江樱花节",10,100,150);
    
      </script>
    </body>
    </html>
    效果如下:



    四、画布的特点

    绘制成的图形网页上认为是一张图片,可以保存。




    展开全文
  • JavaScript之Canvas画布

    万次阅读 多人点赞 2017-04-18 23:10:18
    HTML5有个强大的功能,就是利用canvas进行画图。

    canvas可以在页面中设定一个区域,再利用JavaScript动态地绘制图像。


    基本用法

    使用canvas元素,首先设置width和height属性,为其设置绘制区域的大小,   如果没有设置宽度和高度,是看不到元素的,不能在其区域画图,在开始标签和结束标签之间的信息是后备信息,当用户的浏览器不支持canvas元素时会显示,用于友好地提示用户。

    <canvas id="canvas" width="400" height="300">抱歉,您的浏览器不支持canvas元素</canvas>

    第一步:想要在canvas区域里画图,第一步需要取得绘图上下文,而取得上下文对象的引用,需要调用getContext()方法,并传入上下文参数"2d",这样就能取得2D上下文对象。

    注:在调用getContext()方法,需要检测浏览器是否支持该方法。传入的参数'2d',需要用单引用括起来。
    <body>
    
      <canvas id="canvas" width="400" height="300">抱歉,您的浏览器不支持canvas元素</canvas>
    
    <script>
        
        var canvas = document.getElementById("canvas");
        //检测浏览器是否支持canvas 该方法是否存在 取得上下文对象
        if (canvas.getContext) {
            var context = canvas.getContext('2d'); //2d用单引用括起来
        }
      
    </script>
    </body>
    


    使用toDataURL()方法,可导出绘制的图像。该方法可以获取绘制的图像的数据URL。并可以将该URL赋值给一个img元素,这样在页面中可以显示用canvas绘制的图像了。

    格式:canvas.toDateURL("image/png");

    注:浏览器默认将图像编码成png格式(除非另行指定)。
    <!doctype html>
    <html>
      <head>
        <meta charset="utf-8">
          <title>JavaScript之Canvas</title>
      </head>
      <body>
    	
        <canvas id="canvas" width="400" height="300">抱歉,您的浏览器不支持canvas元素</canvas>
    
    
      
      <script>
          
          var canvas = document.getElementById("canvas");
          //检测浏览器是否支持canvas 该方法是否存在 取得上下文对象
          if (canvas.getContext) {
              var context = canvas.getContext('2d'); //2d用单引用括起来
    		 
              //获取图像的数据URL
              var imgURL = canvas.toDataURL("image/png"); //默认图片格式为png,也可以自定义设置格式。
    		 
              //显示图片
              var image = document.createElement("img"); //添加一个img元素
    		 
              image.src = imgURL; //将获取的图像的URL赋值给src属性
              document.body.appendChild(image); //将元素添加到页面中
          }
    	  
      </script>
      </body>
    </html>
    


    在页面中的代码大概是这样的了:


    这样在页面中有了个img标签用于显示canvas绘制出的图像、




    2D上下文

    2D上下文的坐标起始于canvas的左上角,起始坐标为(0, 0),所有坐标值基于这个坐标原点计算,x越大越向右,越大越向下。

    绘制2D图像将进行三步:

    1、取得上下文对象(getContext("2d"))。
    2、指定填充(fiiStyle)或描边(strokeStyle)颜色。描边的话还可以其线条末端的形状、线条相交的方式。
    3、指定是填充绘制矩形(fiiRect())还是描边绘制矩形(strokeRect())。


    填充和描边

    第二步:

    2D上下文的基本的绘图方式有两种:填充和描边。顾名思义,填充就是用指定的颜色来填充图形,描边,就是绘制边缘。这两种方法分别为:fillStyle和strokeStyle。

    <!doctype html>
    <html>
      <head>
        <meta charset="utf-8">
          <title>JavaScript之Canvas</title>
      </head>
      <body>
    	
        <canvas id="canvas" width="400" height="300">抱歉,您的浏览器不支持canvas元素</canvas>
    
      
      <script>
          
          var canvas = document.getElementById("canvas");
          //检测浏览器是否支持canvas 该方法是否存在 取得上下文对象
          if (canvas.getContext) {
              var context = canvas.getContext('2d'); //2d用单引用括起来
    		 
              if (canvas.getContext) {
    	      var context = canvas.getContext("2d"); //2d用单引用括起来
    	      context.fillStyle = "red"; // 填充颜色为红色
    	      context.strokeStyle = "blue"; //描边颜色为蓝色		  
    	 }
          }
    	  
      </script>
      </body>
    </html>

    当然,像上面的代码还不能绘制图形,这样只能说取得上下文对象、选择好了填充颜色、选择好了描边颜色而已,接下来就是绘制图形了。


    绘制矩形

    第三步:与绘制矩形有关的方法有:fillRect()、strokeRect()、clearRect()。它们分别接收三个参数:第一个参数表示矩形的x坐标、第二个参数表示矩形的y坐标,第三个参数表示矩形的宽度、第四个参数表示矩形的高度。

    fillRect()表示用指定的填充颜色绘制矩形:
    • lineWidth:表示描边线的宽度
    • lineCap:用于控制线条末端的形状是平头(butt)、圆头(round)、方头(square)。
    • lineJoin:用于控制线条相交的方式,圆交(round)、斜交(bevel)、斜接(miter)。


    <body>
    
      <canvas id="canvas" width="400" height="300">抱歉,您的浏览器不支持canvas元素</canvas>
    
    
    <script>
        
        var canvas = document.getElementById("canvas");
        //检测浏览器是否支持canvas 该方法是否存在 取得上下文对象
        if (canvas.getContext) {
            var context = canvas.getContext("2d");
    	context.fillStyle = "red"; // 指定填充颜色为红色
    	context.fillRect(10, 10, 150, 150);	//用指定的颜色填充矩形
        }
      
    </script>
    </body>
    

    效果:



    可以看出:红色矩形离canva区域左边10px,离上边10px。大小为150x150。


    strokeRect()表示用指定的描边颜色描边绘制出矩形。

    • lineWidth:表示描边线的宽度
    • lineCap:用于控制线条末端的形状是平头(butt)、圆头(round)、方头(square)。
    • lineJoin:用于控制线条相交的方式,圆交(round)、斜交(bevel)、斜接(miter)。
    <body>
    
      <canvas id="canvas" width="400" height="300">抱歉,您的浏览器不支持canvas元素</canvas>
    
    
    <script>
        
        var canvas = document.getElementById("canvas");
        //检测浏览器是否支持canvas 该方法是否存在 取得上下文对象
        if (canvas.getContext) {
            var context = canvas.getContext("2d"); //2d用单引用括起来
            context.strokeStyoe = "yellow"; // 描边颜色为蓝色
            context.lineWidth = 4; //指定描边线的宽度
            context.strokeRect(50, 50, 120, 120);	//用指定的颜色描边矩形
        }
    </script>
    </body>

    效果:



    当然,填充和描边可以一起使用来绘制矩形:当填充矩形和描边矩形的坐标一样时
    <body>
    
      <canvas id="canvas" width="400" height="300">抱歉,您的浏览器不支持canvas元素</canvas>
    
    
    <script>
        
        var canvas = document.getElementById("canvas");
        //检测浏览器是否支持canvas 该方法是否存在 取得上下文对象
        if (canvas.getContext) {
            var context = canvas.getContext("2d"); //2d用单引用括起来
            //描边矩形
            context.strokeStyoe = "yellow"; // 描边颜色为蓝色
            context.lineWidth = 4; //指定描边线的宽度
            context.strokeRect(50, 50, 120, 120);	//用指定的颜色描边矩形
            //填充矩形
    	context.fillStyle = "red";
    	context.fillRect(50, 50, 120, 120);
    
        }
    </script>
    </body>

    效果:





    当填充矩形和描边矩形的坐标不一样时,
    <body>
    
      <canvas id="canvas" width="400" height="300">抱歉,您的浏览器不支持canvas元素</canvas>
    
    
    <script>
        
        var canvas = document.getElementById("canvas");
        //检测浏览器是否支持canvas 该方法是否存在 取得上下文对象
        if (canvas.getContext) {
            var context = canvas.getContext("2d"); //2d用单引用括起来
            //描边矩形
            context.strokeStyoe = "yellow"; // 描边颜色为蓝色
            context.lineWidth = 4; //指定描边线的宽度
            context.strokeRect(50, 50, 120, 120);	//用指定的颜色描边矩形
            //填充矩形
    	context.fillStyle = "red";
    	context.fillRect(150, 150, 120, 120);
    
        }
    </script>
    </body>

    效果




    注:当指定的填充颜色或指定的描边颜色用“rgba()”形式表示时,可以用于绘制透明形的矩形。






    clearRcet()方法可以用于清除canvas区域内的矩形,当然也可以用于消除一部分,生成有意思的形状。

    <body>
    
      <canvas id="canvas" width="400" height="300">抱歉,您的浏览器不支持canvas元素</canvas>
    
    
    <script>
        
        var canvas = document.getElementById("canvas");
        //检测浏览器是否支持canvas 该方法是否存在 取得上下文对象
        if (canvas.getContext) {
            var context = canvas.getContext("2d"); //2d用单引用括起来
            //描边矩形
            context.strokeStyoe = "yellow"; // 描边颜色为蓝色
            context.lineWidth = 4; //指定描边线的宽度
            context.strokeRect(50, 50, 120, 120);	//用指定的颜色描边矩形
            //填充矩形
    	context.fillStyle = "red";
    	context.fillRect(150, 150, 120, 120);
    
            //消除指定大小的区域
    	context.clearRect(110, 110, 30, 30);
    
        }
    </script>
    </body>
    


    效果:






    绘制路径

    绘制路径一般是绘制圆形或者圆弧。

    beginPath():表示开始绘制新路径。
    closePath():表示关闭绘制路径。


    可以通过以下方法绘制路径:var canvas = document.getElementById("canvas"); var context = canvas.getContent("2d");

    context.arc(x, y, r, sa, ea, c):表示以(x, y)为圆心,画一个以r为半径,起始角度为sa,结束角度为ea的圆,c表示是否逆时针,默认为true,false表示顺时针。
    其中,fill()表示以填充方式绘制圆,stroke()表示以描边方式绘制圆。
    <body>
    
      <canvas id="canvas" width="400" height="300">抱歉,您的浏览器不支持canvas元素</canvas>
    
    
    <script>
        
        var canvas = document.getElementById("canvas");
        //检测浏览器是否支持canvas 该方法是否存在 取得上下文对象
        if (canvas.getContext) {
            var context = canvas.getContext("2d");
    
            //开始绘制新路径
    	context.beginPath(); 
    	context.arc(50, 50, 40, 0, 2*Math.PI, false);
    	context.closePath(); //关闭路径
    	context.fillStyle = "red";
    	//以填充方式绘制圆
    	context.fill();
    
        }
    </script>
    </body>

    效果:





    context.artTo(x1, y1, x2, y2, r):表示从上一点开始绘制一条半径为r的弧线,结束坐标为(x2, y2),并且经过(x1, y1)这个坐标。
    <body>
    
      <canvas id="canvas" width="400" height="300">抱歉,您的浏览器不支持canvas元素</canvas>
    
    
    <script>
        
        var canvas = document.getElementById("canvas");
        //检测浏览器是否支持canvas 该方法是否存在 取得上下文对象
        if (canvas.getContext) {
            var context = canvas.getContext("2d");
    
            //开始绘制新路径
    	context.beginPath();
    	context.beginPath();
    	context.moveTo(100, 100); //绘制起点
    	context.arcTo(50, 50, 10, 200, 200);
    	context.closePath();
    	context.strokeStyle = "red";
    	context.stroke();
    
        }
    </script>
    </body>

    效果:


    以(100, 100)为起点,画一条经过(50, 50)和(200, 200)的圆弧。



    context.moveTo(x, y):将绘图起点移动到(x, y),不画线。也就是绘制图形的起点坐标。

    context.lineTo(x, y):从上一点绘制一条直线,到(x, y)为止。画线的终点坐标。

    <body>
    
      <canvas id="canvas" width="400" height="300">抱歉,您的浏览器不支持canvas元素</canvas>
    
    
    <script>
        
        var canvas = document.getElementById("canvas");
        //检测浏览器是否支持canvas 该方法是否存在 取得上下文对象
        if (canvas.getContext) {
            var context = canvas.getContext("2d");
    
            //开始绘制新路径
    	context.beginPath();
    	context.moveTo(30, 30); //画线的起点
    	context.lineTo(200, 200); //终点
    	context.closePath();
    	context.strokeStyle = "red";
    	context.stroke();
    
        }
    </script>
    </body>
    


    效果:


    画一条从起点(30, 30)到终点(200, 200)的直线。



    rect(x, y, width, height):从(x, y)为起始点,画一个矩形,此矩形为一条路径,不是一个独立的形状。

    <body>
    
      <canvas id="canvas" width="400" height="300">抱歉,您的浏览器不支持canvas元素</canvas>
    
    
    <script>
        
        var canvas = document.getElementById("canvas");
        //检测浏览器是否支持canvas 该方法是否存在 取得上下文对象
        if (canvas.getContext) {
            var context = canvas.getContext("2d");
    
            //开始绘制新路径
    	context.beginPath();
    	context.arce(20, 20, 200, 200);
    	context.closePath();
    	context.strokeStyle = "red";
    	context.stroke();
    
        }
    </script>
    </body>

    效果:

    注:这个矩形是路径,不是strokeRect()画的独立的形状。



    绘制路径小知识:

    如果想画一条连接到起点的路径,可以使用closePath(),即将路径画成一个回路如果想填充绘制,用fillStyle()和fill()方法,如果相描边绘制,用strokeStyle()和stroke()方法。




    绘制文本


    图形与文本问题如影随形。绘制文本的方法有两个:fillText()和strokeText。
    字面意思就是:填充绘制文本和描边绘制文本。
    fillText方法使用fillStyle属性绘制文本,strokeText()方法使用strokeStyle属性绘制文本。

    这两个方法接收四个参数:第一个参数是要绘制的文本内容,第二个参数是x坐标,第三个参数是y坐标,以及最大像素宽度。

    两个方法均以下3个属性为基础:
    contxt.font:字体样式、大小、字体。

    context.textAlign:对齐方式(start、center、end),相当于向左、中间、向右对齐。

    context.textBaseline:文本基线。

    <body>
    
      <canvas id="canvas" width="400" height="300">抱歉,您的浏览器不支持canvas元素</canvas>
    
    
    <script>
        
        var canvas = document.getElementById("canvas");
        //检测浏览器是否支持canvas 该方法是否存在 取得上下文对象
        if (canvas.getContext) {
            var context = canvas.getContext("2d");
    
    	context.fillStyle = "red";
    	context.font = "bold 26px"; //字体样式
    	context.textAlign = "statr"; //对齐方式为左对齐
    	context.textBaseline = "top"; //基线
    	context.fillText("你好", 100, 100, 200);
    
        }
    </script>
    </body>
    

    效果:




    这是要注意:如果"textAlign"设置为"start",则x表示文本左端的位置(从左向右阅读),如果"textAlign"设置为"end",则x表示文本右凋的位置(从右向左阅读)。




    变换

    通过上下文的变换,可以将处理后的图像绘制到画布上。创建绘制上下文时,会初始化变换矩阵,所有处理按描述直接处理。
    变换的几种方式:
    1、rotate(angle):旋转图像angle角度。注:角度不能带单位。
    2、scale(scaleX, scaleY):缩放图像。在x方向乘以scaleX,在y轴方向上乘以scaleY。
    3、translate(x, y):将坐标原点移动到(x,y)的位置。也就是说原来的(0, 0)位置变成现在的(x, y)位置。
    4、transform(a, b, c, d, e, f):直接修改矩阵。矩阵的算法:
    a  c  e         x
    b  d  f     *   y
    0  0  0         0

    5、setTransform(a, b, c, d, e, f):将矩阵重置为默认状态,然后再调用矩阵。

    6、skew(x, y):将画布拉伸


    以下画一个钟表为例:
    <body>
    
      <canvas id="canvas" width="400" height="300">抱歉,您的浏览器不支持canvas元素</canvas>
    
    
    <script>
        
        var canvas = document.getElementById("canvas");
        //检测浏览器是否支持canvas 该方法是否存在 取得上下文对象
        if (canvas.getContext) {
            var context = canvas.getContext("2d");
    
            context.beginPath();
            //绘制外圆
            context.arc(100, 100, 99, 0, 2*Math.PI, false);
    	//绘制内圆
            context.moveTo(194, 100);
            context.arc(100, 100, 94, 0, 2*Math.PI, false);
    		  
            context.translate(100, 100); //变换原点,为后面绘制时针、分针做准备
    		  
            //绘制分针
            context.moveTo(0, 0); //绘制直线的起点
            context.lineTo(0, -85); //至直线的终点
            //绘制时针
            context.moveTo(0, 0);
            context.lineTo(-65, 0);
            //描边绘制钟表
            context.stroke();
    
        }
    </script>
    </body>


    效果:




    把原点变换到钟表的中心处,这样原点就还在画布的左上角了,而是在钟表的中心处,数学计算是基于(0, 0)计算的,而不是(100, 100)。还可以进一步,将钟表旋转90度等。
    <script>
        
        var canvas = document.getElementById("canvas");
        //检测浏览器是否支持canvas 该方法是否存在 取得上下文对象
        if (canvas.getContext) {
            var context = canvas.getContext("2d");
    
            context.beginPath();
            //绘制外圆
            context.arc(100, 100, 99, 0, 2*Math.PI, false);
    	//绘制内圆
            context.moveTo(194, 100);
            context.arc(100, 100, 94, 0, 2*Math.PI, false);
    		  
            context.translate(100, 100); //变换原点,为后面绘制时针、分针做准备
            context.rotate(90); //不用像HTML中的变换一样带单位
    		  
            //绘制分针
            context.moveTo(0, 0); //绘制直线的起点
            context.lineTo(0, -85); //至直线的终点
            //绘制时针
            context.moveTo(0, 0);
            context.lineTo(-65, 0);
            //描边绘制钟表
            context.stroke();
    
        }
    </script>
    </body>

    效果:



    因为原点已经到了钟表的中心处,所以旋转是基于该中心为原点旋转的。就像是钟表被固定在墙上,然后被旋转90角一样。




    sava()方法和restore()方法可以跟踪上下文的状态变化,如果你想要返回某种属性和状态的组合,可以使用save()方法,也就是说该方法可以保存你先前设置的上下文的属性和状态,该方法将先前你设置的属性和状态推入一个栈结构,等到你想要用保存的属性和状态后,就使用restore()方法,在保存设置的栈结构后 返回一级,如果连续使用save()方法,可以将多个属性和状态的组合保存在栈结构中,此时再使用restore()方法一级一级向上返回。

    设置一次属性和状态,就用save()方法保存一次。这样用restore()方法就可以逐级追溯到每次设置的属性和状态。

    <!doctype html>
    <html>
      <head>
        <meta charset="utf-8">
    	<title>JavaScript之Canvas</title>
      </head>
      <body>
    	
        <canvas id="canvas" width="400" height="300">抱歉,您的浏览器不支持canvas元素</canvas>
    
      
      <script>
          
          var canvas = document.getElementById("canvas");
    	  //检测浏览器是否支持canvas 该方法是否存在 取得上下文对象
    	  if (canvas.getContext) {
    	      var context = canvas.getContext("2d"); 
    		  
              context.fillStyle = "blue"; //先将fillStyle设置为蓝色
              context.save(); //保存此时的属性
              
    		  context.fillStyle = "yellow"; //再将fillStyle设置为黄色
    		  context.translate(100, 100); //变换,将原点设置到(100, 100)这个位置
    		  context.save(); //再保存此时的属性
    		  
              context.fillStyle = "red"; //  将fillStyle设置为红色
    		  context.save();
    		  
    		  //第一次restore(),此时将矩形绘制为红色,且原点(0, 0)的实际坐标为(100, 100)
    		  context.restore(); 
    		  //第二次restore(),此时将矩形绘制为黄色,且原点(0, 0)的实际坐标为(100, 100)
    		  context.restore();
    		  //第三次restore(); 此时将矩形绘制为蓝色,这是最上级设置的属性,且此时的原点(0, 0)的实际坐标为(0, 0)
    		  context.restore();
              context.fillRect(50, 50, 300, 300, false);
    		  
    	 }
    	  
      </script>
      </body>
    </html>

    最终效果:





    先将fillStyle设置为蓝色,将用save()保存此时的属性,再将fillStyle设置为黄色,将变换原点的坐标,再次将此时的属性保存,最后再次设置fillStyle的属性为红色将保存。
    然后多次使用restore()方法逐级向上返回:

    第一次restore(),即追溯到fillStyle属性为红色,此时原点的位置在(100, 100)。





    第二次restore(),即追溯到fillStyle属性为黄色,此时原点的位置在(100, 100)。




    最后一次restore(),追溯到最初的fillStyle的属性即为蓝色,此时没有变换原点(0, 0)的位置,依然在(0, 0)处。



    这也是代码执行后的最终效果。


    注:save()方法只能保存设置的属性和变换,不能保存绘制上下文的内容。




    阴影


    根据以下属性,可以自定义为矩形或者路径添加阴影。

    1、context.showColor:为阴影添加颜色。
    2、context.showOffsetX:为阴影添加x轴方向上的偏移量。
    3、context.showOffsetY:为阴影添加y轴方向上的偏移量。
    3、context.showBlur:为阴影添加模糊程度。

    <body>
    
      <canvas id="canvas" width="400" height="300">抱歉,您的浏览器不支持canvas元素</canvas>
    
    
    <script>
        
        var canvas = document.getElementById("canvas");
        //检测浏览器是否支持canvas 该方法是否存在 取得上下文对象
        if (canvas.getContext) {
            var context = canvas.getContext("2d");
    
            //设置阴影的属性
            context.shadowOffsetX = 10;
            context.shadowOffsetY = 20;
            context.shadowBlur = 2;
            context.shadowColor = "blue";
    	
            //绘制矩形	  
            context.fillStyle = "red";
            context.fillRect(10, 10, 109, 109);
        }
    </script>
    </body>


    效果:




    这里为红色矩形设置了一个蓝色阴影。注:设置阴影属性的代码要在绘制矩形的代码之前,不然就不会绘制出阴影




    渐变

    在canvas画布中使用渐变,需要用一个矩形作为参照来显示这个渐变效果,如线性渐变、径向渐变等都需要绘制一个矩形来显示渐变效果。
    线性渐变 (createLinearGradient)
    在canvas中使用渐变,首先可以通过createLinearGradient()方法一创建一个 线性渐变对象,它接收四个参数,分别为:渐变起始位的x、y坐标,渐变结束位的x、y坐标。这样就能创建一个指定大小的渐变。

    然后在创建渐变对象后,使用addColorStop()方法来指定色标(渐变颜色),它接收两个参数:色标位置和css颜色值,色标位置是0(开始的颜色)和1(结束的颜色)之间的数值组成。

    最后将fillStyle或者strokeStyle设置为这个渐变对象即可(将渐变对象赋值给它们)。

    为了让渐变覆盖整个矩形,需要将矩形的坐标与渐变的坐标相匹配(不一定相等),这个显示的渐变区域不只能矩形的一小部分了。

    <!doctype html>
    <html>
      <head>
        <meta charset="utf-8">
    	<title>JavaScript之Canvas</title>
      </head>
      <body>
    	
        <canvas id="canvas" width="400" height="300">抱歉,您的浏览器不支持canvas元素</canvas>
    
      
      <script>
          
          var canvas = document.getElementById("canvas");
    	  //检测浏览器是否支持canvas 该方法是否存在 取得上下文对象
    	  if (canvas.getContext) {
    	      var context = canvas.getContext("2d");
    		  
    		  //定义一个渐变对象
    		  var gradient = context.createLinearGradient(30, 30, 200, 200);
    		  //为这个对象设置渐变颜色
    		  gradient.addColorStop(0, "white");
    		  gradient.addColorStop(1, "black");
    		  
    		  //绘制一个矩形作为参考
    		  context.fillStyle = "orange";
    		  context.fillRect(20, 20, 200, 200);
    		  
    		  //把fillStyle或者strokeStyle设置为这个渐变对象
    		  context.fillStyle = gradient;
    		  context.fillRect(30, 30, 200, 200);
    		 	  
    	  }
    	  
      </script>
      </body>
    </html>


    效果:



    可以看出,绘制了两个矩形,其中一个矩形就是一个参照,而另一个矩形则用来显示渐变效果而绘制的。


    径向渐变(createRadialGradient)
    使用createRadialGradient()方法来创建渐变对象,该方法接收6个参数,分别是起始圆的x、y坐标和半径。结束圆的x、y坐标和半径。

    其中,如果一个圆比另一个圆大,那么就是一个圆锥形状的渐变。

    如果想让渐变从圆的中心扩散,可以将其设置为一个同心圆即圆心坐标相同,而半径不同。

    <!doctype html>
    <html>
      <head>
        <meta charset="utf-8">
    	<title>JavaScript之Canvas</title>
      </head>
      <body>
    	
        <canvas id="canvas" width="400" height="300">抱歉,您的浏览器不支持canvas元素</canvas>
    
      
      <script>
          
          var canvas = document.getElementById("canvas");
    	  //检测浏览器是否支持canvas 该方法是否存在 取得上下文对象
    	  if (canvas.getContext) {
    	      var context = canvas.getContext("2d");
    		  
    		  //定义一个径向渐变对象
    		  var gradient = context.createRadialGradient(57, 57, 10, 57, 57, 30);
    		  //为这个对象设置渐变颜色
    		  gradient.addColorStop(0, "white");
    		  gradient.addColorStop(1, "blue"); //结束颜色也就是矩形的颜色
    		  
    		   
    		  //把fillStyle或者strokeStyle设置为这个渐变对象
    		  context.fillStyle = gradient;
    		  context.fillRect(10, 10, 100, 100);
    		 	  
    	  }
    	  
      </script>
      </body>
    </html>


    效果:



    绘制图像

    2D绘制上下文内置了对图像的支持。如果想要把一张图像绘制在画布上,就需要drawImage()方法。调用这个方法,可以使用三种不同的参数组合。

    格式:drawImage(要绘制的图像, x, y);  其中,x、y表示图像的起点坐标。

    第一种,使用最简单的HTML<img>元素直接嵌入到页面中,再将图像绘制到画布上。

    这种方法首先需要使用document.images[0]获取页面中的第一个<img>元素。

    <!doctype html>
    <html>
      <head>
        <meta charset="utf-8">
    	<title>JavaScript之Canvas</title>
      </head>
      <body>
    	
        <canvas id="canvas" width="400" height="300">抱歉,您的浏览器不支持canvas元素</canvas>
        <img src="1.jpg" width=200 height=200 alt="图片"> <1-- 直接使用HTML<img>元素 -->
      
      <script>
          
          var canvas = document.getElementById("canvas");
              //检测浏览器是否支持canvas 该方法是否存在 取得上下文对象
              if (canvas.getContext) {
    	      var context = canvas.getContext("2d"); 
    	       
                  var img = document.images[0]; //获取页面中的第一个<img>元素
                  img.onload = function () {  //这步很重要,只有在图像加载完成后,才能将其绘制到画布上
                      context.drawImage(img, 5, 5); //起点坐标(5,5)
                  };
    	 }
    	  
      </script>
      </body>
    </html>

    效果:



    从图上可以看出,左边的是画布区域,而右边的是由<img>元素嵌入的一张图像,左边画布上没有笑脸,只有风格是怎么回事呢?原来是因为这张图像太大了,而画布区域太小的原因,使图像不能完全显示出来。在<img>中我们是限制了图像的尺寸的,而在画布上没有限制图像的尺寸。


    第二种,此时我们可以给drawImage()方法传入两个参数来控制画布上的图像尺寸。

    格式;drawImage(要绘制的图像, x, y, w, h);  其中,w、h分别表示图像的尺寸。

    <!doctype html>
    <html>
      <head>
        <meta charset="utf-8">
    	<title>JavaScript之Canvas</title>
      </head>
      <body>
    	
        <canvas id="canvas" width="400" height="300">抱歉,您的浏览器不支持canvas元素</canvas>
        <img src="1.jpg" width=200 height=200 alt="图片">
      
      <script>
          
          var canvas = document.getElementById("canvas");
              //检测浏览器是否支持canvas 该方法是否存在 取得上下文对象
              if (canvas.getContext) {
    	      var context = canvas.getContext("2d"); 
    	       
                  var img = document.images[0];
                  img.onload = function () {
                      context.drawImage(img, 10, 10, 200, 200); //此时,起始坐标为(10, 10),且,画布上的图像尺寸大小为200*200
                  };
    	 }
    	  
      </script>
      </body>
    </html>

    效果:



    此时,将画布上图像的尺寸改了一下,这样就能看见了。当然我们也可以更改图像的起点坐标。起点坐标是基于画布的左边、顶部来计算的,也就是说,画布的左上角是原点即(0, 0)。

    像以上例子,直接将页面中的图像绘制到画布中时,需要用document.Images[?]来获取页面中的第?个<img>元素。


    第三种,前二种都是绘制图像的整体,而通过传入不同的参数,也可以只绘制图像的部分区域。

    drawImage()方法接收9个参数:要绘制的图像、源图像的x坐标、源图像的y坐标、源图像的宽度、源图像的高度、目标图像的x坐标、目标图像的y坐标、目标图像的宽度、目标图像的高度。

    <!doctype html>
    <html>
      <head>
        <meta charset="utf-8">
    	<title>JavaScript之Canvas</title>
      </head>
      <body>
    	
        <canvas id="canvas" width="400" height="300">抱歉,您的浏览器不支持canvas元素</canvas>
        <img src="1.jpg" width=200 height=200 alt="图片">
      
      <script>
          
          var canvas = document.getElementById("canvas");
              //检测浏览器是否支持canvas 该方法是否存在 取得上下文对象
              if (canvas.getContext) {
    	      var context = canvas.getContext("2d"); 
    	       
                  var img = document.images[0];
                  img.onload = function () {
                      context.drawImage(img, 250, 150, 400, 400, 200, 100, 100, 100); 
                  };
    	 }
    	  
      </script>
      </body>
    </html>


    效果:



    可以看出,我们只绘制了图像的一部分区域。源坐标是为了找到源图像(右边的那个图像),源图像的宽度、高度是为了确定要绘制源图像的多大区域。


    除了直接获取页面中的图像外,也可以获取外部的图像资源来将此图像绘制到画布上。

    知识点:
    var img = new Image(); //表示嵌入一个图像对象实例

    img.src = "1.jpg"; //表示获取图像的URL

    img.onload = function () {}; //等到图像加载完成后,才能绘制。


    <!doctype html>
    <html>
      <head>
        <meta charset="utf-8">
    	<title>JavaScript之Canvas</title>
      </head>
      <body>
    	
        <canvas id="canvas" width="400" height="300">抱歉,您的浏览器不支持canvas元素</canvas>
      
      <script>
          
          var canvas = document.getElementById("canvas");
              //检测浏览器是否支持canvas 该方法是否存在 取得上下文对象
              if (canvas.getContext) {
    	      var context = canvas.getContext("2d"); 
    	       
                  //嵌入一个图像对象
                  var img = new Image();
                  //并获取图像的URL,当在赋值时,图像才开始加载。
                  img.src = "1.jpg";
                  //等图像加载完成后,才能将其绘制到画布中,所以要使用img.onload
                  img.onload = function () {
                      context.drawImage(img, 20, 20, 250, 270); 
                  };
    	 }
    	  
      </script>
      </body>
    </html>

    效果:



    使用这种方法,无需将图像使用<img>元素嵌入到页面中了。这里要注意的是,"img.src = "1.jpg;  img.onload = function () {};",img.src赋值的时候,图片才开始去加载,需要等到图像加载完成后才能使用drawImage()方法绘制,或者还没加载完,就直接drawImage了,画布上就会显示空白。

    注:drawImage()的第一个参数除了可以是图像外,也可以是<canvas>元素。



    模式(在canvas中重复显示图像)


    可以在画布中绘制重复显示图像的效果,模式就是重复的图像,可以用于填充和描边图像。创建一个模式,要使用createPattern()方法,它接收两个参数:第一个参数是一个HTML<img>元素或者是表示图像的变量,第二个参数是表示如何重复显示的字符串,即“repeat”、"repeat-x"、"repeat-y"、“no-repeat”。再将此模式赋值给fillStyle()或strokeStyle()。最后画出矩形。

    <!doctype html>  
    <html>  
      <head>  
        <meta charset="utf-8">  
        <title>JavaScript之Canvas</title>  
      </head>  
      <body>  
          
        <canvas id="canvas" width="400" height="300">抱歉,您的浏览器不支持canvas元素</canvas>  
        <img src="1.jpg" width=100 height=100 alt="图片">  
        
      <script>  
            
          var canvas = document.getElementById("canvas");  
              //检测浏览器是否支持canvas 该方法是否存在 取得上下文对象  
              if (canvas.getContext) {  
              var context = canvas.getContext("2d");   
      
                  var img = document.images[0]; 
    
                  //在图像加载完成后,才开始绘制矩形
                  img.onload = function () {
                      var pattern = context.createPattern(img, "repeat"); //创建模式并重复显示图像  
                      context.fillStyle = pattern; //将模式赋值给填充  
                      context.fillRect(10, 10, 500, 500); //最后画出矩形 
                  }; 
                    
         }  
            
      </script>  
      </body>  
    </html>  
    
    

    效果:



    图像在画布中重复显示了,如果画布足够大,那么显示的图像越多。

    注意:在页面中的图像加载完成后,才能开始创建模式和绘制和矩形,即"img.onload = function () {}"。



    图像合成

    还有两个运用到2D绘制上下文的操作方法:globalAlpha和globalCompositionOperation。

    globalAlpha是一个介于0和1之间值,它指定所有绘制的透明度,默认值为0。如果后续的绘制都基于相同的透明度,可以设置为适当的值,最后,再把它设置为默认值0(重置透明度)。

    <!doctype html>
    <html>
      <head>
        <meta charset="utf-8">
    	<title>JavaScript之Canvas</title>
      </head>
      <body>
    	
        <canvas id="canvas" width="400" height="300">抱歉,您的浏览器不支持canvas元素</canvas>
      
      <script>
          
          var canvas = document.getElementById("canvas");
              //检测浏览器是否支持canvas 该方法是否存在 取得上下文对象
              if (canvas.getContext) {
    	      var context = canvas.getContext("2d"); 
                  //画一个蓝色矩形
                  context.fillStyle = "blue";
                  context.fillRect(10, 10, 200, 200);
    
                  //修改全局透明度
                  context.globalAlpha = 0.5;
    
                  //绘制一个红色矩形,红色矩形在蓝色矩形的上面,这是根据文档流的结果。
                  context.fillStyle = "red";
                  context.fillRect(50, 50, 200, 200);
    
                  //重置全局透明度 
                  context.globalAlpha = 0;
    	 }
    	  
      </script>
      </body>
    </html>

    效果:



    我们把红色矩形绘制在了蓝色矩形的上面。在绘制红色矩形之前,将globalAlpha设置为0.5,所以透过红色矩形我们可以看见下面的蓝色矩形。


    globalCompositionOperation 表示 后绘制的图像 怎样先绘制的图像结合。它的属性值有:

    注:这里的 目标图像指后绘制的图形,源图像指前绘制的图像。



    举个例子:

    <!doctype html>
    <html>
      <head>
        <meta charset="utf-8">
    	<title>JavaScript之Canvas</title>
      </head>
      <body>
    	
        <canvas id="canvas" width="400" height="300">抱歉,您的浏览器不支持canvas元素</canvas>
    
      
      <script>
          
          var canvas = document.getElementById("canvas");
    	  //检测浏览器是否支持canvas 该方法是否存在 取得上下文对象
    	  if (canvas.getContext) {
    	      var context = canvas.getContext("2d"); 
    	       
                  context.fillStyle="red";
                  context.fillRect(20,20,75,50);
    
                  context.fillStyle="blue";	
                  context.globalCompositeOperation="source-over";
                  context.fillRect(50,50,75,50);
    	
                  context.fillStyle="red";
                  context.fillRect(150,20,75,50);
    
                  context.fillStyle="blue";	
                  context.globalCompositeOperation="destination-over";
                  context.fillRect(180,50,75,50);
    	 }
    	  
      </script>
      </body>
    </html>

    效果:



    使用这个方法来合成图像,应该多试几个浏览器,因为有一些浏览器显示这个效果还有一些问题,例如谷歌不能显示指定的效果,火狐也是不能显示。



    WebGL

    WebGL是canvas的3D上下文,关于这部分内容,本人将在以后的文章介绍。


    展开全文
  • html5 画布例子

    2017-02-06 20:49:43
    html5 canvas画布例子,一个连线的游戏
  • android中自定义画布Canvas的实现

    千次阅读 2017-07-02 22:28:15
    1.画布绘制控件的方法,控件应该是一个可以自定义的; 2.画布是可以缩放,且提供一个缩放的方法供外使用; 3.控件之间连线的方法; 4.画布缩放之后手势滑动的识别实现; 二、在github里面种找到了一个类似度挺高的...
    一、要求:
    1.画布绘制控件的方法,控件应该是一个可以自定义的;
    2.画布是可以缩放,且提供一个缩放的方法供外使用;
    3.控件之间连线的方法;
    4.画布缩放之后手势滑动的识别实现;

    二、在github里面种找到了一个类似度挺高的开源项目:

    github中的第三方的开源项目地址

    在第三方的FabricView的项目中已经实现了:

    1.控件的可以绘制;
    2.可以连线;
    3.未实现的是缩放的实现?

    4.手势滑动的识别?

    5.缩放之后的滑动识别?

    三、需求改造:
    把开源项目经过修剪和添加来实现自己项目中的画布功能


    1.在画板上连线已经实现:

    public boolean onTouchDrawMode(MotionEvent event)
        {
            // get location of touch
            float eventX = event.getX();
            float eventY = event.getY();
    
            // based on the users action, start drawing
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    // create new path and paint
                    currentPath = new CPath();
                    currentPaint = new Paint();
                    currentPaint.setAntiAlias(true);
                    currentPaint.setColor(mColor);
                    currentPaint.setStyle(mStyle);
                    currentPaint.setStrokeJoin(Paint.Join.ROUND);
                    currentPaint.setStrokeWidth(mSize);
                    currentPath.moveTo(eventX, eventY);
                    currentPath.setPaint(currentPaint);
                    // capture touched locations
                    lastTouchX = eventX;
                    lastTouchY = eventY;
                    Log.i("Everbrilliant","点下去位置lastTouchX:"+lastTouchX+">>lastTouchY:"+lastTouchY);
                    mDrawableList.add(currentPath);
                    return true;
                case MotionEvent.ACTION_MOVE:
                case MotionEvent.ACTION_UP:
                    currentPath.lineTo(eventX, eventY);
                    // When the hardware tracks events faster than they are delivered, the
                    // event will contain a history of those skipped points.
                    int historySize = event.getHistorySize();
                    for (int i = 0; i < historySize; i++) {
                        float historicalX = event.getHistoricalX(i);
                        float historicalY = event.getHistoricalY(i);
                        Log.i("everb","存在缓存中的值historicalX:"+historicalX+">>historicalY:"+historicalY);
                        if (historicalX < dirtyRect.left) {
                            dirtyRect.left = historicalX;
                        } else if (historicalX > dirtyRect.right) {
                            dirtyRect.right = historicalX;
                        }
                        if (historicalY < dirtyRect.top) {
                            dirtyRect.top = historicalY;
                        } else if (historicalY > dirtyRect.bottom) {
                            dirtyRect.bottom = historicalY;
                        }
                        currentPath.lineTo(historicalX, historicalY);
                    }
    
                    // After replaying history, connect the line to the touch point.
                    currentPath.lineTo(eventX, eventY);
                    cleanDirtyRegion(eventX, eventY);
                    break;
                default:
                    return false;
            }
    
            // Include some padding to ensure nothing is clipped
            invalidate(
                    (int) (dirtyRect.left - 20),
                    (int) (dirtyRect.top - 20),
                    (int) (dirtyRect.right + 20),
                    (int) (dirtyRect.bottom + 20));
    
            // register most recent touch locations
            lastTouchX = eventX;
            lastTouchY = eventY;
            return true;
        }
    2.绘制控件只要一类实现CDrawable的接口并把这控件添加FabricView画布的onDraw方法中的数组中既可以

    private ArrayList<CDrawable> mDrawableList = new ArrayList<>();
        /*
         * Called when there is the canvas is being re-drawn.
         */
        @Override
        protected void onDraw(Canvas canvas) {
            // check if background needs to be redrawn
            mBackgroundMode=BACKGROUND_STYLE_NOTEBOOK_PAPER;
            drawBackground(canvas, mBackgroundMode);
    
            // go through each item in the list and draw it
            for (int i = 0; i < mDrawableList.size(); i++) {
                try {
                    mDrawableList.get(i).draw(canvas);
                }
    
                catch(Exception ex)
                {
    
                }
            }
        }

    3.至于画布的缩放方法可以用view中的setScaleX、setScaleY来实现缩放。这里在放大后还要画布是可以拖动到:

    private void updateScaleStep(int newScaleIndex) {
            if (newScaleIndex != mCurrentZoomScaleIndex) {
                final float oldViewScale = mViewScale;
    
                mCurrentZoomScaleIndex = newScaleIndex;
                mViewScale = ZOOM_SCALES[mCurrentZoomScaleIndex];
                final float scaleDifference = mViewScale - oldViewScale;
                scrollBy((int) (scaleDifference * getMeasuredWidth() / 2),
                        (int) (scaleDifference * getMeasuredHeight() / 2));
    
                if (shouldDrawGrid()) {
                    mGridRenderer.updateGridBitmap(mViewScale);
                }
    
                mWorkspaceView.setScaleX(mViewScale);
                mWorkspaceView.setScaleY(mViewScale);
                mWorkspaceView.requestLayout();
            }
        }

    4.手势的滑动识别:则在FabricView的onTouchEvent实现手势的滑动识别:
    在view中GestrueDetector.OnGstureListener监听点击屏幕的接口和GestrueDetector.OnDoubleTapListener监听双击的接口实现这两个接口就可以实现手势的实现。

    mTapGestureDetector = new GestureDetector(getContext(), new TapGestureListener());          //设置手势的监听
    
    private class TapGestureListener implements GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener {
    
            @Override
            public boolean onDown(MotionEvent motionEvent) {
                return false;
            }
    
            @Override
            public void onShowPress(MotionEvent motionEvent) {
    
            }
    
            @Override
            public boolean onSingleTapUp(MotionEvent motionEvent) {
                return false;
            }
    
            @Override
            public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
                return false;
            }
    
            @Override
            public void onLongPress(MotionEvent motionEvent) {
                // TODO: 2017/6/29 长按可以设置模式为连线
                Toast.makeText(mContext,"实现了长按手势",Toast.LENGTH_SHORT).show();
            }
    
            @Override
            public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
                return false;
            }
    
            @Override
            public boolean onSingleTapConfirmed(MotionEvent motionEvent) {
                return false;
            }
    
            @Override
            public boolean onDoubleTap(MotionEvent motionEvent) {
                return false;
            }
    
            @Override
            public boolean onDoubleTapEvent(MotionEvent motionEvent) {
                return false;
            }
        }

    5.缩放后手势滑动识别的实现,同样是在view中有ScaleGestureDetector.SimpleOnScaleGestureListener的接口实现之后运用坐标系的改造,通过坐标的算出比例值设置setScaleX、setScaleY,就可以实现手势的缩放是实现:

    mScaleGestureDetector = new ScaleGestureDetector(getContext(), new ScaleGestureListener());         //设置手势缩放的监听
    
     private class ScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
            private float mStartFocusX;
            private float mStartFocusY;
            private float mStartScale;       
            private int mStartScrollX;
            private int mStartScrollY;
    
            @Override
            public boolean onScaleBegin(ScaleGestureDetector detector) {
                mStartFocusX = detector.getFocusX();
                mStartFocusY = detector.getFocusY();
                mStartScrollX = getScrollX();
                mStartScrollY = getScrollY();
    
                mStartScale = mViewScale;
                return true;
            }
    
            @Override
            public boolean onScale(ScaleGestureDetector detector) {
                final float oldViewScale = mViewScale;
    
                final float scaleFactor = detector.getScaleFactor();
                mViewScale *= scaleFactor;
    
                if (mViewScale < ZOOM_SCALES[0]) {
                    mCurrentZoomScaleIndex = 0;
                    mViewScale = ZOOM_SCALES[mCurrentZoomScaleIndex];
                } else if (mViewScale > ZOOM_SCALES[ZOOM_SCALES.length - 1]) {
                    mCurrentZoomScaleIndex = ZOOM_SCALES.length - 1;
                    mViewScale = ZOOM_SCALES[mCurrentZoomScaleIndex];
                } else {             
                    float minDist = Float.MAX_VALUE;
                    int index = ZOOM_SCALES.length - 1;
                    for (int i = 0; i < ZOOM_SCALES.length; i++) {
                        float dist = Math.abs(mViewScale - ZOOM_SCALES[i]);
                        if (dist < minDist) {
                            minDist = dist;
                        } else {                       
                            index = i - 1;
                            break;
                        }
                    }
                    mCurrentZoomScaleIndex = index;
                }         
               ActionEditorCanvasView.this.setScaleX(mViewScale);
                ActionEditorCanvasView.this.setScaleY(mViewScale);           
                final float scaleDifference = mViewScale - mStartScale;
                final int scrollScaleX = (int) (scaleDifference * mStartFocusX);
                final int scrollScaleY = (int) (scaleDifference * mStartFocusY);
                final int scrollPanX = (int) (mStartFocusX - detector.getFocusX());
                final int scrollPanY = (int) (mStartFocusY - detector.getFocusY());
                scrollTo(mStartScrollX + scrollScaleX + scrollPanX,
                        mStartScrollY + scrollScaleY + scrollPanY);
                return true;
            }
        }
    四.核心类的分析:
    1.CDrawable接口,只要实现这个接口的控件就可以绘制到自定义的画布中:

    CDrawable {
        Paint getPaint();                   //获取Paint
        int getXcoords();                   //获取X轴坐标
        int getYcoords();                   //获取Y轴坐标
        void setXcoords(int x);             //设置X轴坐标
        void setYcoords(int y);             //设置Y轴坐标
        void setPaint(Paint p);             //设置画笔
        void draw(Canvas canvas);           //绘制画板
    }
      2.FabricView抽象类继承于View,主要功能是把控件绘制到这个画布中、绘制画布的背景、区分了几种绘制模式。

    public abstract  class FabricView extends View {
        // painting objects and properties
        public ArrayList<CDrawable> mDrawableList = new ArrayList<>();
        private int mColor = Color.BLACK;
    
        // Canvas interaction modes
        private int mInteractionMode = DRAW_MODE;
    
        // background color of the library
        private int mBackgroundColor = Color.WHITE;
        // default style for the library
        private Paint.Style mStyle = Paint.Style.STROKE;
    
        // default stroke size for the library    默认点击的尺寸
        private float mSize = 5f;
    
        // flag indicating whether or not the background needs to be redrawn
        private boolean mRedrawBackground;
    
        // background mode for the library, default to blank
        private int mBackgroundMode = BACKGROUND_STYLE_BLANK;
    
        // Default Notebook left line color
        public static final int NOTEBOOK_LEFT_LINE_COLOR = Color.RED;
    
        // Flag indicating that we are waiting for a location for the text
        private boolean mTextExpectTouch;
    
        // Vars to decrease dirty area and increase performance
        private float lastTouchX, lastTouchY;
        private final RectF dirtyRect = new RectF();
        
        // keep track of path and paint being in use
        CPath currentPath;
        Paint currentPaint;
    
        /*********************************************************************************************/
        /************************************     FLAGS    *******************************************/
        /*********************************************************************************************/
        // Default Background Styles         背景颜色
        public static final int BACKGROUND_STYLE_BLANK = 0;
        public static final int BACKGROUND_STYLE_NOTEBOOK_PAPER = 1;
        public static final int BACKGROUND_STYLE_GRAPH_PAPER = 2;
    
        // Interactive Modes
        public static final int DRAW_MODE = 0;
        public static final int SELECT_MODE = 1; // TODO Support Object Selection.
        public static final int ROTATE_MODE = 2; // TODO Support Object ROtation.
        public static final int LOCKED_MODE = 3;
    
        /*********************************************************************************************/
        /**********************************     CONSTANTS    *****************************************/
        /*********************************************************************************************/
        public static final int NOTEBOOK_LEFT_LINE_PADDING = 120;
    
        /*********************************************************************************************/
        /************************************     TO-DOs    ******************************************/
        /*********************************************************************************************/
        private float mZoomLevel = 1.0f; //TODO Support Zoom                要去做支持放大缩小的功能
        private float mHorizontalOffset = 1, mVerticalOffset = 1; // TODO Support Offset and Viewport  支持可以偏移的功能
        public int mAutoscrollDistance = 100; // TODO Support Autoscroll
    
    
        /**
         * Default Constructor, sets sane values.
         *
         * @param context the activity that containts the view
         * @param attrs   view attributes
         */
        public FabricView(Context context, AttributeSet attrs) {
            super(context, attrs);
            setFocusable(true);
            setFocusableInTouchMode(true);
            this.setBackgroundColor(mBackgroundColor);
            mTextExpectTouch = false;
    
        }
    
        /**
         * Called when there is the canvas is being re-drawn.
         */
        @Override
        protected void onDraw(Canvas canvas) {
            // check if background needs to be redrawn
            mBackgroundMode=BACKGROUND_STYLE_NOTEBOOK_PAPER;
            drawBackground(canvas, mBackgroundMode);
    
            // go through each item in the list and draw it
            for (int i = 0; i < mDrawableList.size(); i++) {
                try {
                    mDrawableList.get(i).draw(canvas);
                }
    
                catch(Exception ex)
                {
    
                }
            }
        }
    
    
        /*********************************************************************************************/
        /*******************************     Handling User Touch    **********************************/
        /*********************************************************************************************/
    
        /**
         * Handles user touch event
         *
         * @param event the user's motion event
         * @return true, the event is consumed.
         */
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            // delegate action to the correct method
            if (getInteractionMode() == DRAW_MODE)                  //绘画的模式
                return onTouchDrawMode(event);
            else if (getInteractionMode() == SELECT_MODE)           //选择的模式
                return onTouchSelectMode(event);
            else if (getInteractionMode() == ROTATE_MODE)
                return onTouchRotateMode(event);
            // if none of the above are selected, delegate to locked mode
            else
                return onTouchLockedMode(event);
        }
    
        /**
         * Handles touch event if the mode is set to locked
         * @param event the event to handle
         * @return false, shouldn't do anything with it for now
         */
        private boolean onTouchLockedMode(MotionEvent event) {
            // return false since we don't want to do anything so far
            return true;
        }
    
        /**
         * Handles the touch input if the mode is set to rotate
         * @param event the touch event
         * @return the result of the action
         */
        private boolean onTouchRotateMode(MotionEvent event) {
            return false;
        }
    
    
        /**
         * Handles the touch input if the mode is set to draw
         * @param event the touch event
         * @return the result of the action
         */
        public boolean onTouchDrawMode(MotionEvent event)
        {
            // get location of touch
            float eventX = event.getX();
            float eventY = event.getY();
            Log.i("everb", "划线或的位置X信息:"+ event.getX()+"划线或的位置Y信息:"+eventY);
    
    
            // based on the users action, start drawing
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    // create new path and paint
                    currentPath = new CPath();
                    currentPaint = new Paint();
                    currentPaint.setAntiAlias(true);
                    currentPaint.setColor(mColor);
                    currentPaint.setStyle(mStyle);
                    currentPaint.setStrokeJoin(Paint.Join.ROUND);
                    currentPaint.setStrokeWidth(mSize);
                    currentPath.moveTo(eventX, eventY);
                    currentPath.setPaint(currentPaint);
                    // capture touched locations
                    lastTouchX = eventX;
                    lastTouchY = eventY;
                    mDrawableList.add(currentPath);
                    return true;
                case MotionEvent.ACTION_MOVE:
                case MotionEvent.ACTION_UP:
                    currentPath.lineTo(eventX, eventY);
                    // When the hardware tracks events faster than they are delivered, the
                    // event will contain a history of those skipped points.
                    int historySize = event.getHistorySize();
                    for (int i = 0; i < historySize; i++) {
                        float historicalX = event.getHistoricalX(i);
                        float historicalY = event.getHistoricalY(i);
                        if (historicalX < dirtyRect.left) {
                            dirtyRect.left = historicalX;
                        } else if (historicalX > dirtyRect.right) {
                            dirtyRect.right = historicalX;
                        }
                        if (historicalY < dirtyRect.top) {
                            dirtyRect.top = historicalY;
                        } else if (historicalY > dirtyRect.bottom) {
                            dirtyRect.bottom = historicalY;
                        }
                        currentPath.lineTo(historicalX, historicalY);
                    }
    
                    // After replaying history, connect the line to the touch point.
                    currentPath.lineTo(eventX, eventY);
                    cleanDirtyRegion(eventX, eventY);
                    break;
                default:
                    return false;
            }
    
            // Include some padding to ensure nothing is clipped
            invalidate(
                    (int) (dirtyRect.left - 20),
                    (int) (dirtyRect.top - 20),
                    (int) (dirtyRect.right + 20),
                    (int) (dirtyRect.bottom + 20));
    
            // register most recent touch locations
            lastTouchX = eventX;
            lastTouchY = eventY;
            return true;
        }
    
        /**
         * Handles the touch input if the mode is set to select
         * @param event the touch event
         */
        private boolean onTouchSelectMode(MotionEvent event) {
            // TODO Implement Method
            return false;
        }
    
    
        /*******************************************
         * Drawing Events
         ******************************************/
        /**
         * Draw the background on the canvas
         * @param canvas the canvas to draw on
         * @param backgroundMode one of BACKGROUND_STYLE_GRAPH_PAPER, BACKGROUND_STYLE_NOTEBOOK_PAPER, BACKGROUND_STYLE_BLANK
         */
        public void drawBackground(Canvas canvas, int backgroundMode) {
            canvas.drawColor(mBackgroundColor);
            if(backgroundMode != BACKGROUND_STYLE_BLANK) {
                Paint linePaint = new Paint();
                linePaint.setColor(Color.argb(50, 0, 0, 0));
                linePaint.setStyle(mStyle);
                linePaint.setStrokeJoin(Paint.Join.ROUND);
                linePaint.setStrokeWidth(mSize - 2f);
                switch (backgroundMode) {
                    case BACKGROUND_STYLE_GRAPH_PAPER:
                        drawGraphPaperBackground(canvas, linePaint);
                        break;
                    case BACKGROUND_STYLE_NOTEBOOK_PAPER:
                        drawNotebookPaperBackground(canvas, linePaint);
                    default:
                        break;
                }
            }
            mRedrawBackground = false;
        }
    
        /**
         * Draws a graph paper background on the view
         * @param canvas the canvas to draw on
         * @param paint the paint to use
         */
        private void drawGraphPaperBackground(Canvas canvas, Paint paint) {
            int i = 0;
            boolean doneH = false, doneV = false;
    
            // while we still need to draw either H or V
            while (!(doneH && doneV)) {
    
                // check if there is more H lines to draw
                if (i < canvas.getHeight())
                    canvas.drawLine(0, i, canvas.getWidth(), i, paint);
                else
                    doneH = true;
    
                // check if there is more V lines to draw
                if (i < canvas.getWidth())
                    canvas.drawLine(i, 0, i, canvas.getHeight(), paint);
                else
                    doneV = true;
    
                // declare as done
                i += 75;
            }
        }
    
        /**
         * Draws a notebook paper background on the view
         * @param canvas the canvas to draw on
         * @param paint the paint to use
         */
        private void drawNotebookPaperBackground(Canvas canvas, Paint paint) {
            int i = 0;
            boolean doneV = false;
            // draw horizental lines
            while (!(doneV)) {
                if (i < canvas.getHeight())
                    canvas.drawLine(0, i, canvas.getWidth(), i, paint);
                else
                    doneV = true;
                i += 75;
            }
            // change line color
            paint.setColor(NOTEBOOK_LEFT_LINE_COLOR);
            // draw side line
            canvas.drawLine(NOTEBOOK_LEFT_LINE_PADDING, 0,
                    NOTEBOOK_LEFT_LINE_PADDING, canvas.getHeight(), paint);
    
    
        }
    
        /**
         * Draw text on the screen
         * @param text the text to draw
         * @param x the x location of the text
         * @param y the y location of the text
         * @param p the paint to use
         */
        public void drawText(String text, int x, int y, Paint p) {
            mDrawableList.add(new CText(text, x, y, p));
            invalidate();
        }
    
        /**
         * Capture Text from the keyboard and draw it on the screen
         * //TODO Implement the method
         */
        private void drawTextFromKeyboard() {
            Toast.makeText(getContext(), "Touch where you want the text to be", Toast.LENGTH_LONG).show();
            //TODO
            mTextExpectTouch = true;
        }
    
        /**
         * Retrieve the region needing to be redrawn
         * @param eventX The current x location of the touch
         * @param eventY the current y location of the touch
         */
        private void cleanDirtyRegion(float eventX, float eventY) {
            // figure out the sides of the dirty region
            dirtyRect.left = Math.min(lastTouchX, eventX);
            dirtyRect.right = Math.max(lastTouchX, eventX);
            dirtyRect.top = Math.min(lastTouchY, eventY);
            dirtyRect.bottom = Math.max(lastTouchY, eventY);
        }
    
        /**
         * Clean the canvas, remove everything drawn on the canvas.
         */
        public void cleanPage() {
            // remove everything from the list
            while (!(mDrawableList.isEmpty())) {
                mDrawableList.remove(0);
            }
            // request to redraw the canvas
            invalidate();
        }
    
        /**
         * Draws an image on the canvas
         *
         * @param x      location of the image
         * @param y      location of the image
         * @param width  the width of the image
         * @param height the height of the image
         * @param pic    the image itself
         */
        public void drawImage(int x, int y, int width, int height, Bitmap pic) {
            CBitmap bitmap = new CBitmap(pic, x, y);
            bitmap.setWidth(width);
            bitmap.setHeight(height);
            mDrawableList.add(bitmap);
            invalidate();
        }
    
    
        /*******************************************
         * Getters and Setters
         ******************************************/
    
    
        /**
         * Gets what has been drawn on the canvas so far as a bitmap
         * @return Bitmap of the canvas.
         */
        public Bitmap getCanvasBitmap()
        {
            // build drawing cache of the canvas, use it to create a new bitmap, then destroy it.
            buildDrawingCache();
            Bitmap mCanvasBitmap = Bitmap.createBitmap(getDrawingCache());
            destroyDrawingCache();
    
            // return the created bitmap.
            return mCanvasBitmap;
        }
    
        public int getColor() {
            return mColor;
        }
    
        public void setColor(int mColor) {
            this.mColor = mColor;
        }
    
        public int getBackgroundColor() {
            return mBackgroundColor;
        }
    
        public int getBackgroundMode() {
            return mBackgroundMode;
        }
    
        public void setBackgroundMode(int mBackgroundMode) {
            this.mBackgroundMode = mBackgroundMode;
            invalidate();
        }
    
        public void setBackgroundColor(int mBackgroundColor) {
            this.mBackgroundColor = mBackgroundColor;
        }
    
        public Paint.Style getStyle() {
            return mStyle;
        }
    
        public void setStyle(Paint.Style mStyle) {
            this.mStyle = mStyle;
        }
    
        public float getSize() {
            return mSize;
        }
    
        public void setSize(float mSize) {
            this.mSize = mSize;
        }
    
    
        public int getInteractionMode() {
            return mInteractionMode;
        }
    
        public void setInteractionMode(int interactionMode) {
    
            // if the value passed is not any of the flags, set the library to locked mode
            if (interactionMode > LOCKED_MODE)
                interactionMode = LOCKED_MODE;
            else if (interactionMode < DRAW_MODE)
                interactionMode = LOCKED_MODE;
    
            this.mInteractionMode = interactionMode;
        }
    }

    3.接下来在上述两个类和接口的基础上,定制自己的FabricCanvasView画布:

    package com.lejurobot.aelos.aelosmini.view;
    
    import android.content.Context;
    import android.graphics.Point;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.GestureDetector;
    import android.view.MotionEvent;
    import android.view.ScaleGestureDetector;
    import android.widget.Toast;
    
    import com.lejurobot.aelos.aelosmini.activities.ActionEditorActivity;
    import com.lejurobot.aelos.aelosmini.view.FabricView.CDrawable;
    import com.lejurobot.aelos.aelosmini.view.FabricView.FabricView;
    
    
    /**
     * @author wangyao
     * @package com.example.administrator.myapplication
     * @date 2017/6/27  14:05
     * @describe 实现一个画布的放置控件、缩放画布、控件之间的连线、画布的手势识别、缩放后手势滑动的识别
     * @project
     */
    
    public class ActionEditorCanvasView extends FabricView {
    
        private Context mContext;
        // Scale and zoom in/out factor.
        private static final int INIT_ZOOM_SCALES_INDEX = 0;
        private int mCurrentZoomScaleIndex = INIT_ZOOM_SCALES_INDEX;
        private static final float[] ZOOM_SCALES = new float[]{1.0f, 1.25f, 1.5f, 1.75f, 2.0f};
        private float mViewScale = ZOOM_SCALES[INIT_ZOOM_SCALES_INDEX];
    
        protected boolean mScrollable = true;
        private ScaleGestureDetector mScaleGestureDetector;                 //缩放手势
        private GestureDetector mTapGestureDetector;                        //手势监听类
        private int mPanningPointerId = MotionEvent.INVALID_POINTER_ID;
        private Point mPanningStart = new Point();
        private int mOriginalScrollX;
        private int mOriginalScrollY;
        private float mOffSetViewScroll;
    
        // Default desired width of the view in pixels.
        private static final int DESIRED_WIDTH = 2048;
        // Default desired height of the view in pixels.
        private static final int DESIRED_HEIGHT = 2048;
        // Interactive Modes
        public static final int DRAW_MODE = 0;              //可以绘制线段的模式
        public static final int SELECT_MODE = 1; // TODO Support Object Selection.
        public static final int ROTATE_MODE = 2; // TODO Support Object ROtation.
        public static final int LOCKED_MODE = 3;              //空模式
    
        // Default Background Styles         背景颜色
        public static final int BACKGROUND_STYLE_BLANK = 0;
        public static final int BACKGROUND_STYLE_NOTEBOOK_PAPER = 1;
        public static final int BACKGROUND_STYLE_GRAPH_PAPER = 2;
    
    
        public ActionEditorCanvasView(Context context, AttributeSet attrs) {
            super(context, attrs);
            this.mContext=context;
        }
    
        /**
         * The method onFinishInflate() will be called after all children have been added.
         * 这个方法是所有的子view被添加之后调用
         */
        @Override
        public void onFinishInflate() {
            super.onFinishInflate();
    
            // Setting the child view's pivot point to (0,0) means scaling leaves top-left corner in
            // place means there is no need to adjust view translation.
            this.setPivotX(0);
            this.setPivotY(0);
    
            setWillNotDraw(false);
            setHorizontalScrollBarEnabled(mScrollable);                 //水平滑动滚动条的设置
            setVerticalScrollBarEnabled(mScrollable);                   //竖直滑动滚动条的设置
            setBackgroundMode(BACKGROUND_STYLE_GRAPH_PAPER);
            mScaleGestureDetector = new ScaleGestureDetector(getContext(), new ScaleGestureListener());         //设置手势缩放的监听
    
            mTapGestureDetector = new GestureDetector(getContext(), new TapGestureListener());          //设置手势的监听
    
        }
    
        public void setBackgroundMode(int mBackgroundMode) {
            super.setBackgroundMode(mBackgroundMode);
        }
    
        /**
         * 设置画布所处在的模式,可以添加并设置其他的模式
         */
        public void setInteractionMode(int interactionMode) {
            if (interactionMode == DRAW_MODE) {
                super.setInteractionMode(DRAW_MODE);
            } else if (interactionMode == LOCKED_MODE) {
                super.setInteractionMode(LOCKED_MODE);
            }else if (interactionMode==SELECT_MODE){
                super.setInteractionMode(SELECT_MODE);
            }
    
        }
    
        /**
         * 增加画布里面的控件或其他实现了CDrawable接口的类
         */
        public boolean addCanvasDrawable(CDrawable cDrawable) {
            super.mDrawableList.add(cDrawable);
            return true;
        }
    
        /**
         * 清除画布里面的控件
         */
        public void cleanPager() {
            super.cleanPage();
        }
    
        /**
         * 重置画布的大小尺寸
         */
        public void resetView() {
            // Reset scrolling state.
            mPanningPointerId = MotionEvent.INVALID_POINTER_ID;
            mPanningStart.set(0, 0);
            mOriginalScrollX = 0;
            mOriginalScrollY = 0;
            updateScaleStep(INIT_ZOOM_SCALES_INDEX);
            scrollTo((int) this.getX(), (int) this.getY());
        }
    
    
        @Override
        public boolean onTouchDrawMode(MotionEvent event) {
            event.offsetLocation(getScrollX(), getScrollY());                //缩放后偏移的距离,保证缩放后触点跟缩放前对应
            return super.onTouchDrawMode(event);
        }
    
        @Override
        public boolean onTouchSelectMode(MotionEvent event) {
            return super.onTouchSelectMode(event);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            mScaleGestureDetector.onTouchEvent(event);
            mTapGestureDetector.onTouchEvent(event);
            // TODO: 2017/6/29 可根据触发事件做到动作控件的拖动
            return super.onTouchEvent(event);
        }
    
        /**
         * 画布实现缩小的方法
         *
         * @return
         */
        public boolean zoomOut() {
    //        if (mScrollable && mCurrentZoomScaleIndex > 0) {
            if (mCurrentZoomScaleIndex > 0) {
                updateScaleStep(mCurrentZoomScaleIndex - 1);
                return true;
            }
            return false;
        }
    
    
        /**
         * 画布实现放大的方法
         *
         * @return
         */
        public boolean zoomIn() {
            if (mCurrentZoomScaleIndex < ZOOM_SCALES.length - 1) {
                updateScaleStep(mCurrentZoomScaleIndex + 1);
                return true;
            }
            return false;
        }
    
        /**
         * 缩放的具体实现
         *
         * @param newScaleIndex
         */
        private void updateScaleStep(int newScaleIndex) {
            if (newScaleIndex != mCurrentZoomScaleIndex) {
                final float oldViewScale = mViewScale;
    
                mCurrentZoomScaleIndex = newScaleIndex;
                mViewScale = ZOOM_SCALES[mCurrentZoomScaleIndex];
    
                final float scaleDifference = mViewScale - oldViewScale;
                scrollBy((int) (scaleDifference * getMeasuredWidth() / 2),
                        (int) (scaleDifference * getMeasuredHeight() / 2));
    
                this.setScaleX(mViewScale);
                this.setScaleY(mViewScale);
                this.requestLayout();
            }
        }
    
        @Override
        public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            setMeasuredDimension(
                    getMeasuredSize(widthMeasureSpec, DESIRED_WIDTH),
                    getMeasuredSize(heightMeasureSpec, DESIRED_HEIGHT));
    
        }
    
        private static int getMeasuredSize(int measureSpec, int desiredSize) {
            int mode = MeasureSpec.getMode(measureSpec);
            int size = MeasureSpec.getSize(measureSpec);
    
            if (mode == MeasureSpec.EXACTLY) {
                return size;
            } else if (mode == MeasureSpec.AT_MOST) {
                return Math.min(size, desiredSize);
            } else {
                return desiredSize;
            }
    
        }
    
    
        private class TapGestureListener implements GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener {
    
            @Override
            public boolean onDown(MotionEvent motionEvent) {
                return false;
            }
    
            @Override
            public void onShowPress(MotionEvent motionEvent) {
    
            }
    
            @Override
            public boolean onSingleTapUp(MotionEvent motionEvent) {
                return false;
            }
    
            @Override
            public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
                return false;
            }
    
            @Override
            public void onLongPress(MotionEvent motionEvent) {
                // TODO: 2017/6/29 长按可以设置模式为连线
                Toast.makeText(mContext,"实现了长按手势",Toast.LENGTH_SHORT).show();
            }
    
            @Override
            public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
                return false;
            }
    
            @Override
            public boolean onSingleTapConfirmed(MotionEvent motionEvent) {
                return false;
            }
    
            @Override
            public boolean onDoubleTap(MotionEvent motionEvent) {
                return false;
            }
    
            @Override
            public boolean onDoubleTapEvent(MotionEvent motionEvent) {
                return false;
            }
        }
    
        private class ScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
            // Focus point at the start of the pinch gesture. This is used for computing proper scroll
            // offsets during scaling, as well as for simultaneous panning.
            private float mStartFocusX;
            private float mStartFocusY;
            // View scale at the beginning of the gesture. This is used for computing proper scroll
            // offsets during scaling.
            private float mStartScale;
            // View scroll offsets at the beginning of the gesture. These provide the reference point
            // for adjusting scroll in response to scaling and panning.
            private int mStartScrollX;
            private int mStartScrollY;
    
            @Override
            public boolean onScaleBegin(ScaleGestureDetector detector) {
                mStartFocusX = detector.getFocusX();
                mStartFocusY = detector.getFocusY();
                mStartScrollX = getScrollX();
                mStartScrollY = getScrollY();
    
                mStartScale = mViewScale;
                return true;
            }
    
            @Override
            public boolean onScale(ScaleGestureDetector detector) {
                final float oldViewScale = mViewScale;
    
                final float scaleFactor = detector.getScaleFactor();
                mViewScale *= scaleFactor;
    
                if (mViewScale < ZOOM_SCALES[0]) {
                    mCurrentZoomScaleIndex = 0;
                    mViewScale = ZOOM_SCALES[mCurrentZoomScaleIndex];
                } else if (mViewScale > ZOOM_SCALES[ZOOM_SCALES.length - 1]) {
                    mCurrentZoomScaleIndex = ZOOM_SCALES.length - 1;
                    mViewScale = ZOOM_SCALES[mCurrentZoomScaleIndex];
                } else {
                    // find nearest zoom scale
                    float minDist = Float.MAX_VALUE;
                    // If we reach the end the last one was the closest
                    int index = ZOOM_SCALES.length - 1;
                    for (int i = 0; i < ZOOM_SCALES.length; i++) {
                        float dist = Math.abs(mViewScale - ZOOM_SCALES[i]);
                        if (dist < minDist) {
                            minDist = dist;
                        } else {
                            // When it starts increasing again we've found the closest
                            index = i - 1;
                            break;
                        }
                    }
                    mCurrentZoomScaleIndex = index;
                }
    
              /*  if (shouldDrawGrid()) {
                    mGridRenderer.updateGridBitmap(mViewScale);
                }*/
    
               ActionEditorCanvasView.this.setScaleX(mViewScale);
                ActionEditorCanvasView.this.setScaleY(mViewScale);
    
                // Compute scroll offsets based on difference between original and new scaling factor
                // and the focus point where the gesture started. This makes sure that the scroll offset
                // is adjusted to keep the focus point in place on the screen unless there is also a
                // focus point shift (see next scroll component below).
                final float scaleDifference = mViewScale - mStartScale;
                final int scrollScaleX = (int) (scaleDifference * mStartFocusX);
                final int scrollScaleY = (int) (scaleDifference * mStartFocusY);
    
                // Compute scroll offset based on shift of the focus point. This makes sure the view
                // pans along with the focus.
                final int scrollPanX = (int) (mStartFocusX - detector.getFocusX());
                final int scrollPanY = (int) (mStartFocusY - detector.getFocusY());
    
                // Apply the computed scroll components for scale and panning relative to the scroll
                // coordinates at the beginning of the gesture.
                scrollTo(mStartScrollX + scrollScaleX + scrollPanX,
                        mStartScrollY + scrollScaleY + scrollPanY);
    
                return true;
            }
        }
    }

    五、自己根据这实现的基本实现的自定义的画布,来满足项目的需求:

      项目地址为:http://download.csdn.net/download/wangyongyao1989/9886503

    六、关于所用的方法总结:

    1.setScaleX、setScaleY:这是View中方法,设定View轴心点缩放能用于缩放View的大小;

    2.offsetLocation(float deltaX,float deltaY):调整event的位置,参数的含义是调整横纵坐标的在event的数值;

    3.getScrollX(),getScrollY():是View中的方法,返回View在坐标系中横纵坐标的数值;

    4.getAction:获取触摸时间的类型值,比如说ACTION_DOWN、ACTION_MOVE、ACTION_UP等的动作;

    5.getActionMask:返回正在执行被屏蔽的操作,没有指针索引信息。

    6.getActionIndex:返回ACTION_POINTER_DOWN、ACTION_POINTER_MOVE、ACTION_POINTER_UP相关的指针;

    7.getHistoricalX.getHistoricalY:返回在native中存储的坐标的值,比getX()/getY()获取得粒度更细。

    8.


    展开全文
  • HTML5 保存画布

    2018-04-25 22:00:37
    保存画布好不容易在画布中绘制出酷炫的图形,应该把它保存起来吧。但不幸的是,画布中的这些图形本身不是真正的图片,不能直接保存。不幸中的万幸,Canvas API提供了toDataURL()方法,可以把画布中的图形转换为图片...

    保存画布

    好不容易在画布中绘制出酷炫的图形,应该把它保存起来吧。但不幸的是,画布中的这些图形本身不是真正的图片,不能直接保存。不幸中的万幸,Canvas API提供了toDataURL()方法,可以把画布中的图形转换为图片。

    默认情况下,toDataURL()方法把图形转变成base64编码格式的png,然后返回Data URL数据。可以给toDataURL()传入MIME类型的参数,将画布转变成其它格式的图片。

    有了Data URL数据后,就可将这些数据直接填充到<img>元素里,或者直接从浏览器里下载它们。这里通过一个实例,来说明如何使用toDataURL()方法,把整个画布保存为图片。

    HTML代码如下:

    1. <button onclick=toDataURL("image/png")>显示为png图片</button>
    2. <button onclick=toDataURL("image/jpeg")>显示为jpg图片</button>
    3. <canvas id="canvas" width="200" height="200"></canvas>
    4. <img id="image"/>

    Javascript代码如下:

    1. function toDataURL(mime) {
    2.    var canvas = document.getElementById("canvas");
    3.    var image = document.getElementById("image");
    4.    image.src = canvas.toDataURL(mime);
    5. }

    上述代码中,当用户点击按钮“显示为png图片”或“显示为jpg图片”时,会调用toDataURL()方法,把画布中的内容生成图片,填充到img标签中供用户下载。

    说明:

    1、toDataURL()方法是画布元素自身的方法,而非画布上下文对象的方法。

    2、toDataURL()方法保存图片的默认格式是"image/png",浏览器对其他格式支持不是很好。如,Google Chrome浏览器41.0.2272.118版本也支持"image/jpeg"格式,但不支持"image/gif"格式。

    2、现代浏览器已经支持在Canvas上右键,把画布另存为图片,不过会按默认的"image/png"格式保存。当然,可以通过编程,调用toDataURL()方法,传入MIME类型的参数,将其保存为其它格式。

    关于作者

    歪脖先生,十五年以上软件开发经验,酷爱Web开发,精通 HTML、CSS、JavaScript、jQuery、JSON、Python、Less、Bootstrap等,著有《HTML宝典》、《揭秘CSS》、《Less简明教程》、《JSON教程》、《Bootstrap2用户指南》,并全部在 GitHub 上开源。

    版权声明:本文出自 歪脖网 的《HTML宝典》,欢迎在线阅读,并提出宝贵意见。

    展开全文
  • H5的canvas画布使用

    千次阅读 2017-08-14 22:46:54
    H5的canvas canvas画布 canvas上绘制图形 canvas给绘制的图形设置样式 canvas中绘制图像 canvas中的变形 canvas标签
  • 商业画布

    千次阅读 2019-09-03 10:54:22
    什么是商业画布商业模式画布指一种能够帮助团队催生创意、降低猜测、确保他们找对了目标用户、合理解决问题的工具。商业模式画布使得商业模式可视化,使用统一的语言讨论不同商业领域。商业模式画布不仅能够提供更多...
  • 商业模式画布

    万次阅读 2018-07-22 18:52:11
    周四周五机缘巧合参加了一场关于商业模式画布的培训,第一次参加趋向于商业方向的培训,一开始并没有抱太大的期望。但是听完两天的课之后,第一次给培训的老师全是满分的评价,因为确实内容非常受用,两天的课程中即...
  • Android canvas 画布详细讲解

    千次阅读 2019-07-22 19:20:10
    文章目录1、简介2、常用的绘画方法 draw* ...比较清楚详细的讲解画布的一些基础方法,一些参数的含义,让读者能够更清楚地使用 canvas 绘制想要的图形。 2、常用的绘画方法 draw* 待续····· ...
  • 商业画布模板

    2017-06-14 12:48:01
    商业画布模板
  • 价值主张画布

    千次阅读 2019-06-25 19:18:50
    一、价值主张画布概况 首先,什么是价值主张? 价值主张是整个商业模式的核心,它描述了产品提供的价值和顾客需求之间如何建立联系以及为什么顾客要去买你的产品。 而价值主张画布是在《价值主张设计》一书中,由...
  • android开发 之 Canvas之画布操作

    千次阅读 2017-08-02 16:47:06
    一.Canvas的常用操作速查表 操作类型 ... 使用单一颜色填充整个画布 绘制基本形状 drawPoint, drawPoints, drawLine, drawLines, drawRect, drawRoundRect, drawOval, drawCircle, draw...
  • 【微信小程序】之画布

    千次阅读 2019-04-25 17:32:10
    同一页面中的 canvas-id 不可重复,如果使用一个已经出现过的 canvas-id,该 canvas 标签对应的画布将被隐藏并不再正常工作 属性名 类型 默认值 说明 canvas-id String canvas组件的唯一标识符 disable-...
  • 自定义控件---图层,画布和canvas

    千次阅读 2017-11-09 14:34:20
     每一次调用canvas.drawXXX系列函数时,都会生成一个新的透明图层来专门来画这个图形,然后按照绘制的顺序覆盖到画布上。调用完方法就覆盖到画布上了。如果我们连续调用五个draw函数,那么就会生成五个透明图层,画...
  • 商业模式画布.pdf

    2019-11-27 18:23:42
    商业模式画布的培训材料,PPT转的PDF文档。非常详细的商业模式画布的介绍材料,含详细的制图、定义说明、示例。内部培训参考。
  • 精益创业《精益创业》是一本书,主要讲解创业、创新和产品的理念和方法论,它把创业上升到了理论层面,更加科学化和系统化。精益创业提到的三个主要工具是:“最小可用品”、“客户反馈”、“快速迭代”。...
  • 互联网商业画布

    2017-12-11 10:44:02
    互联网商业画布互联网商业画布互联网商业画布互联网商业画布互联网商业画布互联网商业画布互联网商业画布互联网商业画布互联网商业画布互联网商业画布互联网商业画布互联网商业画布互联网商业画布互联网商业画布...
  • 1.在自定义的画布中实现可缩放手势,摇一摇可对控件进行整理排序; 2.画布中可以添加位置设定的控件,控件可以响应点击、长按、拖动事件; 3.控件A长按事件会隐藏画布中的控件除了A之外,显示另一个控件B;当A在在...
  • 创建画布

    千次阅读 2016-05-16 18:00:54
    在开发Web项目时,图片是前台页面不可缺少的组成部分,传统...创建画布  GD2函数库在图形图像绘制方面功能非常强大,开发人员即可以在已有的图片的基础上经行绘制,也可以在没有任何素材的基础上绘制,在这种情况下

空空如也

1 2 3 4 5 ... 20
收藏数 111,232
精华内容 44,492
关键字:

画布