canvas 详解 —— 绘制篇

canvas 详解 —— 绘制篇

在我们开始画图之前,我们需要了解一下画布栅格以及坐标空间。通常来说网格中的一个单元相当于 canvas 元素中的一像素。栅格的起点为左上角(坐标为(0,0))。所有元素的位置都相对于原点定位。距离左边(X 轴)x 像素,距离上边(Y 轴)y 像素(坐标为(x,y))。

在这里插入图片描述

矩形图形

有以下三种方法可以绘制矩形:

fillRect

用于绘制一个填充的矩形。

fillRect(x, y, width, height)

  • x:矩形起始点的 x 轴坐标
  • y:矩形起始点的 y 轴坐标
  • width:矩形的宽度,负值的话宽度是绝对值,但是往左移动宽度绝对值像素。
  • height:矩形的高度,负值的话高度是绝对值,但是往上移动高度绝对值的像素。
ctx.fillRect(10, 10, 55, 50);

如下图所示,绘制的矩形左上顶点在 (10, 10),宽高分别是 55 和 50 像素。由于没有设置颜色,默认是黑色。
在这里插入图片描述
下面这个代码片段填充了整个画布。这样做通常的目的是在开始绘制其他内容前设置一个背景。为了达到这样的效果,需要让填充的范围和画布的范围相同,需要获取到canvas元素的widthheight 属性。

ctx.fillRect(0, 0, canvas.width, canvas.height);

在这里插入图片描述

strokeRect

用于绘制一个矩形的边框。

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

strokeRect(x, y, width, height)

  • x:矩形起始点的 x 轴坐标
  • y:矩形起始点的 y 轴坐标
  • width:矩形的宽度。负值的话宽度是绝对值,但是往左移动宽度绝对值像素。
  • height:矩形的高度。负值的话高度是绝对值,但是往上移动高度绝对值的像素。
ctx.strokeRect(55, 10, 55, 50);

在这里插入图片描述
把宽度设置成负数,看看是什么区别

ctx.strokeRect(55, 10, -55, 50);

在这里插入图片描述
可以看到整个矩形往左移动了 55 像素,由于设置的x值为 55,刚好与边界重叠。

clearRect

用于清除指定矩形区域,让清除部分完全透明。这个方法通过把像素设置为透明(rgba(0,0,0,0))以达到擦除一个矩形区域的目的。

clearRect(x, y, width, height)

  • x:矩形起始点的 x 轴坐标
  • y:矩形起始点的 y 轴坐标
  • width:矩形的宽度
  • height:矩形的高度

绘制路径

路径是通过不同颜色和宽度的线段或曲线相连形成的不同形状的点的集合。一个路径,甚至一个子路径,都是闭合的。使用路径绘制图形比直接绘制矩形要多一些额外的步骤:

  1. 首先,需要创建路径起始点。
  2. 然后使用画图方法去画出路径。
  3. 把路径封闭。
  4. 一旦路径生成,就可以通过描边或填充路径区域来渲染图形。

下面是方法总结介绍:

  • beginPath(): 新建一条路径,生成之后,图形绘制命令被指向到路径上生成路径。
  • closePath(): 闭合路径,用于将当前路径的起点和终点连接起来,形成一个封闭的路径。
  • stroke(): 通过线条来绘制图形轮廓。
  • fill(): 通过填充路径的内容区域生成实心的图形。
  • moveTo(x, y): 将起点移动到指定的坐标 x 以及 y 上(默认是在(0, 0))。
  • lineTo(x, y): 绘制一条从当前位置到指定 x 以及 y 位置的直线(通常结合moveTo(x, y)使用,先设置起点在划线)。
  • arc(): 绘制圆弧或者圆.
  • quadraticCurveTo(): 绘制二次贝塞尔曲线
  • bezierCurveTo(): 绘制三次贝塞尔曲线

beginPath

通过清空子路径列表开始一个新路径的方法。 当我们想创建一个新的路径时,调用此方法。

可能下面会涉及到除了beginPath外的方法,但是我们先不用管,那些方法会在下面继续介绍.

ctx.beginPath();
ctx.moveTo(10,10);
ctx.lineTo(10,100);
ctx.stroke();

