JavaScript的Canvas绘图

目录

一、Canvas简介

二、Canvas基本用法

三、填充和描边

四、绘制矩形

五、绘制路径

5.1、绘制线段

5.2、绘制三角形

5.3、绘制圆弧

5.4、绘制贝塞尔曲线

5.5、线条样式

六、绘制文本

七、绘制图像

八、模式

九、使用图像数据

十、阴影


一、Canvas简介

    <canvas>元素是HTML5新增的,一个可以使用脚本(通常为JavaScript)在其中绘制图像的HTML元素。它可以用来制作照片集或者制作简单的动画,甚至可以进行实时视频处理和渲染。

    <canvas>由几组API构成,除了具备基本绘图能力的2D上下文<canvas>还具备一个名为WebGL3D上下文

二、Canvas基本用法

    使用<canvas>元素之前,必须先设置其widthheight属性,指定可以绘图的区域大小。

    出现在开始和结束标签中的内容是后备信息,如果浏览器不支持<canvas>元素,就会显示这些信息。

<canvas id="drawing" width="200" height="200">A drawing of something.</canvas>

    要在这块画布上绘图,需要使用getContext()方法传入参数"2d"取得2D上下文对象

        var drawing = document.getElementById("drawing");

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){
            var context = drawing.getContext("2d"); //取得2D上下文对象
            //更多代码
        }

    使用2D绘图上下文提供的方法,可以绘制简单的2D图形,比如矩形、弧线和路径。

    2D上下文的坐标开始于<canvas>元素的左上角,原点坐标是(0,0)。

    所有坐标值都基于这个原点计算,x值越大表示越靠右,y值越大表示越靠下。

三、填充和描边

    2D上下文的两种基本绘图操作是填充和描边。

    填充就是用指定的样式(颜色、渐变或图像)填充图形,fillStyle属性设置填充样式。

    描边就是只在图形的边缘画线,strokeStyle属性设置描边样式。

    这两个属性的值可以是字符串、渐变对象或模式对象,例如:

        var drawing = document.getElementById("drawing");

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){
            
            var context = drawing.getContext("2d"); //取得2D上下文对象
            context.strokeStyle = "red";
            context.fillStyle = "#0000FF";
        }

四、绘制矩形

    与绘制矩形有关的方法包括:

  • fillRect()
  • strokeRect()
  • clearRect()

    这三个方法都能接收4个参数:矩形的x坐标、矩形的y坐标、矩形的宽度和矩形高度。参数单位都是像素。

    示例1

        var drawing = document.getElementById("drawing");

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){

            var context = drawing.getContext("2d"); //取得2D上下文对象

            //绘制红色矩形
            context.fillStyle = "#F00";
            context.fillRect(10, 10, 50, 50);

            //绘制半透明的蓝色矩形
            context.fillStyle = "rgba(0, 0, 255, 0.5)";
            context.fillRect(30, 30, 50, 50);
        }

    示例2

        var drawing = document.getElementById("drawing");

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){

            var context = drawing.getContext("2d"); //取得2D上下文对象

            //绘制红色描边矩形
            context.strokeStyle = "#F00";
            context.strokeRect(10, 10, 50, 50);

            //绘制半透明的蓝色描边矩形
            context.strokeStyle = "rgba(0, 0, 255, 0.5)";
            context.strokeRect(30, 30, 50, 50);
        }

    示例3

        var drawing = document.getElementById("drawing");

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){

            var context = drawing.getContext("2d"); //取得2D上下文对象

            //绘制红色矩形
            context.fillStyle = "#F00";
            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);
        }

