canvas第一天

     第一次接触canvas,<canvas></canvas>是html5出现的新标签,IE9开始支持,像所有的dom对象一样它有自己本身的属性、方法和事件,其中就有绘图的方法,js能够调用它来进行绘图。使用的领域涉及到小游戏的开发以及数据的可视化。

 一、两个概念:

    1、 绘图环境的含义:

         绘制图形所需的东西,就叫绘图环境。

    2、 路径的含义:

         路径是一个图形的轮廓,是对将来要绘制图形的一个规划。如果要让路径最终生效需要描边。

 二、使用canvas绘制图形

         canvas要展示的绘制效果是通过js来绘制的。

      1、准备工作:

       (1)需要获取一个绘图环境:

            通过canvasDOM元素提供的一个方法,getContext。

            var cvs = document.querySelector('canvas');

      (2)获取绘图环境的方法:

             canvas.getContext( '2d' || 'webgl' )   传入2d代表获取一个2d绘图环境,传入3d代表获取一个3d绘图环境。

             var ctx = cvs.getContext('2d');

     2、绘制图形

      (1)先移动钢笔到指定的位置

             ctx.moveTo( 0, 0 );

      (2)画图形的路径( 绘制矩形 )           

             ctx.lineTo(100, 0);
             ctx.lineTo(100, 100);
             ctx.lineTo(0, 100);
             ctx.lineTo(0, 0);

      (3)描边

             ctx.stroke();

  三、画布设置

        1、 画布的默认设置:

             canvas画布默认大小为300*150。

        2、 动态设置画布的大小:

           (1)canvasDOM对象有一个width和height属性,通过修改这两个属性值,就可以动态设置画布的大小。因为canvas绘制的图形是位图(像素图),默认就是基于像素单位的(px),所以不用加单位。

             例:cvs.width = 500;

                  cvs.height = 500;

           (2)动态设置画布大小会清除画布的内容,绘制图形之后,动态修改画布的大小,绘自动清除画布已经绘图的内容。

           (3)不要设置canvas样式中的宽高,因为会拉伸画布,原理和img设置样式宽高拉伸图片一样。直接设置<canvas width=" " height=" "></canvas>

   四、基于状态

        canvas绘图环境中的很多方法也是基于状态的,即修改了canvas绘图环境中的某些属性值, 相关连的一些方法最终的执行结果可能就会收到改变。

        案例:

function Person( name, age ) {
            this.name = name;
            this.age = age;
        }

        // 这个方法就是基于状态(属性值)的
        Person.prototype.run = function() {
            if( this.age < 5 ) {
                console.log('爬着跑');
            }else if( this.age >=5 && this.age < 16 ) {
                console.log('跳着跑');
            }else if( this.age >=16 && this.age < 48 ) {
                console.log('慢跑');
            }else {
                console.log('拄着拐杖跑');
            }
        };

        var xiaofang = new Person('小芳', 12);
        var fangma = new Person('小芳妈', 37);
        var fangnai = new Person('小芳奶', 60);

        xiaofang.run();
        fangma.run();
        fangnai.run();

   五、描边色、填充、填充色及设置线宽

         1、 设置描边色

            (1) ctx.strokeStyle = css支持的所有颜色表示方式,这里都支持。

                 当修改了这个描边色之后,一些和描边相关的方法,再次调用时都会受到影响。

            (2)设置 描边色后要调动stroke()方法,新的图形才会显示。

            (3)多次绘制图形描边会把前面的覆盖。

            (4)解决上面出现的问题:清除路径

                  ctx.beginPath();

         2、填充 填充色

           (1)填充:ctx.fill();

           (2)设置填充色:ctx.fillStyle=css

        3、设置线宽

            (1)ctx.lineWidth=number;  注意:不用加"px"

            (2)设置线宽后,顶端会出现锯齿,防止锯齿出现:闭合路径。

                    绘制完路径后,调用closePath()方法,有了这个方法后,最后一条路径可以不用绘制了。

    六、 非零环绕原则

         1、作用:

              用来判断路径围起来的图形,是在路径内,还是路径外。

         2、原理:

           (1) 在路径包含的区域内随便找一点,向外发送一条线,让这条线贯穿所有围绕该点的路径即可

           (2)开始一个计数器,初始值为0

           (3)开始计数,遇到顺时针围绕点的路径,数+1,遇到逆时针围绕点的路径,数-1

           (4)最终看看结果,如果不是0,那么认为这个块区域在路径包含之内

    七、 清除画布

          ctx.clearRect(起点x轴坐标,起点y轴坐标,清除区域的宽,清除区域的高);

          清除整个画布: ctx.clearRect(0 , 0 , cvs.width , cvs.height);

    八、小属性及虚线绘制介绍

           1、 线帽样式:

                 ctx.lineCap = 'butt' || 'round' || 'square'     默认值为butt

                 round用来设置圆头(圆的半径是线宽的一半),square是两段个加长线宽的一半。

           2、 焦点样式:

                ctx.lineJoin = 'miter' || 'round' || 'bevel'     默认值为miter

                设置箭头长度,该属性只在lineJoin为miter的时候有效:ctx.miterLimit

           3、 虚线

             (1)设置虚线样式:

                    ctx.setLineDash([  ]);  注意:数组的长度是任意的,现实后空;可以传入一个参数,此时实空的长度一样。

             (2)获取虚线样式:

                   ctx.getLineDash();

    九、 画弧路径:

               ctx.arc( 圆心x轴坐标,圆心y轴坐标,半径,弧的起始位置,弧的结束位置,是否逆时针画(可选) )     默认是顺时针画弧。

               案例:

