Canvas基本篇 - 大家一起玩一玩 Canvas
什么是Canvas
就是 HTML 5 给出的一个可以展示绘图内容的标签,最早是 苹果公司 提出的 该标签。
canvas 使用领域 :
- 游戏
- 可视化数据(重点)
- banner 广告
- 多媒体
未来
模拟仿真 threejs.org ( 卖汽车的公司有些用这个做 3d 仿真展示 )
远程操作 万网远程控制貌似有用到
图形编辑 网页版的 ps,很多网站已经提供了绘图的功能
只要不是做游戏,我们一般能做到数据图形化就够了
Canvas 基本使用
- 页面新建一个 canvas 标签即可,默认就会占据 300 * 150 的宽高(面试有的会问哦)
宽高利用 html 属性 来设置宽高,注意: 千万不要使用 CSS 来设置。因为:
使用 属性设置 canvas 标签的宽高,实际上相当于增加了 canvas 画布的像素,是真的大了
而通过 css 设置画布大小,是将内容拉伸,不会增加像素点,只是将像素扩大或缩小,甚至会失真
Canvas 只是用于展示 绘图的内容,不能绘图
绘图需要获得绘图工具,每一个 Canvas 都有自己的一套工具。
如果绘制 2d 图形,语法 canvas.getContext( ‘2d’ ) 就会返回绘图工具集,这个工具专门绘制 2d平面图形,是个 CanvasRenderingContext2D 对象。
如果绘制 3d 立体 需要了解 webgl, 语法 canvas.getContext( ‘webgl’ ); 可以获得 WebGLRenderingContext对象,注意低版本可能不支持,这里不做探讨。
绘图常用方法
ctx.moveTo(x,y) 方法: 把画笔放在哪里
ctx.lineTo(x,y) 方法: 从刚才的位置开始 画到哪里
ctx.stroke(); 描边绘图方法,只有最后描边了才能看到效果,作用:将刚刚绘制的所有的点联系起来,就可以看到图形了
代码案例:
<canvas width='800' height='1000' id='cas' style='border: 1px solid blue;'></canvas>
<script>
// 不能用 css 设置, css 设置会拉伸,不会提高像素点
var cas = document.getElementById( 'cas' );
var ctx = cas.getContext( '2d' ); // 获得 CanvasRenderingContext2D 对象
ctx.moveTo( 200, 200 ); // 把笔放到 200 200
ctx.lineTo( 200, 300 ); // 划到 200 300 画一条竖线
ctx.stroke(); // 描边绘图方法,只有最后面描边了才能看到效果,所有的点联系起来
</script>
绘图的坐标系
canvas 中的坐标系 默认是以 canvas 左上角作为原点 (0, 0)
横向 x轴,向右为正,纵向 y轴,向下为正。
绘制虚线
虚线怎么绘制,虚线就是一小段一小段的实线。
- 实例:从 (100,100) 绘制虚线 到右下角 ( 500,100 )
<canvas width='800' height='1000' id='cas' style='border: 1px solid blue;'></canvas>
<script>
var cas = document.getElementById( 'cas' );
var ctx = cas.getContext( '2d' );
// 从 (100,100) 绘制虚线 到右下角 ( 500,100 ),先画一小段
ctx.moveTo( 100, 100 );
ctx.lineTo( 110, 100 );
// 空一段
// 在绘制一段,从 120 开始
ctx.moveTo( 120, 100 );
ctx.lineTo( 130, 100 );
ctx.stroke(); // 描边绘图,所有的点联系起来
</script>
可以看到两段虚线已经出来了。同理,用循环做
<canvas width='800' height='1000' id='cas' style='border: 1px solid blue;'></canvas>
<script>
var cas = document.getElementById( 'cas' );
var ctx = cas.getContext( '2d' );
var step = 10; //虚线距离
var start = 100; // 开始位置
var end = 500;
while( true ) { // 一直绘制下去
if( start > end ) break; // 大于 end 退出
// 实线部分
ctx.moveTo( start, 100 );
ctx.lineTo( start+=step, 100 );
// 绘制空白部分
start+=step;
}
ctx.stroke(); // 描边绘图,所有的点联系起来
</script>
这样我们虚线就画出来了,当然后面还有更简单的绘制方法,这里作为学习,现在这样的绘制是一种方法。
绘制正方形
绘制正方形需要知道坐标,边长,然后从坐标开始moveTo,一步一步连线lineTo连过去,最后描边绘图stroke联系起来, 非常的简单,正方形就出来了。
案例如下:
<canvas width='800' height='600' id='cas' style='border: 1px solid blue;'></canvas>
<script>
var cas = document.getElementById( 'cas' );
var ctx = cas.getContext( '2d' );
// 绘制正方形需要知道坐标,边长
// 假定 起点 (100,100) 宽高(边长) 200
ctx.moveTo( 100, 100 );
// 先横向 200
ctx.lineTo( 100 + 200, 100 );
// 在新的位置向下 200
ctx.lineTo( 300, 100 + 200 );
// 向左 200
ctx.lineTo( 300 - 200, 300 );
// 向上 200
ctx.lineTo( 100, 300 - 200 );
ctx.stroke(); // 描边绘图,所有的点联系起来
</script>
同理呢,三角形, 算算坐标不就好了:
var cas = document.getElementById( 'cas' );
var ctx = cas.getContext( '2d' ); // 获取绘画工具
ctx.moveTo( 100, 100 ); // 开始落笔 左边点
ctx.lineTo( 150, 150 ); // 连线 连到哪里
ctx.lineTo( 200, 100 );
ctx.lineTo( 100, 100 );
ctx.stroke(); // 所有的点联系起来
绘制方法
ctx.stroke( ); 这个方法我们刚才已经用过了, 作用:将刚刚绘制的所有的点联系起来
ctx.fill( ); 填充方法,他会在绘制的形状中进行填充,默认是黑色。如果绘制的时候没有形成一个形状,fill就会将开始的点和结束的点连起来,最后进行填充起来,用法跟 stroke 大同小异。
案例:正方形填充,把前面代码拿过来,最后一句 改成 fill 就好了
<canvas width='800' height='600' id='cas' style='border: 1px solid blue;'></canvas>
<script>
var cas = document.getElementById( 'cas' );
var ctx = cas.getContext( '2d' );
// 绘制正方形需要知道坐标,边长
// 假定 起点 (100,100) 宽高(边长) 200
ctx.moveTo( 100, 100 );
ctx.lineTo( 100 + 200, 100 );
ctx.lineTo( 300, 100 + 200 );
ctx.lineTo( 300 - 200, 300 );
ctx.lineTo( 100, 300 - 200 );
ctx.fill(); // 描边绘图
</script>
非零环绕原则 ( 理解很重要 )
如果需要判断某个区域是否需要填充颜色,就从该区域外随机的选取一个点,从这个点拉一条直线出来,穿过该图形,一定要拉到图形的外面,与穿过的线组成矢量线,看方向,如果是 顺时针方向,就是 +1,如果是 逆时针就 计为 -1, 看求和的结果, 如果是 0 就准备不填充,如果 非 0,就准备填充。就是非 0 环绕原则。
这是浏览器的渲染解析原则,一定要理解。不理解的可以看下下面的 案例 和 图示。
案例:画一个回字框框,可以用 顺时针的正方形 套 逆时针的 正方形来做,看图解析原理
根据图示呢,我们就来画个回形,顺时针套逆时针,最后 ctx.fill( )
<canvas width='800' height='600' id='cas' style='border: 1px solid blue;'></canvas>
<script>
var cas = document.getElementById( 'cas' );
var ctx = cas.getContext( '2d' );
// 顺时针的正方形
ctx.moveTo( 100, 100 );
ctx.lineTo( 300, 100 );
ctx.lineTo( 300, 300 );
ctx.lineTo( 100, 300 );
ctx.lineTo( 100, 100 );
// 逆时针的正方形
ctx.moveTo( 150, 150 );
ctx.lineTo( 150, 250 );
ctx.lineTo( 250, 250 );
ctx.lineTo( 250, 150 );
ctx.lineTo( 150, 150 );
ctx.fill(); // 描边填充绘图
</script>
闭合路径 closePath
closePath(): 当我们进行绘图的时候,可以通过closePath方法来将结束的点和开始的点进行闭合。
案例:
<canvas width='800' height='600' id='cas' style='border: 1px solid blue;'></canvas>
<script>
var cas = document.getElementById( 'cas' );
var ctx = cas.getContext( '2d' );
ctx.moveTo( 100, 100 );
ctx.lineTo( 300, 100 );
ctx.lineTo( 300, 300 );
ctx.closePath(); // 闭合
ctx.stroke(); // 描边绘图
</script>
lineWidth: 可以用来设置线条的粗细。 ctx.lineWidth = 3;
canvas 为了图形能比较美观的展示,会自动给图形取消锯齿的操作,在原有图形线条上延展加像素宽1px左右并虚化的操作。所以线宽多多少少会有些偏差。
同时注意:moveTo是以线条的中心作为起始点的。 那么用 lineTo 闭合起来就会有问题。
<canvas width="600" height="400" id="cas" style='border:1px solid black;'></canvas>
<script>
var cas = document.getElementById( 'cas' );
var ctx = cas.getContext( '2d' );
ctx.lineWidth = 20;
ctx.moveTo( 50, 100 );
ctx.lineTo( 150, 100 );
ctx.lineTo( 150, 200 );
ctx.lineTo( 50, 100 );
ctx.moveTo( 200, 100 );
ctx.lineTo( 300, 100 );
ctx.lineTo( 300, 200 );
ctx.closePath();
ctx.stroke();
</script>
所以 closePath 与 直接使用 lineTo 闭合是有区别的,尽量使用 closePath 闭合
Canvas 中绘图是有状态的 - 开启新路径
Canvas 绘图是含有状态的, 在需要改变颜色, 绘制方法, 改变一些属性…
当需要换颜色,换fill或者换stroke等,就需要改变绘图状态, 使用 beginPath() 方法. 开启一个新的路径.
<canvas width="600" height="400" id="cas" style='border:1px solid black;'></canvas>
<script>
var cas = document.getElementById( 'cas' );
var ctx = cas.getContext( '2d' );
ctx.moveTo( 50, 100 );
ctx.lineTo( 150, 100 );
ctx.lineTo( 150, 200 );
ctx.closePath();
ctx.stroke();
ctx.beginPath(); // 下面的就不会影响到上面的了
ctx.moveTo( 200, 100 );
ctx.lineTo( 300, 100 );
ctx.lineTo( 300, 200 );
ctx.closePath();
ctx.fill();
</script>
设置线末端类型 lineCap - 线帽
设置线型末端的样式, 可取值为: ‘butt’( 默认 ), ‘round’, ‘square’. 默认方形
‘butt’ 表示两端使用方形结束. ctx.lineCap = ‘butt’;
‘round’ 表示两端使用圆角结束. ctx.lineCap = ‘round’;
‘square’ 表示突出的圆角结束. ctx.lineCap = ‘square’;
设置相交线的拐点 lineJoin - 线拐
设置两条直线的拐点描述方式. 可以取值 ‘round’, ‘bevel’, ‘miter’(默认)
‘round’ 使用圆角连接. ctx.lineJoin = ‘round’;
‘bevel’ 使用平切连接. ctx.lineJoin = ‘bevel’;
‘miter’ 使用直角转. ctx.lineJoin = ‘miter’;
<canvas width="600" height="400" id="cas" style='border:1px solid black;'></canvas>
<script>
var cas = document.getElementById( 'cas' );
var ctx = cas.getContext( '2d' );
ctx.moveTo( 50, 100 );
ctx.lineTo( 150, 100 );
ctx.lineTo( 150, 200 );
ctx.lineWidth = 20;
ctx.lineJoin = 'round'; // 线拐 圆角
ctx.closePath();
ctx.stroke();
ctx.beginPath(); // 下面的不会影响到上面的了
ctx.moveTo( 200, 100 );
ctx.lineTo( 200, 300 );
ctx.lineWidth = 20;
ctx.lineCap = 'round'; // 线帽 圆角
ctx.stroke();
</script>
虚线绘制
ctx.setLineDash( 数组 ) 画虚线
ctx.getLineDash() 获取设置的 数组
ctx.lineDashOffset = 值 虚线偏移 + 右 - 左
ctx.setLineDash( 数组 ),数组中存储的数字就是分别表示 实线部分与空白部分的长度
<canvas width="600" height="400" id="cas" style='border:1px solid black;'></canvas>
<script>
var cas = document.getElementById( 'cas' );
var ctx = cas.getContext( '2d' );
ctx.setLineDash( [ 15, 5 ] ); // 实线 15 空白 5 为一个周期
// [ 15, 5, 20 ]奇数项时自动补全成 [ 15, 5, 20, 15, 5, 20 ]
// console.log( ctx.getLineDash() ); 可以看到就是 [ 15, 5, 20, 15, 5, 20 ]
// (实 15,空 5,实 20,空 15, 实 5,空 20) 为一个周期
ctx.moveTo( 100, 150 );
ctx.lineTo( 300, 150 );
ctx.stroke();
</script>
如何设置描边与填充的颜色
ctx.strokeStyle 设置描边的颜色
ctx.strokeStyle = ‘rgb( 255, 0, 0 )’ ; // 绘制 stroke 颜色
ctx.fillStyle 设置填充的颜色
ctx.fillStyle = ‘rgb( 255, 0, 0 )’ ; // 绘制 fill 颜色
注意每次换颜色都要 beginPath();
设置颜色案例:
<canvas width="600" height="400" id="cas" style='border:1px solid black;'></canvas>
<script>
var cas = document.getElementById( 'cas' );
var ctx = cas.getContext( '2d' );
for ( var i = 0; i < 256; i++ ) {
ctx.beginPath();
ctx.strokeStyle = 'rgb( ' + i + ', ' + ( 255 - i ) + ', ' + i + ')';
ctx.moveTo( 50 + i, 100 );
ctx.lineTo( 50 + i, 300 );
ctx.stroke();
}
</script>
直线构造函数的 初步简陋封装
<canvas width='600' height='400' id='cas' style='border: 1px solid blue;'></canvas>
<script>
var cas = document.getElementById('cas');
var ctx = cas.getContext('2d');
function Line( ctx, x0, y0, x1, y1 ) {
this.ctx = ctx;
this.x0 = x0;
this.y0 = y0;
this.x1 = x1;
this.y1 = y1;
}
Line.prototype = {
constructor : Line,
stroke : function() {
this.ctx.moveTo( this.x0, this.y0 );
this.ctx.lineTo( this.x1, this.y1 );
this.ctx.stroke();
}
}
var line = new Line( ctx, 100, 100, 400, 400 );
line.stroke();
</script>
目前的封装呢,不太完善,因为如果要进行设置绘制颜色啊,线帽啊等等时,就会参数十分庞大, 我们需要改良。
直线构造函数的 简陋改良版
<canvas width='600' height='400' id='cas' style='border: 1px solid blue;'></canvas>
<script>
var cas = document.getElementById('cas');
var ctx = cas.getContext('2d');
function Line( config ) {
this.ctx = config.ctx;
this.points = config.points;
this.strokeStyle = config.strokeStyle;
this.lineWidth = config.lineWidth;
}
Line.prototype = {
constructor : Line,
stroke : function() {
this.ctx.moveTo( this.points[0], this.points[1] );
this.ctx.lineTo( this.points[2], this.points[3] );
if ( this.strokeStyle ) this.ctx.strokeStyle = this.strokeStyle;
if ( this.lineWidth ) this.ctx.lineWidth = this.lineWidth;
this.ctx.stroke();
}
}
var line = new Line( {
ctx: ctx,
points : [ 100, 100, 400, 100 ],
strokeStyle:'red',
lineWidth: 5
});
line.stroke();
</script>