Canvas学习笔记(一)

什么是 Canvas

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

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中是透明的,不可见的。


绘制前的准备

获取 Canvas 对象:
js 复制代码
const canvas = document.getElementById('canvas');
获取画笔
js 复制代码
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-p3 srgb
desynchronized 是否将画布绘制周期与事件循环解耦以减少延迟 false
willReadFrequently 是否频繁读取像素(用于优化getImageData()性能) false
  • webgl
属性 作用 说明
alpha 是否包含alpha通道 和2D一样
antialias 是否开启抗锯齿 让图形边缘更平滑
depth 是否包含深度缓冲区 用于3D场景的深度检测
failIfMajorPerformanceCaveat 性能低时是否创建上下文 true:性能差时失败
powerPreference GPU电源偏好 default/high-performance/low-power
premultipliedAlpha 是否预混合alpha 用于图像合成
preserveDrawingBuffer 是否保存缓冲区 true:可以多次读取
stencil 是否包含模版缓冲区 用于复杂遮罩效果

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

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

绘制线段:

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

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

1. 首先移动画笔至绘画的起始位置
js 复制代码
context.moveTo(x, y)
// 将笔画移至 x, y 这个位置,canvas中是以画布的左上角为坐标原点,x轴的正方向向右,y轴的正方向向下
2. 确定第一笔的停止点
js 复制代码
context.lineTo(x, y)
// 从上一个笔的停止点,移动至x,y这里
3. 规划好路线后,选择画笔(粗细,颜色,线条等)

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

属性 说明 默认值 示例
context.lineWidth 线条粗细(宽度) ,单位是像素 1.0 ctx.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)的最大长度限制(防止过长尖角) 10 ctx.miterLimit = 5;
4. 进行绘制

确定绘制有两种方法:

js 复制代码
context.fill() // 填充
context.stroke() // 描边
5. 画一个线条!
js 复制代码
<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>

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

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

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

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

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

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

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

线条组成图形

绘制折线

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

js 复制代码
    ctx.moveTo(0, 0);
    ctx.lineTo(50, 100);
    ctx.lineTo(100, 0);

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

    ctx.stroke();
绘制多条折线

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

js 复制代码
        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(); 

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

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

js 复制代码
        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(); 

绘制矩形

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

js 复制代码
        // 绘制一个矩形
        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();   

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

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

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

给矩形上色

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

js 复制代码
        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();   
使用rect()方法绘制矩形

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

js 复制代码
        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();

今天 Canvas 的学习就到这啦

相关推荐
我有一棵树2 小时前
React 中 useRef 和 useState 的使用场景区别
前端·javascript·react.js
喵个咪2 小时前
Qt6 QML 实现DateTimePicker组件
前端·qt
yinuo2 小时前
CSS奇技淫巧:用你意想不到的4种属性实现裁剪遮罩效果
前端
晓翔仔2 小时前
网络安全之Web入侵场景
前端·安全·web安全·网络安全·信息安全
想努力找到前端实习的呆呆鸟2 小时前
Uniapp如何下载图片到本地相册
前端·vue.js·微信小程序
fmk10232 小时前
Vue中的provide与inject
前端·javascript·vue.js
S***H2832 小时前
前端性能监控实践,用户体验优化心得
前端·ux
5***79002 小时前
前端解决方案不仅仅是关于网页设计和布局,它涉及到用户体验、性能优化、跨平台兼容性以及安全性等多个方面。以下是一些前端解决方案的关键要素:
前端·ux
Wect2 小时前
学习React-DnD:实现多任务项拖动-维护多任务项数组
前端·react.js