五、绘制路径

    绘制路径的方法:

  • beginPath()    ——    表示要开始绘制新路径。
  • moveTo(x, y)    ——    将绘图游标移动到(x, y),不画线。
  • lineTo(x, y)    ——    从上一点开始绘制一条直线,到(x, y)为止。
  • rect(x, y, width, height)    ——    从点(x,y)开始绘制一个矩形,宽度和高度分别由width和height指定。这个方法绘制的是矩形路径,而不是strokeRect()和fillRect()所绘制的独立的形状。
  • arc(x, y, radius, startAngle, endAngle, counterclockwise)    ——   以(x,y)为圆心绘制一条弧线,弧线半径为radius,起始和结束角度(用弧度表示)分别为startAngle和endAngle。最后一个参数表示startAngle和endAngle是否按逆时针方向计算,值为true表示按逆时针方向计算。
  • quadraticCurveTo(cx, cy, x, y)    ——    绘制二次贝塞尔曲线
  • bezierCurveTo(c1x, c1y, c2x, c2y, x, y)    ——    绘制三次贝塞尔曲线
  • closePath()    ——    绘制一条连接到路径起点的线条。 
  • fill()    ——    使用fillStyle填充已经完成的路径
  • stroke()    ——    使用strokeStyle为已经完成的路径描边

5.1、绘制线段

        var drawing = document.getElementById("drawing");

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){

            var context = drawing.getContext("2d"); //取得2D上下文对象

            context.beginPath();        //开始绘制新路径
            context.moveTo(50, 50);     //把画笔移动到指定的坐标
            context.lineTo(200, 50);    //绘制一条从当前位置到指定坐标(200, 50)的直线
            context.stroke();           //绘制
        }

5.2、绘制三角形

    绘制三角形边框:

        var drawing = document.getElementById("drawing");

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){

            var context = drawing.getContext("2d"); //取得2D上下文对象

            context.beginPath();        //开始绘制新路径
            context.moveTo(50, 50);     //把画笔移动到指定的坐标
            context.lineTo(200, 50);    //绘制一条从坐标(50, 50)到坐标(200, 50)的直线
            context.lineTo(200, 200);   //绘制一条从坐标(200, 50)到坐标(200, 200)的直线
            context.closePath();        //闭合路径,绘制一条从路径起点到路径终点的直线
            context.stroke()            //为路径描边
        }

    填充三角形:

        var drawing = document.getElementById("drawing");

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){

            var context = drawing.getContext("2d"); //取得2D上下文对象

            context.beginPath();        //开始绘制新路径
            context.moveTo(50, 50);     //把画笔移动到指定的坐标
            context.lineTo(200, 50);    //绘制一条从坐标(50, 50)到坐标(200, 50)的直线
            context.lineTo(200, 200);   //绘制一条从坐标(200, 50)到坐标(200, 200)的直线
            context.closePath();        //闭合路径,绘制一条从路径起点到路径终点的直线
            context.fill()              //填充三角形
        }

5.3、绘制圆弧

        var drawing = document.getElementById("drawing");

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){

            var context = drawing.getContext("2d"); //取得2D上下文对象

            context.beginPath();        //开始绘制新路径
            context.arc(100, 100, 99, 0, Math.PI, false);   
            context.stroke();
        }

5.4、绘制贝塞尔曲线

    二次贝塞尔曲线:

        var drawing = document.getElementById("drawing");

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){

            var context = drawing.getContext("2d"); //取得2D上下文对象

            context.beginPath();
            context.moveTo(10, 200);    //起始点
            var x1 = 40, y1 = 100;      //控制点
            var x2 = 200, y2 = 200;     //结束点
            //绘制贝塞尔曲线
            context.quadraticCurveTo(x1, y1, x2, y2);
            context.stroke();

            context.beginPath();
            context.rect(10, 200, 10, 10);
            context.rect(x1, y1, 10, 10);
            context.rect(x2, y2, 10, 10);
            context.fill();

        }

    三次贝塞尔曲线:

        var drawing = document.getElementById("drawing");

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){

            var context = drawing.getContext("2d"); //取得2D上下文对象

            context.beginPath();
            context.moveTo(40, 200);    //起始点
            var x1 = 20, y1 = 100;      //控制点1
            var x2 = 100, y2 = 120;     //控制点2
            var x3 = 200, y3 = 200;      //结束点
            //绘制三次贝塞尔曲线
            context.bezierCurveTo(x1, y1, x2, y2, x3, y3);
            context.stroke();

            context.beginPath();
            context.rect(40, 200, 10, 10);
            context.rect(x1, y1, 10, 10);
            context.rect(x2, y2, 10, 10);
            context.rect(x3, y3, 10, 10);
            context.fill();
        }

