强大的画图标签——Canvas(中级篇)

2.中级篇

2.1. 绘制形状

绘制图形不仅仅是利用线条来实现绘图, 还可以有快捷的绘制图形的办法
1.绘制矩形
2.绘制圆弧

2.1.1. 绘制矩形

绘制矩形的方法:

1.CanvasRenderingContext2D.strokeRect
2.CanvasRenderingContext2D.fillRect
3.CanvasRenderingContext2D.rect

注意: rect 方法就是矩形路径, 还需要使用 fill 或 stroke 才可以看到效果. 因此一般使用 strokeRect 或 fillRect 直接可以看到结果.

清除矩形区域:CanvasRenderingContext2D.clearRect

2.1.1.1. 绘制矩形框

语法: CanvasRenderingContext2D.strokeRect( x, y, width. height )

描述:

1.用来绘制一个矩形. 比起直接使用 moveTo 和 lineTo 方法要简单许多.
2.该方法的前两个参数表示绘制矩形的左上角的坐标. 后两个参数表示这个矩形的宽高.
3.使用该方法不需要使用 moveTo 方法设置起始点, 也不需要调用 stroke 等绘画方法.
4.绘制的矩形支持 strokeStyle 设置颜色样式.

案例:

ctx.strokeStyle = ‘red’;
ctx.strokeRect( 100, 100, 200, 100 );

效果:
这里写图片描述

2.1.1.2. 绘制填充矩形

语法: CanvasRenderingContext2D.fillRect( x, y, width. height )

描述:

1.用来绘制一个矩形. 比起直接使用 moveTo 和 lineTo 方法要简单许多.
2.该方法的前两个参数表示绘制矩形的左上角的坐标. 后两个参数表示这个矩形的宽高.
3.使用该方法不需要使用 moveTo 方法设置起始点, 也不需要调用 stroke 等绘画方法.
4.绘制的矩形支持 fillStyle 设置颜色样式.

案例:

ctx.fillStyle = ‘green’;
ctx.fillRect( 100, 100, 200, 100 );

效果:
这里写图片描述

2.1.1.3. 清除矩形区域

语法: CanvasRenderingContext2D.clearRect( x, y, width, height )

描述:

1.用于清除画布中的矩形区域的内容.
2.参数 x, y 表示矩形区域左上角的坐标, width 与 height 表示矩形区域的宽高.

案例:

ctx.fillRect( 100, 100, 200, 100 );
ctx.clearRect( 110, 110, 50, 50 );

效果:
这里写图片描述

2.1.1.4. 案例

利用绘制图形与清除矩形区域, 可以实现简单的动画. 例如代码:

 var x = 10, y = 10, oldx = 10, oldy = 10;
    var width = 100, height = 50;     
    var intervalId = setInterval(function () {
        ctx.clearRect( oldx - 1, oldy - 1, width + 2, height + 2 );

        ctx.strokeRect( x, y, width, height );

        oldx = x;
        oldy = y;

        x += 4;
        y += 2;

        if ( oldy >= 200 ) {
            // clearInterval( intervalId );
            x = 10, y = 10;
        }
    }, 20);

效果:
这里写图片描述

有时为了简单常常将整个画布都清除, 这样就不用每次计算清除的问题.

ctx.clearRect( 0, 0, cas.width, cas.height );
    // 也可以设置画布宽度, 这样就会自动清除
    cas.width = cas.width;

2.1.2. 绘制圆弧

绘制圆弧的方法有

CanvasRenderingContext2D.arc()
CanvasRenderingContext2D.arcTo()

2.1.2.1. 绘制圆弧

语法: CanvasRenderingContext2D.arc( x, y, radius. startAngle. endAngle, anticlockwise )

描述:

1.该方法用于绘制一段弧, 配合开始点的位置 与 stroke 方法或 fill 方法可以绘制扇形.
2.方法中的前两个参数 x, y 表示绘制圆弧的圆心坐标.
3.参数 radius 表示圆弧半径, 单位为弧度.
4.参数 startAngle 与 endAngle 表示开始到结束的角度. 角度以水平向右为 0 弧度, 顺时针为正方向.
5.参数 anticlockwise 表示是否采用默认的正向角度, 如果传入 true 表示逆指针为正. 该参数可选.