<canvas style="border: 1px solid red" width="500" height="500"></canvas>
    <script>
        var cvs = document.querySelector('canvas');
        var ctx = cvs.getContext('2d');

        // 角度转换为弧度
        function angleToRadian( angle ) {
            return Math.PI / 180 * angle;
        }

        // 顺时针从0度到90度画弧
        ctx.arc( 100, 100, 50, angleToRadian(0), angleToRadian(90) );
        ctx.stroke();

        // 逆时针从0度到90度画弧
        ctx.beginPath();
        ctx.arc( 300, 100, 50, angleToRadian(0), angleToRadian(90), true );
        ctx.stroke();

     十、 封装

                 1、 面向对象封装等腰三角形           

        // 绘制等腰三角形的方法
        function triangle( x, y, width, height, strokeStyle ) {
            /*
            * 实现思路:
            * 1、为了防止重绘之前的路径,先清除一下
            * 2、移动钢笔到图形起点
            * 3、画想要图形的路径
            * 4、设置描边色
            * 5、描边
            * */
            ctx.beginPath();
            ctx.moveTo( x, y );
            ctx.lineTo( x + width / 2, y + height );
            ctx.lineTo( x - width / 2, y + height );
            ctx.lineTo( x, y );
            ctx.strokeStyle = strokeStyle;
            ctx.stroke();
        }

        triangle( 100, 100, 100, 50, 'green' );

               2、 面向对象封装

           