5.5、线条样式

    lineWidth属性用于设置线宽

        var drawing = document.getElementById("drawing");

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){

            var context = drawing.getContext("2d"); //取得2D上下文对象

            context.beginPath();
            context.moveTo(10, 10);
            context.lineTo(100, 10);
            context.lineWidth = 10;
            context.stroke();

            context.beginPath();
            context.moveTo(110, 10);
            context.lineTo(160, 10);
            context.lineWidth = 20;
            context.stroke();
        }

    lineCap属性用于设置线条末端样式,可以设置为三个值:

  • butt    ——    线段末端为方形
  • round    ——    线段末端以圆形结束
  • square    ——    线段末端以方形结束,但是增加了一个宽度和线段相同,高度是线段厚度一半的矩形区域。
        var drawing = document.getElementById("drawing");

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){

            var context = drawing.getContext("2d"); //取得2D上下文对象
            
            var lineCaps = ["butt", "round", "square"]
            for(var i = 0; i < 3; i++){
                context.beginPath();
                context.moveTo(20 + 30 * i, 30);
                context.lineTo(20 + 30 * i, 100);
                context.lineWidth = 20;
                context.lineCap = lineCaps[i];
                context.stroke();
            }

            context.beginPath();
            context.moveTo(0, 30);
            context.lineTo(300, 30);

            context.moveTo(0, 100);
            context.lineTo(300, 100);

            context.strokeStyle = "red";
            context.lineWidth = 1;
            context.stroke();
        }

    lineJoin属性用于设置线条与线条间接合处的样式,可以设置为三个值:

  • round    ——    通过填充一个额外的,圆心在相连部分末端的扇形,绘制拐角的形状。圆角的半径是线段的宽度。
  • bevel    ——    在相连部分的末端填充一个额外的以三角形为底的区域,每个部分都有各自独立的矩形拐角。
  • miter(默认)    ——    通过延沈相连部分的外边缘,使其相交于一点,形成一个额外的菱形区域。
        var drawing = document.getElementById("drawing");

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){

            var context = drawing.getContext("2d"); //取得2D上下文对象
            
            var lineJoin = ['round', 'bevel', 'miter'];
            context.lineWidth = 20;

            for (var i = 0; i < lineJoin.length; i++){
                context.lineJoin = lineJoin[i];
                context.beginPath();
                context.moveTo(50, 50 + i * 50);
                context.lineTo(100, 100 + i * 50);
                context.lineTo(150, 50 + i * 50);
                context.lineTo(200, 100 + i * 50);
                context.lineTo(250, 50 + i * 50);
                context.stroke();
            }
        }

六、透明度

    globalAlpha属性用于指定所有绘制的透明度,默认值为0。

    示例:

        var drawing = document.getElementById("drawing");

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){

            var context = drawing.getContext("2d"); //取得2D上下文对象

            //绘制红色矩形
            context.fillStyle = "#F00";
            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 = 0;
  
        }

七、变换

7.1、translate()

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

    示例

        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);

            //绘制内圆
            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();
  
        }

7.2、rotate(angle)

    rotate(angle)方法能够旋转坐标轴。

    示例

        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);

            //绘制内圆
            context.moveTo(194, 100);
            context.arc(100, 100, 94, 0, 2 * Math.PI, false);

            //变换原点
            context.translate(100, 100);

            //旋转表针
            context.rotate(Math.PI / 6);

            //绘制分针
            context.moveTo(0, 0);
            context.lineTo(0, -85);

            //绘制时针
            context.moveTo(0, 0);
            context.lineTo(-65, 0);

            //描边路径
            context.stroke();
  
        }

7.3、scale(scaleX, scaleY)  

    scale(scaleX, scaleY)方法用于缩放图像,在x方向乘以scaleX,在y方向乘以scaleY

    scaleXscaleY的默认值都是1.0

    示例

        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);

            //绘制内圆
            context.moveTo(194, 100);
            context.arc(100, 100, 94, 0, 2 * Math.PI, false);

            //变换原点
            context.translate(100, 100);

            //旋转表针
            context.rotate(Math.PI / 6);

            //绘制分针
            context.moveTo(0, 0);
            context.lineTo(0, -85);

            //绘制时针
            context.moveTo(0, 0);
            context.lineTo(-65, 0);

            //放大路径
            context.scale(5, 5);

            //描边路径
            context.stroke();
  
        }