案例:

 // 在 200, 200 的地方绘制一段半径为 100 的圆弧, 圆心角为 - PI / 2 到 PI / 4
    ...
    ctx.arc( 200, 200, 100, -Math.PI/2, Math.PI/4 );
    ctx.stroke();

    // 为了方便看清楚结构, 绘制坐标轴
    ctx.beginPath();
    ctx.strokeStyle = 'red';
    ctx.moveTo( 50, 200 );
    ctx.lineTo( 350, 200 );

    ctx.moveTo( 200, 50 );
    ctx.lineTo( 200, 350 );

    ctx.moveTo( 200, 200 );
    ctx.lineTo( 300, 300 );

    ctx.stroke();

效果:
这里写图片描述

2.1.2.1.1. 注意事项

1.使用 arc 绘图的时候, 如果没有设置 moveTo 那么会从开始的绘弧的地方作为起始点. 如果设置了 moveTo, 那么会连线该点与圆弧的起点.
2.如果使用 stroke 方法, 那么会从开始连线到圆弧的起始位置. 如果是 fill 方法, 会自动闭合路径填充.

例如:
这里写图片描述

2.1.2.2. 绘制扇形

绘制扇形的重点是需要设置起始位置为圆心点, 然后闭合路径即可

 ...
    ctx.strokeStyle = 'red';
    ctx.fillStyle = 'pink';

    ctx.moveTo( 100, 200 );
    ctx.arc( 100, 200, 100, -Math.PI/3, Math.PI/3 );
    ctx.closePath();
    ctx.stroke();

    ctx.beginPath();
    ctx.moveTo( 300, 200 );
    ctx.arc( 300, 200, 100, -Math.PI/3, Math.PI/3 );
    ctx.closePath();
    ctx.fill();

效果:
这里写图片描述

2.1.2.3. 绘制扇形动画

绘制扇形动画, 就是每隔几毫秒( 20 毫秒)擦除以前绘制的内容, 然后在以前绘制的基础上比以前多绘制一点东西. 这里多绘制的内容就是由角度决定. 比如一开始角度从 -Math.PI / 2 开始绘制. 那么每次角度都 +0.1, 直到 绘制到 Math.PI * 3 / 2 为止.

...
    ctx.fillStyle = 'green';
    var startAngle = -Math.PI / 2,
        angle = startAngle,
        x = 200, y = 200,
        r = 100;
    var intervalId = setInterval(function () {
        // 清除之前绘制的内容
        ctx.clearRect( 0, 0, cas.width, cas.height );
        // 角度增量
        angle += 0.1;
        // 判断是否停止计时器
        if ( angle >= Math.PI * 3 / 2 ) {
            clearInterval( intervalId);
            angle = Math.PI * 3 / 2; 
            console.log( '绘制完成' );
        }
        // 绘制
        ctx.moveTo( x, y );
        ctx.arc( x, y, r, startAngle, angle );
        ctx.fill();
    }, 20);
2.1.2.4. 绘制饼形图

绘制饼形图最大的特点是角度是叠加的. 开始从 -Math.PI/2 开始绘制, 达到执行角 x 后, 下一个区域从 x 开始绘制, 然后有到一个角 y 停下来. 如此反复到 Math.PI * 3 / 2 结束.

2.1.2.4.1. 三等分饼形图

绘制一个三等分的饼形图, 颜色使用 红, 绿, 蓝.

 var x = 200, y = 200,
        r = 100,
        step = Math.PI * 2 / 3,     // 120 度一个区域
        start = -Math.PI / 2,       // 起始角度
        colors = [ 'red', 'green', 'blue' ];

    for ( var i = 0; i < 3; i++ ) {
        ctx.beginPath();
        ctx.moveTo( x, y );
        ctx.fillStyle = colors[ i ];
        ctx.arc( x, y, r, start, start+=step );
        ctx.fill();
    }

效果:
这里写图片描述

2.1.2.4.2. 根据数据定义角度

根据数据源定义角度, 就是将所有的数据求和, 按照总和为 2 * Math.PI 的结论计算出每一个数据部分的弧度值. 同时颜色可以提前定义好.

