• canvas绘图


    HTML5添加的最受欢迎的元素,就是<canvas>元素,这个元素负责在页面中设置一个区域,然后可以通过javascript动态的在这个区域中绘制图形;

    使用<canvas>元素,必须先设置其width和height属性,指定绘图区域大小;出现在canvas开头和结束标签之间的内容,会在不支持canvas元素的情况下显示;

    写一个简单的canvas元素:

    <canvas id="drawing" width="200" height="200">不支持canvas时,会显示canvas标签中的内容</canvas>

    canvas元素对应的DOM元素对象也有width和height属性,可以随意修改,也可以通过css添加样式,如果不添加任何样式或不绘制任何图形,在页面中是看不到该元素的;

    也就是,也可以通过css设置canvas元素的宽高属性:

    <style>
       #drawing{
           border:1px solid red;
           background:yellow;
           width:300px;
           height:300px;
       } 
    </style>
    <body>
        <canvas id="drawing">不支持canvas时,会显示canvas标签中的内容</canvas>    
    <script>
    </script>
    </body>

    要在这块儿画布上绘图,需要取得绘图上下文。而取得绘图上下文的引用,需要调用getContext()方法并传入上下文的名字。传入“2d”,就可以获得2D上下文对象;

    toDataURL()方法,可以导出在canvas元素上绘制的图像。这个方法接受一个参数,即图像的MIME类型格式,而且适合用于创建图像的任何上下文。比如要取得画布中的一副PNG格式的图像,可以使用一下代码:

    <body>
        <canvas id="drawing" width="300" height="300">不支持canvas时,会显示canvas标签中的内容</canvas>
    
    <script>
        var drawing=document.getElementById("drawing");
        //确定浏览器支持canvas元素
        if(drawing.getContext){
            var context=drawing.getContext("2d");//获取2D上下文
            //取得图像的数据URI
            var imgURI=drawing.toDataURL("image/png");
            //显示图像
            var image=document.createElement("img");
            image.src=imgURI;
            document.body.appendChild(image);
        }
    </script>
    </body>

     默认情况下,浏览器会将图片编码为PNG格式(除非另行制定);

    Firefox和Opera也支持基于“image/jpeg”参数的JPEG编码格式,如果绘制到画布上的图像源于不同的域,toDataURL()方法也会抛出错误,后续详细介绍;

    2D上下文

    使用2D绘图上下文提供的方法,可以绘制简单的2D图形,比如矩形、弧线、和路径。2D上下文的坐标开始于canvas元素的左上角,原点坐标是(0,0)。所有坐标值都基于这个原点计算,x值越大表示越靠右,y值越大表示越靠下。默认情况下,width和height表示水平和垂直方向上可用的像素数目。

    填充和描边

    2D上下文的两种基本绘图操作是填充和描边。填充就是用指定的样式(颜色、渐变或图像)填充图形;描边,就是只在图形的边缘画线。大多数2D上下文操作都会细分为填充和描边两个操作,而操作的结果取决于两个属性:fillStyle和strokeStyle。

    这两个属性的值可以是字符串、渐变对象或模式对象,而且他们的默认值都是“#000000”。如果为他们指定表示颜色的字符串值,可以使用css中指定颜色值的任何格式,包括颜色名、十六进制码、rgb、rgba、hsl、hsla。举例:

        var drawing=document.getElementById("drawing");
        //确定浏览器支持canvas元素
        if(drawing.getContext){
            var context=drawing.getContext("2d");//获取2D上下文
            context.strokeStyle = "red";
            context.fillStyle = "#0000ff";
        }

    以上代码将strokeStyle设置为red(css中的颜色名),将fillStyle设置为#0000ff(蓝色)。然后,所有涉及填充和描边的的操作都将使用这两个样式,直至重新设置这两个值。如前所述,这两个属性的值也可以是渐变对象或模式对象。代码到这,在页面还看不出来任何效果,因为我们还没有正儿八经的开始绘制图形;只是设置了描边和填充属性;

     绘制矩形:

    与矩形有关的方法包括fillRect()、strokeRect()、clearRect()。这三个方法都接收4个参数:矩形的x坐标、矩形的y坐标、矩形的宽度和矩形的高度。单位都是像素。

    首先,fillRect()方法在画布上绘制的矩形会填充指定的颜色。填充的颜色由fillStyle属性指定。

    strokeRect()方法在画布上绘制的矩形会使用指定的颜色描边。描边颜色通过strokeStyle属性指定;

    描边线条的宽度由lineWidth属性控制,该属性的值可以是任意整数。另外,通过lineCap属性可以控制线条末端的形状是平头、圆头还是方头(butt、round、square),通过lineJoin属性可以控制线条相交的方式是圆交、斜交、还是斜接(round、bevel、miter)。

    <body>
        <canvas id="drawing" width="300" height="300" style="border:1px solid red;">不支持canvas时,会显示canvas标签中的内容</canvas>
    
    <script>
        var drawing=document.getElementById("drawing");
        //确定浏览器支持canvas元素
        if(drawing.getContext){
            var context=drawing.getContext("2d");//获取2D上下文
            //绘制红色矩形
            context.fillStyle = "#ff0000";//填充颜色
            context.fillRect(10,10,100,100);//填充命令
            //描边
            context.lineWidth="5";//线条宽度5像素
            context.lineCap="square";//butt:平头、round:圆头、square:方头
            context.lineJoin="round";//round:圆角、bevel:斜交、miter:斜接
            context.strokeStyle="yellow";//描边颜色
            context.strokeRect(10,10,100,100);//描边命令
            //绘制半透明的蓝色矩形,后加的矩形会覆盖在前面的矩形上面
            context.lineWidth="5";//线条宽度10像素
            context.fillStyle = "rgba(0,0,255,0.5)";//填充颜色
            context.fillRect(30,30,100,100);//矩形位置在(30,30)、矩形宽高为100x100
            context.strokeStyle="blue";//描边颜色
            context.strokeRect(30,30,100,100);
            //然后将画布绘制的图像导出,放在一个img标签中
            var imgURI=drawing.toDataURL("image/png");
            var image=document.createElement("img");
            image.src=imgURI;
            image.style.cssText="border:1px solid red;display:block;"
            document.body.appendChild(image);
        }
        
    </script>
    </body>

    最后clearRect()方法用于清除画布上的矩形区域。本质上,这个方法可以把绘制上下文的某一矩形区域变透明。通过绘制形状再清除指定区域,就可以清除有意思的效果,例如把某个形状切掉一块;例如:

        var drawing=document.getElementById("drawing");
        //确定浏览器支持canvas元素
        if(drawing.getContext){
            var context=drawing.getContext("2d");//获取2D上下文
            //绘制红色矩形
            context.fillStyle = "#ff0000";//填充颜色
            context.fillRect(10,10,50,50);//填充命令
            //绘制半透明的蓝色矩形
            context.fillStyle = "rgba(0,0,255,0.5)";//填充颜色
            context.fillRect(30,30,50,50);
            //在两个矩形重叠的地方清除一个小矩形
            context.clearRect(40,40,10,10);
        }

     绘制路径:

    2D绘制上下文支持很多在画布上绘制路径的方法。通过路径可以创造出复杂的形状和线条。要绘制路径,首先必须调用beginPath()方法,表示要开始绘制新路径,然后再通过调用下列方法来实际的绘制路径:

    arc(x,y,radius,startAngle,endAngle,counterclockwise):以(x,y)为圆心绘制一条弧线,弧线半径为radius,起始和结束角度(用弧度表示)为别表示为startAngle和endAngle。最后一个参数表示startAngle和endAngle是按逆时针方向计算,值为false表示按顺时针方向计算。

    arcTo(x1,y1,x2,y2,radius):从上一点开始绘制一条弧线,到(x2,y2)为止,并且以给定的半径radius穿过(x1,y1)。

    bezierCurveTo(c1x,c1y,c2x,c2y,x,y):从上一点开始绘制一条曲线,到(x,y)为止,并且以(c1x,c1y)和(c2x,c2y)为控制点。

    lineTo(x,y):从上一点开始绘制一条直线,到(x,y)为止。

    moveTo(x,y):将绘图游标移动到(x,y),不划线。

    quadraticCurveTo(cx,cy,x,y):从上一点开始绘制一条二次曲线,到(x,y)为止,并且以(cx,cy)为控制点。

    rect(x,y,width,height):从点(x,y)开始绘制一个矩形,宽度和高度分别为width和height指定。这个方法绘制的是矩形路径,而不是strokeRect()和fillRect()所绘制的独立的形状。

    创建了路径之后,接下来有几种可能的选择。如果想绘制一条连接到路径起点的线条,可以调用closePath()。如果路径已经完成,你想用fillStyle填充它,可以调用fill()方法。另外,还可以调用stroke()方法对路径描边描边使用的是strokeStyle最后还可以调用clip(),这个方法可以在路径上创建一个剪切区域

    现在绘制一个不带数字的时钟表盘:

        var drawing=document.getElementById("drawing");
        //确定浏览器支持canvas元素
        if(drawing.getContext){
            var context=drawing.getContext("2d");//获取2D上下文
            //开始绘制路径
            context.beginPath();
            //绘制外圈
            context.arc(100,100,99,0,2*Math.PI,false);//以(100,100)为圆心,以99为半径,起始角度为0,结束角度为2π,顺时针画弧
            //绘制内圆
            context.moveTo(194,100);//移动一下起点
            context.arc(100,100,94,0,2*Math.PI,false);
            //绘制分针
            context.moveTo(100,100);//游标移动到(100,100)
            context.lineTo(100,15);//从上一点画直线到(100,15)
            //绘制时针
            context.moveTo(100,100);//游标移动到(100,100)
            context.lineTo(35,100);//从上一点画直线到(35,100)
            //描边路径
            context.stroke();//绘制命令
    
        }

    在2D绘图上下文中,路径是一种主要的绘图方式,因为路径能为要绘制的图形提供更多控制。由于路径的使用很频繁,所以就有了一个名为isPointInPath()的方法。这个方法接收x和y坐标作为参数,用于在路径关闭之前确定画布上的某一点是否位于路径上,例如:

        if(context.isPointInPath(100,100)){
            console.log("这个点在路径上");
        }else{
            console.log("这个点不在路径上");
        }

    其它方法的一些实验:

        var drawing=document.getElementById("drawing");
        //确定浏览器支持canvas元素
        if(drawing.getContext){
            var context=drawing.getContext("2d");//获取2D上下文
            //开始绘制路径
            context.beginPath();
            context.moveTo(100,100);
            context.arcTo(150,50,200,100,50);//从上一点开始到(200,100)绘制一条弧线且经过(150,50)半径为50px
            context.moveTo(110,100);
            context.bezierCurveTo(130,50,160,50,210,100);//从上一点开始画一条贝塞尔曲线,终点为(210,100),以(130,50)和(160,50)为控制点
            context.moveTo(100,110);
            context.quadraticCurveTo(150,50,200,110);//从上一点开始绘制一条二次曲线,到(200,110)为止,并且以(150,50)为控制点
            // context.moveTo(100,200);
            context.rect(100,200,100,100);//从(100,200)开始绘制一个宽高都为100的矩形
            //描边路径
            context.stroke();//绘制命令
    
        }

     绘制文本:

    绘制文本主要有两个方法:filltext()和strokeText()。这两个方法都可以接受4个参数:要绘制文本的字符窜,x坐标、y坐标、、可选的最大像素宽度。而且这两个方法都以下列三个属性为基础:

    font:表示文本样式、大小及字体,用css中指定字体的格式来指定,例如“10px Arial”。

    textAlign:表示文本对齐方式。可能值有“start”、“end”、“left”、“right”、“center”。建议使用“start”和“end”,不要使用“left”和“right”,因为前两者的意思更稳妥,能同时适合从左到右,从右到左的显示。

    textBaseline:表示文本的基线。可能的值有:“top”、“hanging”、“middle”、“alphabetic”、“ideographic”、“bottom”。

    这几个属性都有默认值,没必要每次使用都重新设置一遍。fillText()方法使用fillStyle属性绘制文本,而strokeText()方法使用strokeStyle属性为文本描边。相对来说还是使用fillText()的时候更多,因为该方法模仿了再网页中正常的显示文本。

        var drawing=document.getElementById("drawing");
        //确定浏览器支持canvas元素
        if(drawing.getContext){
            var context=drawing.getContext("2d");//获取2D上下文
            //绘制文本
            context.fillStyle="red";//可选
            context.font="bold 14px Arial";
            context.textAlign="center";
            context.textBaseline="middle";
            context.fillText("哈哈哈",100,20);
        }

    因为这里把textAlign设置为center,把textBaseline设置为“middle”,所以坐标(100,20)表示的是文本水平和垂直中点的坐标。如果将textAlign设置为start,则x坐标表示的是文本左端的位置(从左到右阅读语言);设置为“end”,则x坐标表示的是文本右端的位置(从左到右阅读的语言)。

        var drawing=document.getElementById("drawing");
        //确定浏览器支持canvas元素
        if(drawing.getContext){
            var context=drawing.getContext("2d");//获取2D上下文
            //绘制文本
            context.fillStyle="red";//可选
            context.font="bold 14px Arial";
            context.textAlign="center";
            context.textBaseline="middle";
            context.fillText("哈哈哈",100,20);
            //起点对齐
            context.textAlign="start";
            context.fillText("哈哈哈",100,40);
            //终点对齐
            context.textAlign="end";
            context.fillText("哈哈哈",100,60);
        }

    2D上下文提供了辅助确定文本大小的方法measureText()。这个方法接收一个参数,即要绘制的文本;返回一个TextMetrics对象。返回的对象目前只有一个width属性。

    measureText()方法利用font、textAlign和textBaseline的当前值计算指定文本的大小。

    比如想在一个140像素宽的矩形区域中绘制文本Hello world!,下面的代码从100像素的字体大小开始递减,最终找到合适的字体大小。

        var drawing=document.getElementById("drawing");
        //确定浏览器支持canvas元素
        if(drawing.getContext){
            var context=drawing.getContext("2d");//获取2D上下文
            //绘制文本
            var fontSize=100;
            context.font=fontSize+"px Arial";
            while(context.measureText("Hello world!").width>140){
                fontSize--;
                context.font=fontSize+"px Arial";
            }
            context.fillText("Hellow world!",10,20);
            context.fillText("字体大小为"+fontSize+"px",10,60);
        }

     变换通过上下文的变换,可以把处理后的图像绘制到画布上。2D绘制上下文支持各种基本的绘制变换。为绘制上下文应用变换,会导致使用不同的变换矩阵应用处理,从而产生不同的结果。

    可以通过如下方法来修改变换矩阵:

    rotate(angle):围绕原点旋转图像angle弧度。

    scale(scalex,scaley):缩放图像,在x方向乘以scalex,在y方向乘以scaley,scalex和scaley的默认值都为1.0

    translate(x,y):将坐标原点移动到(x,y)。执行这个变换后,坐标(0,0)会变成之前由(x,y)表示的点;

    transform(m1—1,m1—2,m2—1,m2—2,dx,dy):直接修改变换矩阵,方式是乘以如下矩阵:

    m1—1 m1—2 dx
    m2—1 m2—2 dy
    0 0 1

    setTransform(m1—1,m1—2,m2—1,m2—2,dx,dy):将变换矩阵重置为默认状态,然后再调用transform()。

    变换有可能简单,也有可能复杂,拿前面的绘制表针来说,如果把原点变换到表盘的中心,然后再绘制表针就容易多了。

        var drawing=document.getElementById("drawing");
        //确定浏览器支持canvas元素
        if(drawing.getContext){
            var context=drawing.getContext("2d");//获取2D上下文
            //开始路径
            context.beginPath();
            // 绘制外圆
            context.arc(100,100,99,0,2*Math.PI,false);//绘制一个以(100,100)为圆心,99位半径,开始角度为0,结束角度为2π,顺时针绘制的弧线(圆)
            // 绘制内圆
            context.moveTo(194,100);
            context.arc(100,100,94,0,2*Math.PI,false);
            // 变换原点
            context.translate(100,100);//将原点移到(100,100)此时该坐标的坐标变为(0,0)
            // 绘制分针
            context.moveTo(0,0);
            context.lineTo(0,-85);
            // 绘制时针
            context.moveTo(0,0);
            context.lineTo(-65,0);
            // 描边
            context.stroke();
        }

    还可以更近一步,用rotate()方法旋转时钟的表针;

        var drawing=document.getElementById("drawing");
        //确定浏览器支持canvas元素
        if(drawing.getContext){
            var context=drawing.getContext("2d");//获取2D上下文
            //开始路径
            context.beginPath();
            // 绘制外圆
            context.arc(100,100,99,0,2*Math.PI,false);//绘制一个以(100,100)为圆心,99位半径,开始角度为0,结束角度为2π,顺时针绘制的弧线(圆)
            // 绘制内圆
            context.moveTo(194,100);
            context.arc(100,100,94,0,2*Math.PI,false);
            // 变换原点
            context.translate(100,100);//将原点移到(100,100)此时该坐标的坐标变为(0,0)
            // 旋转表针
            context.rotate(1);//3.14表示1π,也就是180度(按原点旋转,顺时针)
            // 绘制分针
            context.moveTo(0,0);
            context.lineTo(0,-85);
            // 绘制时针
            context.moveTo(0,0);
            context.lineTo(-65,0);
            // 描边
            context.stroke();
        }

    无论是刚才执行的变换,还是fillStyle,strokeStyle等属性,都会在当前上下文中一直有效,除非再对上下文进行什么修改,虽然没办法把上下文的一切重置回默认值,但有两个方法可以跟踪上下文的变化状态。如果你知道将来还要返回某组属性与变换的组合,可以调用save()方法。调用这个方法后,当时的所有设置都会进入一个栈结构,得以妥善保管。然后可以对上下文进行其他修改。等想要回到之前保存的设置时,可以调用restore()方法,在保存设置的栈结构中向前返回一级,恢复之前的状态。连续调用save()可以把更多设置保存到栈结构中,之后再连续调用restore()则可以一级一级返回。下面来看一个例子:

        var drawing=document.getElementById("drawing");
        //确定浏览器支持canvas元素
        if(drawing.getContext){
            var context=drawing.getContext("2d");//获取2D上下文
            context.fillStyle="#ff0000";
            context.save();//保存状态
    
            context.fillStyle="#00ff00";
            context.translate(100,100);
            context.save();
    
            context.fillStyle="#0000ff";
            context.fillRect(0,0,100,200);//从(100,100)开始绘制蓝色矩形
    
            context.restore();//状态返回一级
            context.fillRect(10,10,100,200);//从(110,110)开始绘制绿色矩形
    
            context.restore();//再返回一级
            context.fillRect(0,0,100,200);//从点(0,0)开始绘制红色矩形
        }

    save()方法只是保存对绘图上下文的设置和变换,不会保存绘图上下文的内容;

     绘制图像:

    2D绘图上下文内置了对图像的支持,如果你想把衣服图像绘制在画布上,可以使用drawImage()方法。根据期望的最终结果不同,调用这个方法时,可以使用三个不同的参数组合。最简单的调用方式是传入一个HTML<img>元素,以及绘制该图像的起点的x和y坐标。例如:

        var drawing=document.getElementById("drawing");
        //确定浏览器支持canvas元素
        if(drawing.getContext){
            var context=drawing.getContext("2d");//获取2D上下文
            var image=document.images[0];
            window.onload=function(){//加上load方法,是为了等图片加载完再往画布上绘制
                // context.drawImage(image,10,10);
                context.drawImage(image,10,10,200,200);//后面两个参数是指的图像的宽度和高度(px)
            }
        }

    除了上述两种方式,还可以选择把图像中的某个区域绘制到上下文中。drawImage()方法的这种调用方式总共需要传入9个参数:要绘制的参数源图像的x坐标源图像的y坐标原图像的宽度原图像的高度目标图像的x坐标目标图像的y坐标目标图像的宽度目标图像的高度。这样调用drawImage()方法可以获得更多控制。例如:

    源图像代表原来图像的尺寸,从(0,0)开始圈一块儿地方;再将目标图像绘制在画布的某个位置,(规定这块图像的大小);

    也就是第一步就是在原图上指定位置圈一块儿,放在画布的某个位置上,并指定大小

        var drawing=document.getElementById("drawing");
        //确定浏览器支持canvas元素
        if(drawing.getContext){
            var context=drawing.getContext("2d");//获取2D上下文
            var image=document.images[0];
            window.onload=function(){
                //在原图像的(300,0)位置上圈300x300的一块儿图像,绘制在画布上(0,0)的位置,大小为100x100
                context.drawImage(image,300,0,300,300,0,0,100,100);
            }
        }

    谨记,不能用外域的图像。当然除了把img标签绘制在画布上外,也可以将canvas标签绘制在画布上;

     阴影:

    2D上下文会根据以下几个属性的值,自动为形状或路径绘制出阴影。

    shadowColor:用css颜色的格式表示额阴影颜色,默认为黑色。

    shadowOffsetX:形状或路径x轴方向的阴影偏移量,默认0;

    shadowOffsetY:形状或路径y轴方向的阴影偏移量,默认为0;

    shadowBlur:模糊的像素数,默认0,即不模糊;

        var drawing=document.getElementById("drawing");
        //确定浏览器支持canvas元素
        if(drawing.getContext){
            var context=drawing.getContext("2d");//获取2D上下文
            //设置阴影
            context.shadowOffsetX=5;
            context.shadowOffsetY=4;
            context.shadowBlur=4;
            context.shadowColor="rgba(0,0,0,.5)";
            // 绘制红色矩形
            context.fillStyle="#ff0000";
            context.fillRect(10,10,50,50);
            //绘制蓝色矩形
            context.fillStyle="rgba(0,0,255,1)";
            context.fillRect(30,30,50,50);
        }

    chrome(10-)、safari(5-)浏览器较低版本在给带透明像素的图像应用阴影时,隐隐有可能出不来;

    渐变

    渐变由CanvasGradient实例表示,很容易通过2D上下文来创建和修改。要创建一个新的线性渐变,可以调用createLinearGradient()方法。这个方法接收4个参数:起点x坐标,起点y坐标,终点x坐标,终点y坐标。调用这个方法后,它就创建了一个指定大小的渐变,并返回CanvasGradient对象的实例;

      创建了渐变对象后,下一步就是使用addColorStop()方法来指定色标。这个方法接收两个参数:色标位置和css颜色值。色标位置是一个0(开始的颜色)到1(结束颜色)之间的数字。例如:

        var drawing=document.getElementById("drawing");
        //确定浏览器支持canvas元素
        if(drawing.getContext){
            var context=drawing.getContext("2d");//获取2D上下文
            // 创建一个CanvasGradient对象
            var gradient=context.createLinearGradient(30,30,70,70);
            gradient.addColorStop(0,"white");
            gradient.addColorStop(1,"black");
            //此时gradient对象表示的是一个从画布上点(30,30)到点(70,70)的渐变。起点的色标是白色终点的色标是黑色。然后就可以把fillStyle或strokeStyle设置为这个对象,从而用渐变来绘制形状或描边:
            // 绘制红色矩形
            context.fillStyle="#ff0000";
            context.fillRect(10,10,50,50);
            // 绘制渐变色矩形
            context.fillStyle=gradient;
            context.fillRect(30,30,50,50);
        }

    为了让渐变色覆盖整个矩形,而不是仅应用到矩形的一部分,矩形和渐变对象的坐标必须匹配才行,如果没将矩形绘制到指定位置,那可能就会显示部分渐变效果:

        //若将矩形没绘制到与阴影坐标匹配的位置,则可能显示部分渐变效果
            context.strokeStyle="yellow";
            context.lineWidth=1;
            context.strokeRect(20,50,50,50);//描边命令
            context.fillStyle=gradient;
            context.fillRect(20,50,50,50);

    确保渐变与形状对齐非常重要,有会后可以考虑使用函数来确保坐标合适。例如

        var drawing=document.getElementById("drawing");
        //确定浏览器支持canvas元素
        if(drawing.getContext){
            var context=drawing.getContext("2d");//获取2D上下文
            // 用确保渐变与形状对齐的方法创建一个gradient对象
            var gradient=createRectLinearGradient(context,30,30,50,50);
            gradient.addColorStop(0,"white");
            gradient.addColorStop(1,"black");
            // 绘制渐变矩形
            context.fillStyle=gradient;
            context.fillRect(30,30,50,50);//这样,渐变对象坐标和矩形坐标相同就可以保证两者匹配
        }
        // 确保渐变与形状对齐的方法
        function createRectLinearGradient(context,x,y,width,height){
            return context.createLinearGradient(x,y,x+width,y+height);
        }

    以上是线性渐变,下面说一下径向渐变,可以使用createRadialGradient()方法,这个方法接收6个参数,对应两个圆的圆心和半径。前3个参数是起点圆的圆心(x和y)及半径,后3个参数指的是终点圆的圆心(x和y)及半径。可以把径向渐变想象成一个长圆桶,而这6个参数定义的是这个桶的两个圆形开口位置,如果把一个原圆形开口定义的比另一个小一些,那这个圆桶就变成了圆锥体,而通过移动每个圆形开口的位置,就可以达到下个旋转一个圆锥体一样的效果。

      如果想从某个形状的中心点开始创建一个向外扩撒的径向渐变效果,就要将两个圆定义为同心圆。

        var drawing=document.getElementById("drawing");
        //确定浏览器支持canvas元素
        if(drawing.getContext){
            var context=drawing.getContext("2d");//获取2D上下文
            // 创建径向渐变对象
            var gradient=context.createRadialGradient(55,55,10,55,55,30);
            gradient.addColorStop(0,"white");
            gradient.addColorStop(1,"black");
            //绘制红色矩形
            context.fillStyle="red";
            context.fillRect(10,10,50,50);
            // 绘制径向渐变矩形
            context.fillStyle=gradient;
            context.fillRect(30,30,50,50);
        }

     模式

    模式其实就是重复的图像,可以用来填充或描边。要创建一个新模式,可以调用createPattern()方法并传入两个参数:一个HTML<img>元素和一个表示如何重复图像的字符串。其中,第二个参数的值与css的background-repeat属性值相同,包括"repeat"、“repeat-x”、“repeat-y”和“no-repeat”。例:

        var drawing=document.getElementById("drawing");
        //确定浏览器支持canvas元素
        if(drawing.getContext){
            var context=drawing.getContext("2d");//获取2D上下文
            var image=document.images[0];
            window.onload=function(){//等图像加载好再创建模式
                // 创建一个模式
                var pattern=context.createPattern(image,"repeat");
                // 绘制矩形
                context.fillStyle=pattern;
                context.fillRect(10,10,200,200);
            }
        }

    需要注意的是,模式是重原点(0,0)开始的。将填充样式(fillStyle)设置为模式对象,只表示在某个特定的区域内显示重复图像,而不是要从某个位置开始绘制重复图像

     。另外ctreatePattern()方法的第一个参数也可以是一个<video>元素,或者另一个<canvas>元素。

    使用图像数据

    2D上下文的一个明显的长处就是,可以通过getImageData()取得原始图像数据。这个方法接收4个参数:要取得其数据的画面区域的x和y坐标以及该区域的像素宽度和高度。例如,要取得左上角坐标为(10,5)、大小为50x50像素的区域的图像数据,可以使用以下代码;

        var drawing=document.getElementById("drawing");
        //确定浏览器支持canvas元素
        if(drawing.getContext){
            var context=drawing.getContext("2d");//获取2D上下文
            var image=document.images[0];
            var imageData=context.getImageData(10,5,30,30);
            console.log(imageData)
        }

    这里返回的对象是ImageData的实例。每个ImageData对象都 有三个属性:width、height和data。其中data属性是一个数组,保存着图像中每一个像素的数据。在data数组中,每一个像素用4个元素来来保存,分别表示红、绿、蓝和透明度值。因此,第一个像素的数据就保存在数组的第0到第3个元素中。例如通过修改图像数据,可以像下面这样创建一个简单的灰阶过滤器。

        var drawing=document.getElementById("drawing");
        //确定浏览器支持canvas元素
        if(drawing.getContext){
            var context=drawing.getContext("2d"),
                image=document.images[0],
                imageData,data,
                i,len,average,
                red,green,blue,alpha;
            window.onload=function(){//保证图片加载完
                // 绘制原始图像
                context.drawImage(image,0,0);
                console.log(image)
            } 
            // 取得图像数据
            imageData=context.getImageData(0,0,image.width,image.height);
            data=imageData.data;
            console.log(imageData);
            for(i=0,len=data.length;i<len;i+=4){
                red=data[i];
                green=data[i+1];
                blue=[i+2];
                alpha=data[i+3];
                //求得rgb的平均值
                average=Math.floor((red+green+blue)/3);
                //设置颜色值,透明度不变
                data[i]=average;
                data[i+1]=average;
                data[i+2]=average;
            }
            //回写图像数据并显示结果
            imageData.data=data;
            context.putImageData(imageData,0,0);   
            
        }

    合成

    globalAlpha:透明属性,介于0,-1之间,包括0,也包括1

    globalCompositionOperation:表示后绘制的图形怎么与先绘制的图形结合,可能值如下:

    source-over(默认值):后绘制的图形位于先绘制的图形上方;

    source-in:后绘制的图像与先绘制的图形重叠的部分可见,两者其它部分完全透明;

    source-out:后绘制的图形与先绘制的图形不重叠的部分可见,先绘制的图形完全透明;

    source-atop:后绘制的图形与先绘制的图形重叠的部分可见,先绘制的图形不受影响;

    destination-over:后绘制的图形位于先绘制的图形下方,只有之前透明像素下的部分才可见;

    destination-in:后绘制的图形位于先绘制的图形下方,两者不重叠的部分完全透明。

    destination-out:后绘制的图形擦除与先绘制的图形重叠的部分。

    destination-atop:后绘制的图形位于先绘制的图形下方,在两者不重叠的地方,先绘制的图形会变透明。

    lighter:后绘制的图形与先绘制的图形重叠部分的值相加,使该部分变亮。

    copy:后绘制的图形完全替代与之重叠的先绘制图形。

    xor:后绘制的图形与先绘制的图形重叠的部分执行“异或”操作;

        var drawing=document.getElementById("drawing");
        //确定浏览器支持canvas元素
        if(drawing.getContext){
            var context=drawing.getContext("2d");
            //绘制红色矩形
            context.fillStyle="#ff0000";
            context.fillRect(10,10,50,50); 
            // 修改全局透明度
            context.globalAlpha=0.5;
            //绘制蓝色矩形
            context.fillStyle="rgba(0,0,255,1)";
            context.fillRect(30,30,50,50);
            //重置全局透明度
            context.globalAlpha=1;
    
    
            //绘制红色矩形
            context.fillStyle="#ff0000";
            context.fillRect(100,100,50,50); 
            //设置合成操作
            context.globalCompositeOperation="destination-over";
            //绘制蓝色矩形
            context.fillStyle="rgba(0,0,255,1)";
            context.fillRect(120,120,50,50);
        }

    WebGL

    WebGL是针对Canvas的3D上下文。WebGL并不是W3C指定标准,而是Khronos Group制定的。浏览器中使用的WebGL就是基于OpenGL ES2.0制定的。

    全面了解OpenGL,访问www.opengl.org;全面学习WebGL,参考www.learningwebgl.com

    类型化数组

    WebGl涉及的复杂计算需要提前知道数值的精度,而标准的javascript数值无法满足需求。为此,WebGL引入了一个概念,叫类型化数组(type arrays)。类型化数组也是数组,只不过其元素被设置为特定的值;

    类型化数组的核心就是一个名为ArrayBuffer的类型。没个ArrayBuffer对象表示的只是内存中指定的字节数,但不会指定这些字节用于保存什么类型的数据。通过ArrayBuffer所能做的,就是为了将来使用而分配一定数量的字节。例如,下面这行代码会在内存中分配20B。

        var buffer = new ArrayBuffer(20);
        console.log(buffer);//ArrayBuffer { byteLength: 20 }
        //访问其byteLength属性
        console.log(buffer.byteLength);//20

    虽然ArrayBuffer对象本身没有什么可说的,但对WebGL而言,使用它时及其重要的;

    视图

    使用ArrayBuffer(数组缓冲器类型)的一种特别的方式就是用它来创建数组缓冲器视图。其中最常见的视图是DataView,通过它可以选择ArrayBuffer中一小段字节。为此,创建DataView实例的时候传入一个ArrayBuffer、一个可选字节偏移量(从该字节开始选择)和一个可选的要选择的直接数;

        var buffer = new ArrayBuffer(20);
        console.log(buffer);//ArrayBuffer { byteLength: 20 }
        //访问其byteLength属性
        console.log(buffer.byteLength);//20
    
        // 基于整个缓冲器创建一个新视图
        var view=new DataView(buffer);
        console.log(view);
        // 创建一个开始于字节9的新视图
        var view=new DataView(buffer,9);
        console.log(view);
        // 创建一个从字节9开始到字节18的新视图
        var view =new DataView(buffer,9,10);
        console.log(view);

    实例化之后,DataView对象会把字节偏移量以及字节长度信息分别保存在byteOffset和byteLength属性中。

    读取和写入DataView的时候,要根据实际数据的类型,选择相应的getter和setter方法。

    数据类型 getter setter
    有符号8位整数 getInt8(byteOffset) setInt8(byteOffset,value)
    无符号8位整数 getUint8(byteOffset) setUint8(byteOffset,value)
    有符号16位整数 getInt16(byteOffset,littleEndian) setInt16(byteOffset,value,littleEndian)
    无符号16位整数 getUint16(byteOffset,littleEndian) setUint16(byteOffset,value,littleEndian)
    有符号32位整数 getInt32(byteOffset,littleEndian) setInt32(byteOffset,value,littleEndian)
    无符号32位整数 getUint32(byteOffset,littleEndian) setUint32(byteOffset,value,littleEndian)
    32位浮点数 getFloat32(byteOffset,littleEndian) setFloat32(byteOffset,value,littleEndian)
    64位浮点数 getFloat64(byteOffset,littleEndian)

    setFloat64(byteOffset,value,littleEndian)

     有些数据类型可能需要不止1b。比如无符号8位整数要用1B,而32位浮点数则要用4B。

        var buffer = new ArrayBuffer(20),
            view=new DataView(buffer),
            value;
        view.setUint16(0,25);
        view.setUint16(2,50);//不能从字节1开始,因为16位整数要用2B
        value=view.getUint16(0);
        console.log(value);//25

    用于读写16位或更大数值的方法都有一个可选参数littleEndian。这是一个布尔值;表示读写数值时是否采用小端字节序(将数据的最低有效位保存在低内存地址中),而不是大端字节序(将数据最低有效位保存在高内存地址中)。不传,就是默认大端字节序;

        var buffer = new ArrayBuffer(20),
            view=new DataView(buffer),
            value;
        view.setUint16(0,25);
        view.setUint16(2,50);//不能从字节1开始,因为16位整数要用2B
        value=view.getUint8(0);
        console.log(value);//0

    类型化视图:

    类型化视图一般也称为类型化数组,因为他们除了元素必须是某种特定的数据类型外,与常规的数组无异;

    Init8Array 表示8位二补整数
    Uint8Array 表示8位无符号整数
    Init16Array 表示16位二补整数
    Uint16Array 表示16位无符号整数
    Init32Array 表示32位二补整数
    Uint32Array 表示32位无符号整数
    Float32Array 表示32位IEEE浮点数
    Float64Array 表示64位IEEE浮点数
        var buffer = new ArrayBuffer(20);
        //创建一个新数组,使用整个缓冲器
        var int8s=new Int8Array(buffer);
        // 只使用字节9开始的缓冲器
        var int16s=new Int16Array(buffer,9);
        //只使用从字节9到字节18的缓冲器
        var uint16s=new Uint16Array(buffer,9,10);

    能够指定缓冲器中可用的字节段,意味着能在同一个缓冲器中保存不同类型的数值;

        var buffer = new ArrayBuffer(20);
        // 使用缓冲器的一部分保存8位整数,另一部分保存16位整数。
        var int8s=new Int8Array(buffer,0,10);
        console.log(int8s);
        var unit16s=new Uint16Array(buffer,10,10);
        console.log(unit16s);
        // 每个视图的构造函数都有一个名为BYTES_PER_ELEMENT的属性,表示类型化数组的每个元素需要多少字节。
        //需要10个元素空间
        var ints=new Int8Array(buffer,0,10*Int8Array.BYTES_PER_ELEMENT);
        // 需要5个元素空间
        var unit16s=new Uint16Array(buffer,int8s.byteOffset+ints.byteLength,5*Uint16Array.BYTES_PER_ELEMENT);
        console.log(ints);
        console.log(unit16s);

    另外,创建类型化视图还可以不用首先创建ArrayBuffer对象。只要传入希望数组保存的元素数,相应的构造函数就可以自动创建一个包含足够字节数的ArrayBuffer对象,例如:

        //创建一个数组保存10个8位整数(10字节)
        var int8s=new Int8Array(10);
        //创建一个数组保存10个16位整数(20字节)
        var unit16s=new Uint16Array(10);
        // 也可以把常规数组转化为类型化数组
        // 创建一个数组保存5个8位整数(10字节)
        var ints=new Int8Array([10,20,30,40,50]);
        //类型化视图还有一个方法subarray(),可以基于底层数组缓冲器的子集创建一个新视图
        var uint16s=new Uint16Array(10),//创建一个保存10个16位整数的类型化视图
            sub=uint16s.subarray(2,5);//偏移量为2,长度为5

     不行,不行,WebGL看的有些头蒙,再加上目前支持的浏览器只有火狐和谷歌,而且默认还是禁用的,我找了个很好的理由,暂时停止学习WebGL,有时间再收拾它。。。

  • 相关阅读:
    OpenCV_Python —— (6)图像色彩空间
    OpenCV_Python —— (5)图像模糊/平滑/滤波
    Java 14 祭出增强版 switch,真香!!
    推荐 9 个 爱不释手的 JSON 工具!
    从 0 开始手写一个 Mybatis 框架,三步搞定!
    Java常用的几个Json库,性能强势对比!
    Oracle JDK 和 OpenJDK 有什么区别?
    极客时间-左耳听风-程序员攻略-UI/UX设计
    OpenCV导向滤波(引导滤波)实现(Guided Filter)代码,以及使用颜色先验算法去雾
    python面向对象小练习
  • 原文地址:https://www.cnblogs.com/fqh123/p/10486522.html
一二三 - 开发者的网上家园