7.4、transform(a, b, c, d, e, f)

    直接修改变换矩阵,方式是乘以如下矩阵:

7.5、setTransform(a, b, c, d, e, f)

    将变换矩阵重置为默认状态,然后再调用transform()

八、save()和restore()

    调用save()方法,当时所有的设置都会进入一个栈结构,得以妥善保管。

    然后可以对上下文进行其他修改,等想要回到之前保存的设置时,可以调用restore()方法,在保存设置的栈结构中向前返回一级,恢复之前的状态。

    连续调用save()可以把更多设置保存到栈结构中,之后再连续调用restore()则可以一级一级返回。

    示例:

        var drawing = document.getElementById("drawing");

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){

            var context = drawing.getContext("2d"); //取得2D上下文对象

            context.fillStyle = "#F00";
            context.save();

            context.fillStyle = "#0F0";
            context.translate(100, 100);
            context.save();

            context.fillStyle = "#00F";
            //从点(100, 100)开始绘制蓝色矩形
            context.fillRect(0, 0, 100, 200);   
            
            context.restore();
            //从点(110, 110)开始绘制绿色矩形
            context.fillRect(10, 10, 100, 200);

            context.restore();
            //从点(0, 0)开始绘制红色矩形
            context.fillRect(0, 0, 100, 200);
        }

九、绘制文本

    绘制文本主要有两个方法:

  • fillText()
  • strokeText()

    这两个方法都可以接收4个参数:要绘制的文本字符串、x坐标、y坐标和可选的最大像素宽度。

    这两个方法都以下列3个属性为基础:

  • font    ——    表示文本样式、大小及字体,用CSS中指定字体的格式来指定,例如"10px Arial"
  • textAlign    ——    表示文本对齐方式。可能的值有"start"、"end"、"left"、"right"和"center"
  • textBaseline    ——    表示文本的基线,可能的值有"top"、"hanging"、"middle"、"alphabetic"、"ideographic"、"bottom"

    示例

        var drawing = document.getElementById("drawing");

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){

            var context = drawing.getContext("2d"); //取得2D上下文对象
            
            //开始路径
            context.beginPath();

            //绘制分针
            context.moveTo(100, 100);
            context.lineTo(100, 15);

            //描边路径
            context.stroke();

            context.font = "bold 14px Arial";
            context.textAlign = "center";   
            context.textBaseline = "middle";
            context.fillText("12", 100, 20);

            //起点对齐
            context.textAlign = "start";
            context.fillText("12", 100, 40);

            //终点对齐
            context.textAlign = "end";
            context.strokeText("12", 100, 60);

    fillText()strokeText()方法都可以接收第四个参数,也就是文本的最大像素宽度。提供这个参数后,调用fillText()strokeText()时如果传入的字符串大于最大宽度,则绘制的文本字符的高度正确,但宽度会收缩以适应最大宽度。

十、绘制图像

    2D绘图上下文内置了对图像的支持,可以使用drawImage()方法。

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

    ①绘制图像简单示例

        var drawing = document.getElementById("drawing");

        var img = new Image();      //创建Image对象
        img.src = 'https://avatar.csdn.net/6/4/D/1_qq_35732147.jpg?1539065138'; //设置图片源

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){

            var context = drawing.getContext("2d"); //取得2D上下文对象

            img.addEventListener("load", function(){
                context.drawImage(img, 0, 0);       //图像加载完之后才会执行这条语句
            },false);
            
        }

    使用DOM0级的Image对象可以在客户端预先加载图像,可以像使用<img>元素一样使用Image对象,但无法将其添加到DOM树中。

    考虑到图片是从网络加载,所以应该保证图像加载完之后再调用drawImage()方法绘制图像。

    ②缩放图片简单示例:

        var drawing = document.getElementById("drawing");

        var img = new Image();      //创建Image对象
        img.src = 'https://avatar.csdn.net/6/4/D/1_qq_35732147.jpg?1539065138'; //设置图片源

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){

            var context = drawing.getContext("2d"); //取得2D上下文对象

            img.addEventListener("load", function(){
                context.drawImage(img, 0, 0, 400, 400);       //图像加载完之后才会执行这条语句
            },false);
            
        }

    ③切片

    切片示例:

        var drawing = document.getElementById("drawing");

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){

            var context = drawing.getContext("2d"); //取得2D上下文对象

            var img = new Image();
            img.src = "https://avatar.csdn.net/6/4/D/1_qq_35732147.jpg?1539071828";

            img.addEventListener("load", function(){
                context.drawImage(img, 50, 50, 200, 200, 0, 0, 200, 200);
            }, false);
  
        }