// 等腰三角形的构造函数
        function Triangle( x, y, width, height, strokeStyle ) {
            this.x = x;
            this.y = y;
            this.width = width;
            this.height = height;
            this.strokeStyle = strokeStyle;
        }

        // 根据实例的属性绘制
        Triangle.prototype.draw = function() {
            /*
             * 实现思路:
             * 1、为了防止重绘之前的路径,先清除一下
             * 2、移动钢笔到图形起点
             * 3、画想要图形的路径
             * 4、设置描边色
             * 5、描边
             * */
            ctx.beginPath();
            ctx.moveTo( this.x, this.y );
            ctx.lineTo( this.x + this.width / 2, this.y + this.height );
            ctx.lineTo( this.x - this.width / 2, this.y + this.height );
            ctx.lineTo( this.x, this.y );
            ctx.strokeStyle = this.strokeStyle;
            ctx.stroke();
        };

        var triangle1 = new SanJiaoXing( 100, 100, 100, 50, 'green' );

        triangle1.draw();

    十一、 绘制坐标系                  

 /*
        * constructor { LineChart } 折线图构造函数
        * param { ctx: Context } 绘图上下文
        * param { paddingArr: Array } 折线图到画布四边的距离,存储顺序为上右下左
        * param { arrowArr: Array } 折线图中箭头的宽和高
        * */
        function LineChart( ctx, paddingArr, arrowArr ) {
            this.ctx = ctx;
            this.paddingArr = paddingArr;

            this.arrowArr = arrowArr;
            this.arrowWidth = this.arrowArr[0];
            this.arrowHeight = this.arrowArr[1];

            // 计算上顶点的坐标
            this.vertexTop = {
                x: this.paddingArr[ 3 ],
                y: this.paddingArr[ 0 ]
            };

            // 计算原点的坐标
            this.origin = {
                x: this.paddingArr[ 3 ],
                y: this.ctx.canvas.height - this.paddingArr[ 2 ]
            };

            // 计算右顶点的坐标
            this.vertexRight = {
                x: this.ctx.canvas.width - this.paddingArr[ 1 ],
                y: this.ctx.canvas.height - this.paddingArr[ 2 ]
            };
        }

        // 置换原型
        LineChart.prototype = {

            constructor: LineChart,

            // 绘制坐标轴中的两条线
            drawLine: function() {
                this.ctx.beginPath();
                this.ctx.moveTo( this.vertexTop.x, this.vertexTop.y );
                this.ctx.lineTo( this.origin.x, this.origin.y );
                this.ctx.lineTo( this.vertexRight.x, this.vertexRight.y );
                this.ctx.stroke();
            },

            // 绘制坐标轴中的两个箭头
            drawArrow: function() {

                // 先绘制上面箭头
                this.ctx.beginPath();
                this.ctx.moveTo( this.vertexTop.x, this.vertexTop.y );
                this.ctx.lineTo( this.vertexTop.x - this.arrowWidth / 2, this.vertexTop.y + this.arrowHeight );
                this.ctx.lineTo( this.vertexTop.x, this.vertexTop.y + this.arrowHeight / 2 );
                this.ctx.lineTo( this.vertexTop.x + this.arrowWidth / 2, this.vertexTop.y + this.arrowHeight );
                this.ctx.closePath();
                this.ctx.stroke();

                // 再绘制右面箭头
                this.ctx.beginPath();
                this.ctx.moveTo( this.vertexRight.x, this.vertexRight.y );
                this.ctx.lineTo( this.vertexRight.x - this.arrowHeight, this.vertexRight.y - this.arrowWidth / 2 );
                this.ctx.lineTo( this.vertexRight.x - this.arrowHeight / 2, this.vertexRight.y );
                this.ctx.lineTo( this.vertexRight.x - this.arrowHeight, this.vertexRight.y + this.arrowWidth / 2 );
                this.ctx.closePath();
                this.ctx.stroke();
            }
        };

        var lineChart = new LineChart( ctx, [ 20, 20, 20, 20 ], [ 10, 20 ] );
        lineChart.drawLine();
        lineChart.drawArrow();

    十二、 绘制折线图            