在这里插入图片描述

当时当我们把beginPath去掉不调用时,发现其实还是成功绘制出来了,那么这个方法到底用来干什么呢?

我们不调用beginPath在画一条红线来看看有什么效果

ctx.moveTo(10,10);
ctx.lineTo(10,100);
ctx.stroke();

ctx.moveTo(20,10);
ctx.lineTo(20,100);
ctx.strokeStyle = "red";
ctx.stroke();

在这里插入图片描述

明明第一条没有设置红色,但是页面上看到两条都是红色的线.

这是因为这两条线都放在同一个路径列表中,而这整一个列表就是我们当前绘制的路径.即使在绘制第一条的线的已经调用了stroke方法将当前路径进行描边,并将描边的结果添加到画布上。如果我们在之前的路径上连续调用 stroke() 方法,那么每次调用都会将之前的路径再次描边,从而导致之前的线条被重复绘制(对于复杂的canvas动画,会因为动画越来越卡)。

而调用beginPath()方法的时候,就会把之前的路径列表清空,并重新开始一条新的路径,而不会影响已经绘制的路径。

调用beginPath方法清除之前的路径列表.

ctx.beginPath();
ctx.moveTo(10,10);
ctx.lineTo(10,100);
ctx.stroke();

ctx.beginPath();
ctx.moveTo(20,10);
ctx.lineTo(20,100);
ctx.strokeStyle = "red";
ctx.stroke();

在这里插入图片描述

closePath

在绘制路径时,通常我们会使用 moveTo() 方法将画笔移动到起始点,然后使用其他绘图方法(如 lineTo()arcTo() 等)定义路径的形状和轮廓。当我们想要将路径闭合时,我们可以使用 closePath() 方法。

ctx.beginPath();
ctx.moveTo(20, 20);
ctx.lineTo(20, 50);
ctx.lineTo(80, 50);
ctx.stroke();

// 使用 closePath()
ctx.beginPath();
ctx.moveTo(20, 70);
ctx.lineTo(20, 100);
ctx.lineTo(80, 100);
ctx.closePath();
ctx.stroke();

在这里插入图片描述

使用了 closePath() 方法的路径是封闭的,起点和终点通过一条直线连接。我们调用了 closePath() 方法来将路径封闭,所以画笔会自动将终点与起点连接起来,形成一个封闭的路径。

是否需要使用 closePath() 方法取决于我们希望绘制的图形是否需要封闭.
如果绘制的图形是封闭的,比如一个闭合的多边形或封闭曲线,那么使用 closePath() 方法可以确保路径的起点和终点连接起来,形成一个封闭的路径。这样,在调用 fill() 方法时,填充颜色会被应用到整个封闭区域内。
如果绘制的图形已经是封闭的,或者使用的绘图方法已经自动将路径封闭(例如使用 arc() 方法绘制圆),则不必显式调用 closePath() 方法。
closePath() 方法的作用仅限于路径的封闭,不会直接影响 fill()stroke() 方法的结果。它只是确保路径是封闭的,从而在填充或描边时产生预期的效果。
closePath()只是闭合当前的路径,从起点到现在的这个点形成一个闭合的回路,但是,这并不意味着他之后的路径就是新路径了,不要企图通过closePath()来开始一条新的路径.

stroke

用于绘制所有moveTo()lineTo()方法定义的路径。默认颜色是黑色.

ctx.beginPath();
ctx.moveTo(10,10);
ctx.lineTo(10,100);
ctx.stroke();

就使用之前的例子来展示了.

在这里插入图片描述

fill

用于填充颜色.

ctx.beginPath();
ctx.moveTo(20, 20);
ctx.lineTo(20, 50);
ctx.lineTo(80, 50);
ctx.fill();

在这里插入图片描述

当你调用 fill() 函数时,所有没有闭合的形状都会自动闭合,所以你不需要调用 closePath() 函数。但是调用 stroke() 时不会自动闭合。

moveTo

用于画笔起点,接受两个参数x,y,设置后画笔起点为(x, y),默认(0, 0).

继续使用之前例子,画了两条线,这两条线的起点都使用moveTo方法更改过.