十一、模式

    模式其实就是重复的图像,可以用来填充或描边图形。

    要创建一个新模式,可以调用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 img = new Image();
            img.src = "https://avatar.csdn.net/E/9/8/3_weixin_42058532.jpg";

            img.addEventListener("load", function(){
                var pattern = context.createPattern(img, "repeat");

                //绘制矩形
                context.fillStyle = pattern;
                context.fillRect(10, 10, 200, 200);
            }, false);
  
        }

十二、使用图像数据

    canvas操作图像数据使用的几个方法:

    ①getImageData()    ——    用于取得画布上指定矩形的像素数据。

    具体是这个方法会返回ImageData对象,ImageData对象中包含图像数据。

     getImageData()方法接收4个参数:

  • 要取得画面区域左上角的x坐标
  • 要取得画面区域左上角的y坐标
  • 要取得画面区域的像素宽度
  • 要取得画面区域的像素高度

    例如要取得左上角坐标为(10,5)、大小为50*50像素的区域的图像数据:

        var imageData = context.getImageData(10, 5, 50, 50);

    返回的对象是ImageData的实例,每个ImageData对象都有三个属性:

  • width    ——    图像的宽度
  • height    ——    图像的高度
  • data    ——    保存图像中每一个像素的数据的数组,每一个像素用4个元素来保存,分别表示红、绿、蓝和透明度值。

    ②putImageData()    ——    把图像数据(从指定的ImageData对象)放回画布上。

    这个方法接收以下参数:

    

    下面的代码通过getImageData()取得画布上指定矩形的像素数据,然后通过putImageData()将图像数据放回画布:

var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.fillStyle="green";
ctx.fillRect(10,10,50,50);

var imgData=ctx.getImageData(10,10,50,50);
ctx.putImageData(imgData,10,70);

    ③createImageData()    ——    创建新的、空白的ImageData对象

    这个方法接收的参数:

    以下代码创建100*100像素的ImageData对象,其中每个像素都是红色的,然后把它放到画布上:

var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
var imgData=ctx.createImageData(100,100);
for (var i=0;i<imgData.data.length;i+=4)
  {
  imgData.data[i+0]=255;
  imgData.data[i+1]=0;
  imgData.data[i+2]=0;
  imgData.data[i+3]=255;
  }
ctx.putImageData(imgData,10,10);

    效果:

    示例

    创建一个简单的灰阶过滤器:

        var drawing = document.getElementById("drawing");

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){

            var context = drawing.getContext("2d"); //取得2D上下文对象

            var image = new Image();
            image.src = "liyangqiao.jpg";

            image.addEventListener("load", function(event){
                var red, green, blue, alpha, average;

                //绘制原始图像
                context.drawImage(image, 0, 0);

                //取得图像数据
                imageData = context.getImageData(0, 0, image.width, image.height);
                data = imageData.data;
                
                for(var i = 0, len = data.length; i < len; i += 4){
                    red = data[i];
                    green = data[i + 1];
                    blue = data[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);
            }, false);
  
        }

    注意只有在画布“干净”的情况下(即图像并非来自其他域),才可以取得图像数据。如果画布”不干净“,那么访问图像数据时会导致JavaScript错误。

十三、阴影

    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 = 5;
            context.shadowBlur = 5;
            context.shadowColor = "rgba(0, 0, 0, 0.5)";

            //绘制红色矩形
            context.fillStyle = "#F00";
            context.fillRect(10, 10, 50, 50);

            //绘制蓝色矩形
            context.fillStyle = "rgba(0, 0, 255, 1)";
            context.fillRect(30, 30, 50, 50);
            
        }