/*
        * constructor { LineChart } 折线图构造函数
        * param { ctx: Context } 绘图上下文
        * param { paddingArr: Array } 折线图到画布四边的距离,存储顺序为上右下左
        * param { arrowArr: Array } 折线图中箭头的宽和高
        * param { data: Array } 存储了折线图中所需的数据
        * */
        function LineChart( ctx, paddingArr, arrowArr, data ) {
            this.ctx = ctx;
            this.paddingArr = paddingArr;

            this.arrowArr = arrowArr;
            this.arrowWidth = this.arrowArr[0];
            this.arrowHeight = this.arrowArr[1];

            this.data = data;

            // 计算上顶点的坐标
            this.vertexTop = {
                x: this.paddingArr[ 3 ],
                y: this.paddingArr[ 0 ]
            };

            // 计算原点的坐标
            this.origin = {
                x: this.paddingArr[ 3 ],
                y: this.ctx.canvas.height - this.paddingArr[ 2 ]
            };

            // 计算右顶点的坐标
            this.vertexRight = {
                x: this.ctx.canvas.width - this.paddingArr[ 1 ],
                y: this.ctx.canvas.height - this.paddingArr[ 2 ]
            };

            // 根据数据得到对应的坐标
            this.processData();
        }

        // 置换原型
        LineChart.prototype = {

            constructor: LineChart,

            // 绘制折线图
            draw: function() {
                this.drawCoordinate();
                this.drawArrow();
                this.drawPoint();
                this.drawLine();
            },

            // 绘制坐标轴中的两条线
            drawCoordinate: function() {
                this.ctx.beginPath();
                this.ctx.moveTo( this.vertexTop.x, this.vertexTop.y );
                this.ctx.lineTo( this.origin.x, this.origin.y );
                this.ctx.lineTo( this.vertexRight.x, this.vertexRight.y );
                this.ctx.stroke();
            },

            // 绘制坐标轴中的两个箭头
            drawArrow: function() {

                // 先绘制上面箭头
                this.ctx.beginPath();
                this.ctx.moveTo( this.vertexTop.x, this.vertexTop.y );
                this.ctx.lineTo( this.vertexTop.x - this.arrowWidth / 2, this.vertexTop.y + this.arrowHeight );
                this.ctx.lineTo( this.vertexTop.x, this.vertexTop.y + this.arrowHeight / 2 );
                this.ctx.lineTo( this.vertexTop.x + this.arrowWidth / 2, this.vertexTop.y + this.arrowHeight );
                this.ctx.closePath();
                this.ctx.stroke();

                // 再绘制右面箭头
                this.ctx.beginPath();
                this.ctx.moveTo( this.vertexRight.x, this.vertexRight.y );
                this.ctx.lineTo( this.vertexRight.x - this.arrowHeight, this.vertexRight.y - this.arrowWidth / 2 );
                this.ctx.lineTo( this.vertexRight.x - this.arrowHeight / 2, this.vertexRight.y );
                this.ctx.lineTo( this.vertexRight.x - this.arrowHeight, this.vertexRight.y + this.arrowWidth / 2 );
                this.ctx.closePath();
                this.ctx.stroke();
            },

            // 把传入进来的数据转化为对应画布的坐标
            processData: function() {

                // 用来存储转换后的坐标数据
                this.processArr = [];

                // 遍历所有的数据,依次转换为对应的坐标
                for( var i = 0, len = this.data.length; i < len; i+=2 ) {
                    /*
                     * 数据转化为相当于画布的坐标:
                     * canvasX = this.origin.x + x
                     * canvasY = this.origin.y - y
                     * */
                    this.processArr.push( this.origin.x + this.data[ i ] );
                    this.processArr.push( this.origin.y - this.data[ i + 1 ] );
                }
            },

            // 根据数据绘制相应的点
            drawPoint: function() {
                var r = 4;

                // 遍历所有的坐标,依次绘制点
                for( var i = 0, len = this.processArr.length; i < len; i+=2 ) {
                    this.ctx.beginPath();
                    this.ctx.arc( this.processArr[ i ], this.processArr[ i + 1 ], r, 0, Math.PI*2 );
                    this.ctx.fill();
                }
            },

            // 根据数据绘制折线
            drawLine: function() {
                this.ctx.beginPath();
                for( var i = 0, len = this.processArr.length; i < len; i+=2 ) {
                    this.ctx.lineTo( this.processArr[ i ], this.processArr[ i + 1 ] );
                }
                this.ctx.stroke();
            }
        };

        var lineChart = new LineChart( ctx, [ 20, 20, 20, 20 ], [ 10, 20 ], [ 10, 10, 30, 20, 50, 50, 60, 80, 100, 100 ] );
        lineChart.draw();

      总结:

                     

            

posted on 2016-11-17 21:34  fatimah_man  阅读(255)  评论(0编辑  收藏  举报

导航