ctx.beginPath();
ctx.moveTo(10,10);
ctx.lineTo(10,100);
ctx.stroke();

ctx.beginPath();
ctx.moveTo(20,10);
ctx.lineTo(20,100);
ctx.strokeStyle = "red";
ctx.stroke();

在这里插入图片描述

lineTo

绘制一条从当前位置到指定 x 以及 y 位置的直线,该方法有两个参数:x 以及 y,代表坐标系中直线结束的点。开始点和之前的绘制路径有关,之前路径的结束点就是接下来的开始点,开始点也可以通过 moveTo() 函数改变。

ctx.beginPath();
ctx.moveTo(10,10);
ctx.lineTo(10,100);
ctx.stroke();

arc

绘制圆弧路径的方法。圆弧路径的圆心在 (x, y) 位置,半径为 r,根据anticlockwise (默认为顺时针)指定的方向从 startAngle 开始绘制,到 endAngle 结束.

语法:

ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise)
  • x: 圆弧中心(圆心)的 x 轴坐标。
  • y: 圆弧中心(圆心)的 y 轴坐标。
  • radius: 圆弧的半径。
  • startAngle: 圆弧的起始点,x 轴方向开始计算,单位以弧度表示.
  • endAngle: 圆弧的终点,单位以弧度表示。
  • anticlockwise : 可选的Boolean值,如果为 true,逆时针绘制圆弧,反之,顺时针绘制。
ctx.beginPath();
ctx.arc(75, 75, 50, 0, Math.PI, true);
ctx.stroke();

在这里插入图片描述

ctx.beginPath();
ctx.arc(75, 75, 50, 0, Math.PI, true);
ctx.fill();

在这里插入图片描述

arc() 函数中表示角的单位是弧度,不是角度。角度与弧度的 js 表达式: 弧度=(Math.PI/180)*角度。

quadraticCurveTo

绘制二次贝塞尔曲线,cpx,cpy 为一个控制点,x,y 为结束点

语法:

ctx.quadraticCurveTo(cpx, cpy, x, y)
  • cpx: 控制点的 x 轴坐标。
  • cpy: 控制点的 y 轴坐标。
  • x: 终点的 x 轴坐标。
  • y: 终点的 y 轴坐标。
ctx.beginPath();
ctx.moveTo(20, 10);
ctx.quadraticCurveTo(30, 50, 50, 20);
ctx.stroke();

在这里插入图片描述

本文不讲解贝塞尔曲线相关的知识,如果大家想了解更多关于贝塞尔曲线的知识,可以查看wiki上关于贝塞尔曲线的介绍或者自行查看其他相关文档.

bezierCurveTo

绘制三次贝赛尔曲线路径的方法。该方法需要三个点。第一、第二个点是控制点,第三个点是结束点。起始点是当前路径的最后一个点,绘制贝赛尔曲线前,可以通过调用 moveTo() 进行修改。

语法:

ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
  • cp1x: 第一个控制点的 x 轴坐标。
  • cp1y: 第一个控制点的 y 轴坐标。
  • cp2x: 第二个控制点的 x 轴坐标。
  • cp2y: 第二个控制点的 y 轴坐标。
  • x: 结束点的 x 轴坐标。
  • y: 结束点的 y 轴坐标。

在这里插入图片描述

绘制文本