十四、渐变

    渐变由CanvasGradient实例表示,很容易通过2D上下文来创建和修改。

    要创建一个新的线性渐变对象,可以调用createLinearGradient()方法。

    这个方法接受4个参数:起点的x坐标、起点的y坐标、终点的x坐标、终点的y坐标。

    创建了渐变对象后,下一步就是使用addColorStop()方法来指定色标。

    这个方法接收两个参数:色标位置和CSS颜色值。色标位置是一个0(开始的颜色)到1(结束的颜色)之间的数字。

    示例:

        var drawing = document.getElementById("drawing");

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){

            var context = drawing.getContext("2d"); //取得2D上下文对象

            var gradient = context.createLinearGradient(30, 30, 70, 70);

            gradient.addColorStop(0, "white");
            gradient.addColorStop(1, "black");

            //绘制红色矩形
            context.fillStyle = "#ff0000";
            context.fillRect(10, 10, 50, 50);

            //绘制渐变矩形
            context.fillStyle = gradient;
            context.fillRect(30, 30, 50, 50);
  
        }

    要创建径向渐变(或放射性渐变),可以使用createRadialGradient()方法。

    这个方法接收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 = "#ff0000";
            context.fillRect(10, 10, 50, 50);

            //绘制渐变矩形
            context.fillStyle = gradient;
            context.fillRect(30, 30, 50, 50);
  
        }

