canvas笔记

作者: oldSix_Zhu | 来源:发表于2017-03-29 19:56 被阅读37次

    记录笔记,请多指教
    <a href="http://www.w3school.com.cn/html5/html5_canvas.asp">学习网址推荐</a>
    <a href="http://canvas.migong.org/">酷炫canvas动画网址推荐</a>
    <a href="http://echarts.baidu.com/echarts2/">公司用的图表ECharts</a>

    HTML5 < canvas > 标签是一个用来展示绘图效果的标签,它和< img >类似,都是行内块元素
    不过< canvas > 元素本身并没有绘制能力,它仅仅是图形的容器,或者是画板,用来展示绘图效果的
    需要通过 getContext() 方法来打开canvas,然后在上面用JS来完成实际的绘图


    <canvas> 标记和 SVG 以及 VML 之间的差异(引用w3school):
    <canvas> 标记和 SVG 以及 VML 之间的一个重要的不同是,<canvas> 有一个基于 JavaScript 的绘图 API,而 SVG 和 VML 使用一个 XML 文档来描述绘图。
    这两种方式在功能上是等同的,任何一种都可以用另一种来模拟。从表面上看,它们很不相同,可是,每一种都有强项和弱点。例如,SVG 绘图很容易编辑,只要从其描述中移除元素就行。
    要从同一图形的一个 <canvas> 标记中移除元素,往往需要擦掉绘图重新绘制它。

    以下是基础用法:

    1.最简单的画一个正方形
    <body>
        <!-- 画布默认300*150.设置画布的宽高-->
        <canvas id="cvs" width="500" height="500"></canvas>
        <script>
            var cvs = document.getElementById('cvs');
            var ctx = cvs.getContext('2d');
    
            // 1.先到起始的位置
            // 这一步也可以用lineTo()直接开始初始位置
            // ctx.moveTo( x轴移动的坐标,y轴移动的坐标 )
            ctx.moveTo( 10, 10 );
    
            // 2.画线条
            // ctx.lineTo( x轴移动的坐标,y轴移动的坐标 )
            ctx.lineTo( 110, 10 );
            ctx.lineTo( 110, 110 );
            ctx.lineTo( 10, 110 );
            ctx.lineTo( 10, 10 );
            // 有了closePath,绘制直线图形时,最后一条边就可以省去了
            //ctx.closePath();
    
            // 3.描边路径
            ctx.stroke();
        </script>
    </body>
    
    2.等比缩放折线图
    效果图

    .html文件:

    <body>
        <canvas id="cvs" width="500" height="500"></canvas>
        <script src="line.js"></script>
        <script>
            var cvs = document.getElementById('cvs');
            var ctx = cvs.getContext('2d');
    
            var line = new Line( ctx, [ 10, 30, 50, 430, 200, 50, 150, 200, 600 ] );
            line.draw();
        </script>
    </body>
    

    line.js文件:

    /*
     *  line  折线图构造函数
     *  ctx: Context 绘图环境
     *  data: Array 绘制折线图所需的数据
     *  padding: Object 设置坐标轴到画布的边距
     *  arrow: Object 设置箭头的宽高
     * */
    function Line( ctx, data, padding, arrow ) {
    
        this.ctx = ctx;
        this.data = data;
        this.padding = padding || { top: 10, right: 10, bottom: 10, left: 10 };
        this.arrow = arrow || { width: 10, height: 20 };
    
        // 上顶点的坐标
        this.vertexTop = {
            x: this.padding.left,
            y: this.padding.top
        }
    
        // 原点的坐标
        this.origin = {
            x: this.padding.left,
            y: this.ctx.canvas.height - this.padding.bottom
        }
    
        // 右顶点的坐标
        this.vertexRight = {
            x: this.ctx.canvas.width - this.padding.right,
            y: this.ctx.canvas.height - this.padding.bottom
        }
    
        // 计算坐标轴表示的最大刻度
        this.coordWidth = this.ctx.canvas.width - this.padding.left - this.padding.right - this.arrow.height;
        this.coordHeight = this.ctx.canvas.height - this.padding.top - this.padding.bottom - this.arrow.height;
    
    }
    
    // 给原型扩充方法
    Line.prototype = {
        constructor: Line,
    
        draw: function() {
            this.drawCoord();
            this.drawArrow();
            this.drawLine();
        },
    
        // 绘制坐标轴
        drawCoord: 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.arrow.width / 2, this.vertexTop.y + this.arrow.height );
            this.ctx.lineTo( this.vertexTop.x, this.vertexTop.y + this.arrow.height / 2 );
            this.ctx.lineTo( this.vertexTop.x + this.arrow.width / 2, this.vertexTop.y + this.arrow.height );
            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.arrow.height, this.vertexRight.y - this.arrow.width / 2 );
            this.ctx.lineTo( this.vertexRight.x - this.arrow.height / 2, this.vertexRight.y );
            this.ctx.lineTo( this.vertexRight.x - this.arrow.height, this.vertexRight.y + this.arrow.width / 2 );
            this.ctx.closePath();
            this.ctx.stroke();
        },
    
        // 根据数据绘制折线图
        drawLine: function() {
    
            // 先清除之前的路径
            this.ctx.beginPath();
    
            // 保存当前的this
            var self = this;
    
            /*
             * 计算x和y轴坐标的缩放比值:
             * ratioX = this.coordWidth / this.data.length
             * ratioY = this.coordHeight / Math.max.apply( this.data )
             * */
    
            var ratioX = this.coordWidth / this.data.length,
                ratioY = this.coordHeight / Math.max.apply( null, this.data );
    
            /*
             * 要根据原点的坐标来计算点的坐标
             * x = self.origin.x + x
             * y = self.origin.y - y
             * */
    
            // 遍历所有的数据,依次绘制点
            this.data.forEach( function( y, x ) {
                self.ctx.fillRect( self.origin.x + ( x * ratioX ) - 1, self.origin.y - ( y * ratioY ) - 1 , 2, 2 );
            });
    
            // 遍历所有的数据,依次绘制线
            this.data.forEach( function( y, x ) {
                self.ctx.lineTo( self.origin.x + ( x * ratioX ), self.origin.y - ( y * ratioY ) );
            });
    
            // 绘制线
            this.ctx.stroke();
        }
    }
    
    3.最简单的画弧或圆
    <body>
        <canvas id="cvs" width="500" height="500"></canvas>
        <script>
            var cvs = document.getElementById('cvs');
            var ctx = cvs.getContext('2d');
    
            /*
            * 画弧( 画的是路径 )
            * ctx.arc( 圆心x轴坐标,圆心y轴坐标,半径, 起点弧度,结束点弧度,是否逆时针画(可选) )
            * arc方法内部会先从路径结束点到弧的起点画一条路径线。
            * 起点弧度、结束点弧度以及弧度的方向共同决定了弧的大小。
            * */
    
            // 把角度转换为弧度
            function angleToRadian( angle ) {
                return Math.PI / 180 * angle;
            }
    
            // 顺时针画一段弧
            ctx.arc( 100, 100, 30, angleToRadian( 90 ), angleToRadian( 270 ) );
            ctx.stroke();
    
            // 逆时针画一段弧
            ctx.beginPath();
            ctx.arc( 200, 100, 30, angleToRadian( 90 ), angleToRadian( 270 ), true );
            ctx.stroke();
    
            // 一个圆
            ctx.beginPath();
            ctx.arc( 300, 100, 30, angleToRadian( 90 ), angleToRadian( 360 ) );
            ctx.stroke();
    
            /*
            * 画扇形:
            * 1、先设置路径起点为圆心
            * 2、画弧
            * 3、闭合路径
            * */
            ctx.beginPath();
            ctx.moveTo( 100, 300 );
            ctx.arc( 100, 300, 90, angleToRadian( 45 ), angleToRadian( 180 ) );
            ctx.closePath();
            ctx.stroke();
    
        </script>
    </body>
    
    4.基本的饼图
    效果图

    .html文件:

    <body>
        <canvas id="cvs" width="500" height="500"></canvas>
        <script src="drawBing.js"></script>
        <script>
            var cvs = document.getElementById('cvs');
            var ctx = cvs.getContext('2d');
    
            // [ 10, 30, 50, 60, 20 ]
            var bingTu = new bing( 200, 200, 80, [
                {
                    val: 10,
                    msg: 'C'
                },
                {
                    val: 30,
                    msg: 'HTML '
                },
                {
                    val: 50,
                    msg: 'CSS'
                },
                {
                    val: 50,
                    msg: 'JavaScript'
                },
                {
                    val: 50,
                    msg: 'swift'
                },
                {
                    val: 90,
                    msg: 'Object-C'
                },
            ] );
            bingTu.draw();
        </script>
    </body>
    

    drawBing.js文件

    (function( w ) {
    
        // 把角度转换为弧度
        function angleToRadian( angle ) {
            return Math.PI / 180 * angle;
        }
    
        // 混入式继承
        function extend( o1, o2 ) {
            for ( var key in o2 ) {
                // 只有o2自己的属性才会copy到o1身上
                if ( o2.hasOwnProperty( key ) ) {
                    o1[ key ] = o2[ key ];
                }
            }
        }
    
        /*
         * bing 饼图构造函数
         * x: number 圆心x轴坐标
         * y: number 圆心y轴坐标
         * r: number 圆半径
         * data: Array 绘制饼图所需的数据
         * */
        function bing( x, y, r, data ) {
    
            this.x = x;
            this.y = y;
            this.r = r;
            this.data = data;
    
            // 一组颜色
            this.colors = [ 'orange', 'orchid', 'palegoldenrod', 'palegreen', 'paleturquoise', 'peru', 'pink' ];
        }
    
        // 给原型扩充方法
        extend( bing.prototype, {
    
            // 绘制饼图
            draw: function() {
    
                // 在外面保存一下this
                var self = this;
    
                // 数据的总和
                var num = 0;
                this.data.forEach( function( obj ) {
                    num += obj.val;
                });
    
                // 一个数据值所占用的角度
                var baseAngle = 360 / num;
    
                // 假设一开始就绘制了一个起始为0,结束为0的扇形
                var startAngle = 0,
                    endAngle = 0,
                    lineAngle = 0,
                    lineX, lineY;
    
                // 画扇形
                this.data.forEach( function( obj, i ) {
    
                    // 每次进来,计算当前扇形的起始角度和结束角度
    
                    // 下一个扇形的起始角度,是当前扇形的结束角度
                    startAngle = endAngle;
                    // 这个结束角度 = 上一个扇形的结束角度 + 当前数值所对应的角度
                    endAngle = endAngle + baseAngle * obj.val;
    
                    // 求扇形中间线的角度
                    lineAngle = startAngle + baseAngle * obj.val / 2;
                    /*
                    * 根据中间线的角度,求中间的线的x和y坐标:
                    * x = 圆心x + r * Math.cos( angleToRadian( pointAngle ) )
                    * y = 圆心y + r * Math.sin( angleToRadian( pointAngle ) )
                    * */
                    lineX = self.x + ( self.r + 20 ) * Math.cos( angleToRadian( lineAngle ) );
                    lineY = self.y + ( self.r + 20 ) * Math.sin( angleToRadian( lineAngle ) );
    
    
                    // 画每一个扇形
                    ctx.beginPath();
                    ctx.moveTo( self.x, self.y );
                    ctx.arc( self.x, self.y, self.r, angleToRadian( startAngle ), angleToRadian( endAngle ) );
                    ctx.closePath();
                    ctx.fillStyle = self.colors[ i ];
                    ctx.fill();
    
                    // 画每一个扇形的平分线
                    ctx.beginPath();
                    ctx.moveTo( self.x, self.y );
                    ctx.lineTo( lineX, lineY );
                    ctx.strokeStyle = self.colors[ i ];
                    ctx.stroke();
    
                    // 绘制文字
                    if ( lineAngle >= 90 && lineAngle <= 270 ) {
                        ctx.textAlign = 'right';
                    }else {
                        ctx.textAlign = 'left';
                    }
                    ctx.fillText( obj.msg, lineX, lineY );
                });
            }
        } );
    
        // 把构造函数暴露到全局
        w.bing = bing;
    
    }( window ));
    
    
    5.绘制图像是绘制动画的基础
    <body>
        <canvas id="cvs" width="500" height="500"></canvas>
        ![](./imgs/XXX.jpg)
        <script>
            var cvs = document.getElementById('cvs');
            var ctx = cvs.getContext('2d');
            var img = document.querySelector('img');
            /*
            * ctx.drawImage()
            * 绘制图像,有三种使用方式
            * 必须要在img图像下载完毕之后使用
            * */
            
            //3参数
            //把图像绘制到指定的坐标
            img.onload = function() {
                ctx.drawImage( img, 10, 10 );
            }
    
            //5参数
            //把图像绘制到指定的坐标,并指定其大小
            img.onload = function() {
                ctx.drawImage( img, 10, 10, 200, 200 );
            }
    
            //9参数
            //把裁剪到的部分图像绘制到指定的坐标,并指定其大小。
            img.onload = function() {
                ctx.drawImage( img,
                        300, 100, 400, 400,
                        10, 10, 200, 200 );
            }
        </script>
    
    6.帧动画

    有了绘制图像的基础,加个定时器,拿一张精灵图不停地重绘就可以实现帧动画了

        <script>
            var cvs = document.getElementById('cvs');
            var ctx = cvs.getContext('2d');
            var img = document.querySelector('img');
    
            img.onload = function() {
                var i = 0;
                setInterval( function() {
                    // 绘制新的图像时,需要先清除画布
                    ctx.clearRect( 0, 0, cvs.width, cvs.height );
                    // 绘图每一帧
                    /*
                    * 裁剪的x轴,用来控制每一排不同的帧,
                    * 裁剪的y轴,用来控制当前行走的方向(也就是绘制那一排
                    * */
                    ctx.drawImage( img,
                        img.width / 4 * i, img.height / 4 * 3, img.width / 4, img.height / 4,
                        10, 10, 200, 200 );
                    // i的最大值为3
                    if ( ++i >= 4 ) 
                    {
                        i = 0;
                    }
                }, 200);
            };
        </script>
    
    7.平移.旋转.缩放

    旋转.平移.缩放加上帧动画,就是制作动画的基础了

    效果图
    <body>
        <canvas id="cvs" width="500" height="500"></canvas>
        <script>
            var cvs = document.getElementById('cvs');
            var ctx = cvs.getContext('2d');
    
            /*
            * 平移坐标轴:
            * ctx.translate( x轴平移量,y轴平移量 )
            * 已绘制的图形不会受到影响,平移会累加。
            * */
            ctx.fillStyle = 'blue';
            ctx.fillRect( 10, 10, 50, 50 );
            // 平移坐标轴之后,按照同样的坐标绘制一个填充矩形
            ctx.fillStyle = 'red';
            ctx.translate( 50, 50 );
            ctx.fillRect( 10, 10, 50, 50 );
            // 再平移
            ctx.fillStyle = 'green';
            ctx.translate( 50, 50 );
            ctx.fillRect( 10, 10, 50, 50 );
    
            /*
            * 旋转坐标轴:
            * ctx.rotate( 旋转的弧度 )
            * 已绘制的图形不会受到影响,旋转会累加。
            * */
            ctx.fillStyle = 'blue';
            ctx.translate( 100, 100 );
            ctx.fillRect( 0, 0, 50, 50 );
            // 旋转坐标轴,按照同样的坐标绘制填充矩形
            ctx.rotate( Math.PI / 6 );
            ctx.fillStyle = 'red';
            ctx.fillRect( 0, 0, 50, 50 );
            // 再旋转
            ctx.rotate( Math.PI / 6 );
            ctx.fillStyle = 'green';
            ctx.fillRect( 0, 0, 50, 50 );
    
            /*
            * 缩放坐标轴:
            * ctx.scale( x轴缩放的比值,y轴缩放的比值 )
            * 已绘制的图形不会受到影响,缩放会累加。
            * */
            ctx.fillStyle = 'blue';
            ctx.translate( 100, 100 );
            ctx.fillRect( 0, 0, 50, 50 );
            // 缩放坐标轴,再按照同样的坐标绘制填充矩形
            ctx.scale( 0.5, 0.5 );
            ctx.fillStyle = 'red';
            ctx.fillRect( 0, 0, 50, 50 );
            // 再缩放
            ctx.scale( 0.5, 0.5 );
            ctx.fillStyle = 'green';
            ctx.fillRect( 0, 0, 50, 50 );
        </script>
    </body>
    

    相关文章

      网友评论

        本文标题:canvas笔记

        本文链接:https://www.haomeiwen.com/subject/djdrnttx.html