除了图形外,还可以绘制文本.有两种方法可以渲染文本:

  • fillText: 在指定的 (x,y) 位置填充指定的文本,绘制的最大宽度是可选的。
  • strokeText: 在指定的 (x,y) 位置绘制文本边框,绘制的最大宽度是可选的`

fillText

语法:

ctx.fillText(text, x, y, [maxWidth]);
  • text: 使用当前的 font, textAlign, textBaselinedirection 值对文本进行渲染。
  • x: 文本起点的 x 轴坐标。
  • y: 文本起点的 y 轴坐标。
  • maxWidth: 可选,绘制的最大宽度。如果指定了值,并且经过计算字符串的值比最大宽度还要宽,字体为了适应会水平缩放(如果通过水平缩放当前字体,可以进行有效的或者合理可读的处理)或者使用小号的字体。
ctx.fillText("Hello world", 30, 50);

在这里插入图片描述

绘制的最大宽度小于文本的宽度,文本的进行了水平缩放.

ctx.fillText("Hello world", 30, 50, 20);

在这里插入图片描述

strokeText

绘制文字轮廓.

  • text: 使用当前的 font, textAlign, textBaselinedirection 值对文本进行渲染。
  • x: 文本起点的 x 轴坐标。
  • y: 文本起点的 y 轴坐标。
  • maxWidth: 可选,绘制的最大宽度。如果指定了值,并且经过计算字符串的值比最大宽度还要宽,字体为了适应会水平缩放(如果通过水平缩放当前字体,可以进行有效的或者合理可读的处理)或者使用小号的字体。

为了能更清晰的看到是文字轮廓,设置了font属性加大字体.

ctx.font = "bold 20px serif";
ctx.strokeText("Hello world", 20, 90);

在这里插入图片描述

绘制图片

canvas 还具有图像操作能力。可以用于动态的图像合成或者作为图形的背景,以及游戏界面等等。浏览器支持的任意格式的外部图片都可以使用,比如 PNGGIF 或者 JPEG。甚至可以将同一个页面中其他 canvas 元素生成的图片作为图片源。

引入图像到 canvas 里需要以下两步基本操作:

  1. 使用特定html元素对象或者另一个 canvas 元素的引用作为源,也可以通过提供一个 URL 的方式来使用图片
  2. 使用 drawImage() 函数将图片绘制到画布上

可以使用的html的元素对象:

  • HTMLImageElement: 由 Image() 函数构造出来的,或者任何的 <img> 元素
  • HTMLVideoElement: 从video标签的视频中抓取当前帧作为一个图像
  • HTMLCanvasElement: 使用另一个 <canvas> 元素作为你的图片源。
  • ImageBitmap: 这是一个高性能的位图,可以低延迟地绘制,它可以从上述的所有源以及其他几种源中生成(通过createImageBitmap方法创建)。

我们下面将一个个展示:

使用HTMLImageElement作为图片源

const imageObj = new Image();
imageObj.onload = function() {
  ctx.drawImage(imageObj, 0, 0);
};
imageObj.src = 'http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg';

在这里插入图片描述

使用 data:url 方式嵌入图像
imageObj.src = 'data:image/xxxxxxxxxxx';

使用HTMLVideoElement作为图片源

添加一个video标签.

<video src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4" controls width="200px" height="200px"></video>
const video = document.getElementsByTagName('video')[0]
video.onplay = function() {
  ctx.drawImage(video, 0, 0);
}

在这里插入图片描述

使用HTMLCanvasElement作为图片源

添加多一个canvas并添加一些线条作为图片源.

<canvas width="150" height="150" id="source"></canvas>
const source = document.getElementById("source");
const sourceCtx = source.getContext("2d");
sourceCtx.beginPath();
sourceCtx.moveTo(10, 10);
sourceCtx.lineTo(10, 100);
sourceCtx.stroke();

sourceCtx.beginPath();
sourceCtx.moveTo(20, 10);
sourceCtx.lineTo(20, 100);
sourceCtx.strokeStyle = "red";
sourceCtx.stroke();

const canvas = document.getElementById("demo");
const ctx = canvas.getContext("2d");
ctx.drawImage(source, 0, 0);

在这里插入图片描述

使用ImageBitmap作为图片源

使用createImageBitmap加载雪碧图并从中提取出两个小图渲染到画布上.

const image = new Image();

image.onload = () => {
  Promise.all([
    createImageBitmap(image, 0, 0, 80, 80),
    createImageBitmap(image, 32, 0, 80, 80),
  ]).then((sprites) => {
    ctx.drawImage(sprites[0], 0, 0);
    ctx.drawImage(sprites[1], 32, 32);
  });
};

image.src = "sprites.png";

在这里插入图片描述

drawImage

前面都使用到了drawImage这个方法去渲染,但是并未讲解,现在就让我们来看看具体是怎么用的.

语法:

ctx.drawImage(image, x, y)
ctx.drawImage(image, x, y, width, height)
ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)

通过不同的传参,渲染结果也不一样:

drawImage(image, x, y)

这种方式是最容易理解的,image表示图片源(前面有详细介绍过),xy 是其在目标 canvas 里的起始坐标.

SVG 图像必须在 <svg> 根指定元素的宽度和高度。

比如说前面这个例子:

在这里插入图片描述

当我们把xy设置成其他的看看效果.

ctx.drawImage(source, -10, -10);

在这里插入图片描述

可以看到现在图片源往左和上都移动了10像素,起始点在原点了.

ctx.drawImage(image, x, y, width, height)

用于缩放图片,与第一种的区别就是增加了两个用于控制图像在 canvas 中缩放的参数

widthheight都设置成75,为原来的1/2.

ctx.drawImage(source, 0, 0, 75, 75);

在这里插入图片描述

图像可能会因为大幅度的缩放而变得起杂点或者模糊。如果您的图像里面有文字,那么最好还是不要进行缩放,因为那样处理之后很可能图像里的文字就会变得无法辨认了。

ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)

最后一个有8个新参数,用于控制做切片显示的.

除了第一个参数跟之前两个方法的一样都是图片源以外,剩余的都没关联,后8个的新参数的前4个用来定义图像源的切片位置和大小,后 4 个则是定义切片的目标显示位置和大小。

切片是个做图像合成的强大工具。假设有一张包含了所有元素的图像,那么你可以用这个方法来合成一个完整图像。例如,我们想画一张图表,而手上有一个包含所有必需的文字的 PNG 文件,那么可以很轻易的根据实际数据的需要来改变最终显示的图表。这方法的另一个好处就是你不需要单独装载每一个图像。

ctx.drawImage(source, 10, 10, 20, 100, 50, 50, 20, 50)

在这个例子中,从(10, 10)的地方开始切割出宽20高100的图片源,并放在目标canvas画布(50, 50)的地方,并再次切成宽20高50.

在这里插入图片描述

控制图像的缩放行为

前面提到了,过度缩放图像可能会导致图像模糊或像素化,可以通过使用绘图环境的imageSmoothingEnabled属性来控制是否在缩放图像时使用平滑算法。默认值为true,即启用平滑缩放。您也可以像这样禁用此功能:

ctx.mozImageSmoothingEnabled = false;
ctx.webkitImageSmoothingEnabled = false;
ctx.msImageSmoothingEnabled = false;
ctx.imageSmoothingEnabled = false;
  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CanvasHTML5新增的一个用于绘制图形的标签,可以通过JavaScript来控制Canvas进行图形的绘制。在进行Canvas绘制时,可以通过调整画布的位置、大小和旋转角度等属性,来控制绘制出来的图形的位置和形态。 下面我们就来介绍一下Canvas画布的移动、缩放和旋转: 1. 画布移动 画布移动可以通过Canvas提供的translate方法来实现。translate方法接收两个参数,分别表示x轴和y轴方向上的偏移量。偏移量为正值表示向右或向下移动,为负值表示向左或向上移动。 例如,我们可以通过下面的代码将画布向右移动50个像素,向下移动100个像素: ``` context.translate(50, 100); ``` 2. 画布缩放 画布缩放可以通过Canvas提供的scale方法来实现。scale方法接收两个参数,分别表示x轴和y轴方向上的缩放比例。缩放比例为大于1的值表示放大,小于1的值表示缩小。 例如,我们可以通过下面的代码将画布在x轴和y轴方向上都放大2倍: ``` context.scale(2, 2); ``` 3. 画布旋转 画布旋转可以通过Canvas提供的rotate方法来实现。rotate方法接收一个参数,表示旋转的角度,单位为弧度。 例如,我们可以通过下面的代码将画布旋转45度: ``` context.rotate(Math.PI / 4); ``` 需要注意的是,Canvas绘图的坐标系原点默认在画布的左上角,而移动、缩放和旋转操作都是相对于原点进行的。因此,在进行这些操作时,需要先将画布的原点移动到需要的位置,再进行操作。例如,如果需要将画布向右移动50个像素,需要先将原点移动到(50,0)的位置,再进行移动操作: ``` context.translate(50, 0); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值