十五、合成

    globalCompositionOperation属性表示后绘制的图形怎样与先绘制的图形结合。

    这个属性的值是字符串,可能的值如下:

  • source-over(默认值)    ——    后绘制的图形位于先绘制的图形上方
  • source-in    ——    后绘制的图形与先绘制的图形重叠的部分可见,两者其他部分完全透明
  • source-out    ——    后绘制的图形与先绘制的图形不重叠的部分可见,先绘制的图形完全透明
  • source-atop    ——    后绘制的图形与先绘制的图形重叠部分可见,先绘制图形不受影响
  • destination-over    ——    后绘制的图形位于先绘制的图形下方,只有之前透明像素下的部分才可见
  • destination-in    ——    后绘制的图形位于先绘制的图形下方,两者不重叠的部分完全透明
  • destination-out    ——    后绘制的图形擦除与先绘制的图形重叠的部分
  • destination-atop    ——    后绘制的图形位于先绘制的图形下方,在两者不重叠的地方,先绘制的图形会变透明
  • lighter    ——    后绘制的图形与先绘制的图形重叠部分的值相加,使该部分变亮
  • copy    ——    后绘制的图形完全替代与之重叠的先绘制图形
  • xor    ——    后绘制的图形与先绘制的图形重叠的部分执行“异或”操作

    ①source-over(默认值)

        var drawing = document.getElementById("drawing");

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){

            var context = drawing.getContext("2d"); //取得2D上下文对象

            context.globalCompositeOperation = "source-over";

            //绘制红色矩形
            context.fillStyle = "#F00";
            context.fillRect(10, 10, 50, 50);

            //绘制蓝色矩形
            context.fillStyle = "rgba(0, 0, 255, 1)";
            context.fillRect(30, 30, 50, 50);
        }

    ②source-in

        var drawing = document.getElementById("drawing");

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){

            var context = drawing.getContext("2d"); //取得2D上下文对象

            //绘制红色矩形
            context.fillStyle = "#F00";
            context.fillRect(10, 10, 50, 50);

            context.globalCompositeOperation = "source-in";

            //绘制蓝色矩形
            context.fillStyle = "rgba(0, 0, 255, 1)";
            context.fillRect(30, 30, 50, 50);
        }

    ③source-out

        var drawing = document.getElementById("drawing");

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){

            var context = drawing.getContext("2d"); //取得2D上下文对象

            //绘制红色矩形
            context.fillStyle = "#F00";
            context.fillRect(10, 10, 50, 50);

            context.globalCompositeOperation = "source-out";

            //绘制蓝色矩形
            context.fillStyle = "rgba(0, 0, 255, 1)";
            context.fillRect(30, 30, 50, 50);
        }

    ④source-atop

        var drawing = document.getElementById("drawing");

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){

            var context = drawing.getContext("2d"); //取得2D上下文对象

            //绘制红色矩形
            context.fillStyle = "#F00";
            context.fillRect(10, 10, 50, 50);

            context.globalCompositeOperation = "source-atop";

            //绘制蓝色矩形
            context.fillStyle = "rgba(0, 0, 255, 1)";
            context.fillRect(30, 30, 50, 50);
        }

    ⑤destination-over

        var drawing = document.getElementById("drawing");

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){

            var context = drawing.getContext("2d"); //取得2D上下文对象

            //绘制红色矩形
            context.fillStyle = "#F00";
            context.fillRect(10, 10, 50, 50);

            context.globalCompositeOperation = "destination-over";

            //绘制蓝色矩形
            context.fillStyle = "rgba(0, 0, 255, 1)";
            context.fillRect(30, 30, 50, 50);
        }

    ⑥destination-in

        var drawing = document.getElementById("drawing");

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){

            var context = drawing.getContext("2d"); //取得2D上下文对象

            //绘制红色矩形
            context.fillStyle = "#F00";
            context.fillRect(10, 10, 50, 50);

            context.globalCompositeOperation = "destination-in";

            //绘制蓝色矩形
            context.fillStyle = "rgba(0, 0, 255, 1)";
            context.fillRect(30, 30, 50, 50);
        }

    ⑦destination-out

        var drawing = document.getElementById("drawing");

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){

            var context = drawing.getContext("2d"); //取得2D上下文对象

            //绘制红色矩形
            context.fillStyle = "#F00";
            context.fillRect(10, 10, 50, 50);

            context.globalCompositeOperation = "destination-out";

            //绘制蓝色矩形
            context.fillStyle = "rgba(0, 0, 255, 1)";
            context.fillRect(30, 30, 50, 50);
        }

    ⑧destination-atop

        var drawing = document.getElementById("drawing");

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){

            var context = drawing.getContext("2d"); //取得2D上下文对象

            //绘制红色矩形
            context.fillStyle = "#F00";
            context.fillRect(10, 10, 50, 50);

            context.globalCompositeOperation = "destination-atop";

            //绘制蓝色矩形
            context.fillStyle = "rgba(0, 0, 255, 1)";
            context.fillRect(30, 30, 50, 50);
        }

    ⑨lighter

        var drawing = document.getElementById("drawing");

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){

            var context = drawing.getContext("2d"); //取得2D上下文对象

            //绘制红色矩形
            context.fillStyle = "#F00";
            context.fillRect(10, 10, 50, 50);

            context.globalCompositeOperation = "lighter";

            //绘制蓝色矩形
            context.fillStyle = "rgba(0, 0, 255, 1)";
            context.fillRect(30, 30, 50, 50);
        }

    ⑩copy

        var drawing = document.getElementById("drawing");

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){

            var context = drawing.getContext("2d"); //取得2D上下文对象

            //绘制红色矩形
            context.fillStyle = "#F00";
            context.fillRect(10, 10, 50, 50);

            context.globalCompositeOperation = "copy";

            //绘制蓝色矩形
            context.fillStyle = "rgba(0, 0, 255, 1)";
            context.fillRect(30, 30, 50, 50);
        }

    ⑾xor

        var drawing = document.getElementById("drawing");

        //确定浏览器支持<canvas>元素
        if(drawing.getContext){

            var context = drawing.getContext("2d"); //取得2D上下文对象

            //绘制红色矩形
            context.fillStyle = "#F00";
            context.fillRect(10, 10, 50, 50);

            context.globalCompositeOperation = "xor";

            //绘制蓝色矩形
            context.fillStyle = "rgba(0, 0, 255, 1)";
            context.fillRect(30, 30, 50, 50);
        }