从 Konva 库中分离出来的颜色

var colors = 
        ( "aliceblue,antiquewhite,aqua,aquamarine,azure,beige,bisque,black,blanchedalmond,blue," +
        "blueviolet,brown,burlywood,cadetblue,chartreuse,chocolate,coral,cornflowerblue,cornsilk," +
        "crimson,cyan,darkblue,darkcyan,darkgoldenrod,darkgray,darkgreen,darkgrey,darkkhaki,darkmagenta," +
        "darkolivegreen,darkorange,darkorchid,darkred,darksalmon,darkseagreen,darkslateblue,darkslategray," +
        "darkslategrey,darkturquoise,darkviolet,deeppink,deepskyblue,dimgray,dimgrey,dodgerblue,firebrick," +
        "floralwhite,forestgreen,fuchsia,gainsboro,ghostwhite,gold,goldenrod,gray,green,greenyellow,grey," +
        "honeydew,hotpink,indianred,indigo,ivory,khaki,lavender,lavenderblush,lawngreen,lemonchiffon," + 
        "lightblue,lightcoral,lightcyan,lightgoldenrodyellow,lightgray,lightgreen,lightgrey,lightpink," +
        "lightsalmon,lightseagreen,lightskyblue,lightslategray,lightslategrey,lightsteelblue,lightyellow," +
        "lime,limegreen,linen,magenta,maroon,mediumaquamarine,mediumblue,mediumorchid,mediumpurple," +
        "mediumseagreen,mediumslateblue,mediumspringgreen,mediumturquoise,mediumvioletred,midnightblue," +
        "mintcream,mistyrose,moccasin,navajowhite,navy,oldlace,olive,olivedrab,orange,orangered,orchid," +
        "palegoldenrod,palegreen,paleturquoise,palevioletred,papayawhip,peachpuff,peru,pink,plum,powderblue," +
        "purple,rebeccapurple,red,rosybrown,royalblue,saddlebrown,salmon,sandybrown,seagreen,seashell,sienna," +
        "silver,skyblue,slateblue,slategray,slategrey,snow,springgreen,steelblue,tan,teal,thistle,transparent," +
        "tomato,turquoise,violet,wheat,white,whitesmoke,yellow,yellowgreen" ).split( ',' );

如果得到数据

var data = [ 123, 156, 47, 100, 80 ];

那么计算各个部分的比例时, 可以构造一个存储分量值与弧度的对象数组.

var sum = 0;
    for ( var i = 0; i < data.length; i++ ) {
        sum += data[ i ];
    }
    // 得到总数后, 分量比就有了
    var odata = data.map(function ( v, i ) {
        return { value: v, radius: v * 2 * Math.PI / sum };
    });

最后根据数据开始绘图

// 开始绘图
    var start = -Math.PI / 2,
        x = 200, y = 200,
        r = 100;

    for ( var i = 0; i < odata.length; i++ ) {
        ctx.beginPath();
        ctx.fillStyle = colors[ i + 10 ];
        ctx.moveTo( x, y );
        ctx.arc( x, y, r, start, start+=odata[ i ][ 'radius' ] );
        ctx.fill();
    }

效果:
这里写图片描述

2.1.2.5. 绘制相切弧

语法: CanvasRenderingContext2D.arcTo( x1, y1, x2, y2, radius )

描述:

1.该方法用于绘制圆弧
2.绘制的规则是当前位置与第一个参考点连线, 绘制的弧与该直线相切.
3.同时连接两个参考点, 圆弧根据半径与该连线相切

例如有一个起始点 ( 100, 100 ), 那么绘制其点. 颜色设置为红色.

ctx.fillStyle = 'red';
    ctx.fillRect( 100 - 4, 100 - 4, 8, 8 );

然后两个参考点分别为 ( 100, 300 ) 和 ( 300, 300 ), 绘制出该点

ctx.fillRect( 100 - 4, 300 - 4, 8, 8 );
ctx.fillRect( 300 - 4, 300 - 4, 8, 8 );

连接两个参考点

ctx.beginPath();
ctx.strokeStyle = 'red';
ctx.moveTo( 100, 300 );
ctx.lineTo( 300, 300 );
ctx.stroke();

