Canvas学习笔记(一)

269 阅读6分钟

什么是 Canvas

Canvas 是 HTML5 中新增的一种标签,表示一个画布,只是图形容器,绘制功能必须使用js来实现。

<!doctype html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>基础的HTML5页面</title> 
</head>
<body>
	<canvas id="canvas">
            这里是canvas标签,当你的浏览器不支持canvas时,会显示这行文字
	</canvas>
</body> 

</html>

打开后会是一个完全空白的画面,因为canvas本意就是一块画布,画布在html5中是透明的,不可见的。

image.png


绘制前的准备

获取 Canvas 对象:

const canvas = document.getElementById('canvas');

获取画笔

const context = canvas.getContext(contextType, contextAttributes?);
const context = canvas.getContext('2d');
const ctx = canvas.getContext('2d', {
  alpha: true,          // 启用透明通道(默认就是true)
});

getContext方法用于获取 Canvas 元素的绘图上下文,来对 Canvas 元素进行绘制操作,通常是 2D 类型,用于二维绘图。第一个参数是必须传的,第二个参数代表配置上下文的行为,不同的上下文的配置属性不同。

  • 2d
属性说明默认值
alpha是否包含alpha通道(透明度)true
colorSpace指定渲染上下文的色彩空间(srgbdisplay-p3srgb
desynchronized是否将画布绘制周期与事件循环解耦以减少延迟false
willReadFrequently是否频繁读取像素(用于优化getImageData()性能)false
  • webgl
属性作用说明
alpha是否包含alpha通道和2D一样
antialias是否开启抗锯齿让图形边缘更平滑
depth是否包含深度缓冲区用于3D场景的深度检测
failIfMajorPerformanceCaveat性能低时是否创建上下文true:性能差时失败
powerPreferenceGPU电源偏好default/high-performance/low-power
premultipliedAlpha是否预混合alpha用于图像合成
preserveDrawingBuffer是否保存缓冲区true:可以多次读取
stencil是否包含模版缓冲区用于复杂遮罩效果

小结一下:准备工作就分为三步:

  1. 布置画布:通过添加 <canvas> 标签,添加canvas元素
  2. 获取画布:通过 <canvas> 标签的id,获得canvas对象
  3. 获得画笔:通过canvas对象的getContext("2d") 方法,获得2D环境

绘制线段:

在Canvas中,是基于状态的绘制,所以前面几步都是在确定状态,直到最后一步才会具体绘制。

绘画步骤和现实中画画差不多,可以分为四步:

1. 首先移动画笔至绘画的起始位置

context.moveTo(x, y)
// 将笔画移至 x, y 这个位置,canvas中是以画布的左上角为坐标原点,x轴的正方向向右,y轴的正方向向下

image.png

2. 确定第一笔的停止点

context.lineTo(x, y)
// 从上一个笔的停止点,移动至x,y这里

3. 规划好路线后,选择画笔(粗细,颜色,线条等)

因为 Canvas 是基于状态的,所以我们在选择画笔粗细和颜色的同时,其实也是选择了线条的粗细和颜色。

属性说明默认值示例
context.lineWidth线条粗细(宽度) ,单位是像素1.0ctx.lineWidth = 5;
context.strokeStyle描边颜色/样式(可为颜色、渐变、图案)#000000(黑色)ctx.strokeStyle = "#AA394C";``ctx.strokeStyle = "red";``ctx.strokeStyle = gradient;
context.lineCap线段末端样式"butt""butt"(平头) "round"(圆头) "square"(方头)
context.lineJoin两条线相交处的连接样式"miter""miter"(尖角) "round"(圆角) "bevel"(斜角)
context.miterLimit尖角(miter)的最大长度限制(防止过长尖角)10ctx.miterLimit = 5;

4. 进行绘制

确定绘制有两种方法:

context.fill() // 填充
context.stroke() // 描边

5. 画一个线条!

<body>
    <canvas id="myCanvas" style="border: 1px solid black;"></canvas>
    <script>
        const canvas = document.getElementById('myCanvas');
        const ctx = canvas.getContext('2d');
        
        ctx.moveTo(0, 0);
        ctx.lineTo(100, 100);

        ctx.lineWidth = 10;
        ctx.strokeStyle = 'red';

        ctx.stroke();
    </script>
</body>

image.png

一条红色的线条就画好了。但是我们现在只给 Canvas 设置了边框,那它的宽高是从哪来的呢?

这是浏览器默认给 Canvas 分配的尺寸 300x150 像素的画布。如果我们要自己设置 Canvas 宽高的话有两种方法

  1. 通过标签内部设置
 <canvas id="myCanvas" style="border: 1px solid black;" width = "500" height = "500"></canvas>
  1. 通过 JS 设置
const canvas = document.getElementById('myCanvas');

canvas.width = 500;
canvas.height = 500;

这两种方式设置的宽高是等效的

image.png 这里有一个特别需要注意的点:我们设置的是画布的物理宽高,也就是 Canvas 元素的真实宽高,并不是通过 Css 设置的样式宽高,如果我们通过 Css 设置了 Canvas 的宽高,效果其实是在物理宽高的基础上进行了等比的缩小或者拉伸

<canvas id="myCanvas" 
    style="border: 1px solid black; width: 100px; height: 100px;" width="500" height="500">
</canvas>

image.png

线条组成图形

绘制折线

当我们学会绘制线条后,就可以用线条来组成图形了,方法其实很简单,就是复用上文用过的lineTo() 方法即可:

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

    ctx.lineWidth = 10;
    ctx.strokeStyle = 'red';

    ctx.stroke();

image.png

绘制多条折线

如果需要绘制多条不连接的线条,只需在绘制完一次后,再重新移动画笔,重新绘制一次即可

        ctx.moveTo(0, 0);
        ctx.lineTo(50, 100);
        ctx.lineTo(100, 0);
        ctx.lineWidth = 10;
        ctx.strokeStyle = 'red';
        ctx.stroke();

        ctx.moveTo(100, 0);
        ctx.lineTo(150, 100);
        ctx.lineTo(200, 0);
        ctx.lineWidth = 10;
        ctx.strokeStyle = 'blue';
        ctx.stroke(); 

image.png 诶?这是不是很奇怪,明明是先红色再蓝色,为什么就全变为蓝色了呢?是因为上文说过的 Canvas 是基于状态绘制的。我们每次使用stroke() 时,它都会把之前设置的状态再次绘制一遍,第一次stroke时会绘制一条红色折线,第二次stroke时,会再次重新绘制之前那条红色的折线,但是画笔已经变为蓝色的了,所以画出的折线全是蓝色的

为了解决这个问题,我们需要在每次重新绘制时加上beginPath(),这个 API 代表下次绘制的起始处为beginPath()之后的代码。

        ctx.moveTo(0, 0);
        ctx.lineTo(50, 100);
        ctx.lineTo(100, 0);
        ctx.lineWidth = 10;
        ctx.strokeStyle = 'red';
        ctx.stroke();
        
        ctx.beginPath(); // 重新定义起始位置
        ctx.moveTo(100, 0);
        ctx.lineTo(150, 100);
        ctx.lineTo(200, 0);
        ctx.lineWidth = 10;
        ctx.strokeStyle = 'blue';
        ctx.stroke(); 

image.png

绘制矩形

我们先使用绘制线条的方式绘制一个矩形

        // 绘制一个矩形
        ctx.beginPath();
        ctx.moveTo(50,50);
        ctx.lineTo(100,50);
        ctx.lineTo(100,100);
        ctx.lineTo(50,100);
        ctx.lineTo(50,50);

        ctx.lineWidth = 5;
        ctx.strokeStyle = "black";

        ctx.stroke();   

image.png 会发现此时最后一笔画时有一个小缺口,这种情况是因为我们设置了lineWidth导致的,如果是默认笔触为1的话是不会有问题的,但是如果笔触越大,线条越宽,这个小缺口就会越明显。此时需要使用closePath()闭合图形。

        // 绘制一个矩形
        ctx.beginPath();
        ctx.moveTo(50,50);
        ctx.lineTo(100,50);
        ctx.lineTo(100,100);
        ctx.lineTo(50,100);
        // ctx.lineTo(50,50); // 最后一笔可以不画
        ctx.closePath();

image.png

所以我们在绘制图形时需要使用beginPath()closePath()包裹起来。

给矩形上色

上文中有提过绘制的两种方法分别是stroke()fill(),我们需要使用fill()方法给矩形上色。和使用stroke前相同,我们需要先给它设置好属性。

        ctx.beginPath();
        ctx.moveTo(50,50);
        ctx.lineTo(100,50);
        ctx.lineTo(100,100);
        ctx.lineTo(50,100);
        ctx.closePath();

        ctx.lineWidth = 5;
        ctx.strokeStyle = "black";

        ctx.fillStyle = "red";

        ctx.fill();
        ctx.stroke();   

image.png

使用rect()方法绘制矩形

由于矩形是常用的图形,所以 Canvas API 封装好了一个定义矩形位置信息的方法,这个方法接受四个参数,分别表示矩形的起点坐标和宽高。

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

        ctx.beginPath();
        ctx.rect(50,50,100,100);
        
        ctx.lineWidth = 5;
        ctx.strokeStyle = "black";
        ctx.fillStyle = "red";

        ctx.fill();
        ctx.stroke();

image.png

今天 Canvas 的学习就到这啦