十六、基本动画

16.1、动画的基本步骤

    ①清空canvas

    除非要画的内容会完全充满canvas(例如背景图),否则需要清空所有。

    最简单的做法就是用clearRect()方法。

    ②保存canvas状态

    如果要改变一些会改变canvas状态的设置(样式,变形之类的),又要在每画一帧之时都是原始状态的话,需要先保存一下。

    ③绘制动画图形

    这一步才是重绘动画帧

    ④恢复canvas状态

    如果已经保存了canvas的状态,可以先恢复它,然后重绘下一帧。

16.2、操纵动画

    在canvas上绘制内容是用canvas提供的或者自定义的方法,而通常,我们仅仅在脚本执行结束后才能看见结果,比如说,在for循环里面做完成动画是不太可能的,因为for循环一直高速执行,动画都来不及绘制。

    因此,为了实现动画,我们需要一些可以定时执行重绘的方法。

    首先,可以用window.setInterval()window.setTimeout(),和window.requestAnimationFrame()来设定定期执行一个指定函数:

  • setInterval(function, delay)    ——    当设定好间隔时间后,function会定期执行
  • setTimeout(function, delay)    ——    在设定好的时间之后执行函数
  • requestAnimationFrame(callback)    ——    告诉浏览器希望执行一个动画,并在重绘之前,请求浏览器执行一个特定的函数来更新动画

    如果并不需要与用户互动,可以使用setInterval()方法,它就可以定期执行指定代码。

    如果需要做一个游戏,我们可以使用键盘或者鼠标事件配合上setTimeout()方法来实现。

    通过设置事件监听,可以捕捉用户的交互,并执行相应的动作。

16.3、太阳系的动画

    下面的例子,采用window.requestAnimationFrame()实现动画效果。

    这个方法提供了更加平缓并更加有效率的方式来执行动画,当系统准备好了重绘条件的时候,才调用绘制动画帧。

    一般每秒钟回调函数执行60次,也有可能会被降低。

    一个小型的太阳系模拟动画:

        var sun = new Image();
        var moon = new Image();
        var earth = new Image();
        function init(){
            sun.src = 'https://mdn.mozillademos.org/files/1456/Canvas_sun.png';
            moon.src = 'https://mdn.mozillademos.org/files/1443/Canvas_moon.png';
            earth.src = 'https://mdn.mozillademos.org/files/1429/Canvas_earth.png';
            window.requestAnimationFrame(draw);
        }
        
        function draw(){
            var ctx = document.getElementById("drawing").getContext('2d');
            
            //后绘制的图形位于先绘制的图形下方,只有之前透明像素下的部分才可见
            ctx.globalCompositeOperation = 'destination-over';
            ctx.clearRect(0, 0, 300, 300);    //clear canvas

            ctx.fillStyle = 'rgba(0, 0, 0, 0.4)';
            ctx.strokeStyle = 'rgba(0, 153, 255, 0.4)';
            ctx.save();
            //移动坐标原点
            ctx.translate(150, 150);

            // Earth
            var time = new Date();
            ctx.rotate( ((2 * Math.PI) / 60) * time.getSeconds() + ((2 * Math.PI) / 60000) * time.getMilliseconds() );
            ctx.translate(105, 0);
            ctx.fillRect(0, -12, 50, 24);   //Shadow
            ctx.drawImage(earth, -12, -12);
            
            // Moon
            ctx.save();
            ctx.rotate( ((2 * Math.PI) / 60) * time.getSeconds() + ((2 * Math.PI) / 60000) * time.getMilliseconds() );
            ctx.translate(0, 28.5);
            ctx.drawImage(moon, -3.5, -3.5);
            ctx.restore();
            
            ctx.restore();

            ctx.beginPath();
            ctx.arc(150, 150, 105, 0, Math.PI * 2, false);  // Earth orbit
            ctx.stroke();

            ctx.drawImage(sun, 0, 0, 300, 300);

            window.requestAnimationFrame(draw);

        }

        init();

猜你喜欢

转载自blog.csdn.net/qq_35732147/article/details/82964017
今日推荐