得到效果为:
这里写图片描述

调用 arcTo 方法绘制圆弧. 记得将起始点设置为 ( 100, 100 )

ctx.beginPath();
ctx.strokeStyle = 'blue';
ctx.moveTo( 100, 100 );

ctx.arcTo( 100, 300, 300, 300, 100 );
ctx.stroke();

得到效果:
这里写图片描述

注意: 使用该方法可以使用圆弧连接两条直线, 而不用计算复杂的起始角度与结束角度. 因此用于绘制圆角矩形等案例较多.

2.1.2.6. 绘制圆角矩形

封装一个函数, 用于绘制圆角矩形.

1.参考 rect 方法, 需要坐标参数 x, y.
2.由于设置圆角, 因此需要设置圆角半径 cornerRadius.
3.还需要提供宽高.

首先绘制一个矩形边框. 但是需要考虑圆角, 虽然从 x, y 开始绘制, 但是中间要空出 半径的距离.

var x = 100, y = 100, width = 300, height = 100,
cornerRadius = 10;
ctx.strokeStyle = 'red';

ctx.moveTo( x + cornerRadius, y );
ctx.lineTo( x + width - cornerRadius, y );

ctx.moveTo( x + width, y + cornerRadius );
ctx.lineTo( x + width, y + height - cornerRadius );

ctx.moveTo( x + width - cornerRadius, y + height );
ctx.lineTo( x + cornerRadius, y + height );

ctx.moveTo( x, y + height - cornerRadius );
ctx.lineTo( x, y + cornerRadius );

ctx.stroke();

效果为:
这里写图片描述

然后再分别绘制四个角, 设置当前位置与参考点的位置. 设置当前位置为一个线端点, 然后参考点依次就是 矩形顶点 和 另一个线段的端点.

ctx.moveTo( x + cornerRadius, y );
ctx.arcTo( x, y, x, y + cornerRadius, cornerRadius );

即可得到:
这里写图片描述

同理绘制另外三个圆角

ctx.moveTo( x + width - cornerRadius, y );
ctx.arcTo( x + width, y, x + width, y + cornerRadius, cornerRadius );

ctx.moveTo( x + width, y + height - cornerRadius );
ctx.arcTo( x + width, y + height, x + width - cornerRadius, y + height, cornerRadius );

ctx.moveTo( x + cornerRadius, y + height );
ctx.arcTo( x, y + height, x, y + height - cornerRadius, cornerRadius );

即可得到:
这里写图片描述

封装成方法就可以绘制更多圆角矩形了. 封装中注意 beginPath() 和 save() 和 restore()

function cRect ( x, y, width, height, cornerRadius, color ) {
        ctx.save();
        ctx.beginPath();
        ctx.strokeStyle = color || 'red';

        ctx.moveTo( x + cornerRadius, y );
        ctx.lineTo( x + width - cornerRadius, y );

        ctx.moveTo( x + width, y + cornerRadius );
        ctx.lineTo( x + width, y + height - cornerRadius );

        ctx.moveTo( x + width - cornerRadius, y + height );
        ctx.lineTo( x + cornerRadius, y + height );

        ctx.moveTo( x, y + height - cornerRadius );
        ctx.lineTo( x, y + cornerRadius );


        // 开始绘制四个圆角
        ctx.moveTo( x + cornerRadius, y );
        ctx.arcTo( x, y, x, y + cornerRadius, cornerRadius );

        ctx.moveTo( x + width - cornerRadius, y );
        ctx.arcTo( x + width, y, x + width, y + cornerRadius, cornerRadius );

        ctx.moveTo( x + width, y + height - cornerRadius );
        ctx.arcTo( x + width, y + height, x + width - cornerRadius, y + height, cornerRadius );

        ctx.moveTo( x + cornerRadius, y + height );
        ctx.arcTo( x, y + height, x, y + height - cornerRadius, cornerRadius );

        ctx.stroke();
        ctx.restore();
    }

调用代码:

cRect( 50, 50, 100, 50, 5 );
cRect( 100, 120, 100, 80, 8, 'blue' );
cRect( 300, 100, 200, 100, 10, 'green' );

得到结果为:
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值