html5 Canvas 绘制基本图形 从直线图形到使用路径 - 直线、矩形、路径、多边形、复杂组合图形

简介: html5 Canvas 绘制基本图形 从直线图形到使用路径 - 直线、矩形、路径、多边形、复杂组合图形

html5: Canvas 绘制基本图形
从绘制直线 到 路径


1. Canvas 相关概念

1.1 什么是Canvas

Canvas是 HTML5 的 2D图形技术之一,是一门纯JavaScript操作的图形技术。另一个是2D图形技术为 SVG。这两者的区别在于:

Canvas SVG
Canvas是基于位图的,放大图形会导致失真,适用于像素处理和动态渲染 SVG是基于矢量图的,放大图形不会导致失真,但不适用于像素处理和适合静态描述
Canvas是使用JavaScript动态生成的 SVG是使用XML静态描述的
若发生修改,使用Canvas需要重绘 使用SVG需要重绘

1.2 Canvas坐标系

Canvas使用的坐标系是 W3C坐标系 这与数学中的直角坐标系中的区别在于,数学直角坐标系y轴正方向向上,而W3C坐标系中的y轴正方向向下:

1.3 Canvas元素

1.3.1 基本用法

Canvas 使用一个通过JavaScript 和 HTML的<canvas>元素来绘制图形的方式,以下是 HTML 中的一个Canvas元素:

<canvas id="canvas" width="300" height="200"></canvas>

要操作Canvas绘图首先要获取 HTML <canvas> 元素的引用,然后获取这个元素的context,需要依次使用以下方法:

方法 描述
Document.getElementById() 取HTML 元素的引用
getContext() 获取这个元素的context,图像稍后将在此被渲染。

如获取上面id属性值为 canvas的上下文对象(context):

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');

在 HTML 中,你需要指定canvas元素的宽度和高度作为你的画布大小,宽度和高度都为纯数字。

后文默认使用变量名 cts 表示一个已经获取的Canvas上下文对象(CanvasRenderingContext2D)

1.3.2 在 TypeScript 中的用法

在TypeScript中你需要指定类型。在这里,getElementById()方法返回的类型应该被断言为 HTMLCanvasElement 而非直接断言成普通的 HTMLElement

HTMLCanvasElement 接口提供用于操作元素布局和表示的属性和方法。HTMLCanvasElement 接口还继承了HTMLElement 接口的属性和方法。

<canvas id="canvas" width="300" height="200" ></canvas>
<script lang="ts">
  let canvas = document.getElementById("canvas") as HTMLCanvasElement;
  let ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
</script>

2. Canvas 绘制直线

2.1 直线与路径:从绘制基本线段开始

不同于 SVG, 只支持两种形式的图形绘制:矩形路径(由一系列点连成的直线段)。所有其他类型的图形都是通过一条或者多条路径组合而成的。不过,我们拥有众多路径生成的方法让复杂图形的绘制成为了可能。

我们可以使用以下两个方法来配合绘制直线:

方法 描述 说明
moveTo(x, y) 将一个新的子路径的起始点移动到(x,y)坐标 -
lineTO(x, y) 使用直线连接子路径的终点到x,y坐标的方法 并不会真正地绘制
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(50,50);
ctx.lineTo(200, 50);
ctx.stroke();

2.1.1 CanvasRenderingContext2D 对象的 moveTo() 方法

将一个 新的子路径的起始点 移动到(x,y)坐标的方法。

语法
void ctx.moveTo(x, y);

2.1.2 CanvasRenderingContext2D 对象的 lineTo() 方法

使用直线连接子路径的终点到x,y坐标的方法(并不会真正地绘制)

语法
void ctx.lineTo(x, y);
参数
参数 描述
x 点的 x 轴。
y 点的 y 轴。

2.1.3 CanvasRenderingContext2D 对象的 stroke() 方法

使用非零环绕规则,根据当前的画线样式,绘制当前或已经存在的路径的方法。

语法
void ctx.stroke();
void ctx.stroke(path);
参数
参数 描述 说明
path 绘制Path2D的路径 Path2D对象 用于声明路径

2.2. Canvas 绘制矩形

2.2.1 通过绘制直线的方法绘制矩形

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

效果如下:

2.2.2 通过 CanvasRenderingContext2D 对象的矩形方法

2.2.2.1 rect(x, y, width, height) 方法

创建矩形路径的方法,矩形的起点位置是 (x, y) ,尺寸为 width 和 height。矩形的4个点通过直线连接,子路径做为闭合的标记,所以你可以填充或者描边矩形。

语法
void ctx.rect(x, y, width, height);
例如
ctx.beginPath();
ctx.rect(10, 30, 200, 100);
ctx.fill();

效果如下:


2.2.2.2 fillRect(x, y, width, height)

CanvasRenderingContext2D.fillRect() 函数是Canvas 2D API 绘制填充矩形的方法。当前渲染上下文中的fillStyle 属性决定了对这个矩形对的填充样式。

这个方法是直接在画布上绘制填充,并不修改当前路径,所以在这个方法后面调用 fill() 或者stroke()方法并不会对这个方法有什么影响。

语法
void ctx.fillRect(x, y, width, height);

参数:

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

fillRect()方法绘制一个填充了内容的矩形,这个矩形的开始点(左上点)在(x, y) ,它的宽度和高度分别由widthheight 确定,填充样式由当前的fillStyle 决定。

例如
ctx.fillStyle = 'yellow';
ctx.fillRect(60, 20, 200, 100);

效果如下:


2.2.2.3 strokeRect(x, y, width, height)

该方法使用当前的绘画样式,描绘一个起点在 (x, y) 、宽度为 w 、高度为 h 的矩形的方法。

语法
void ctx.strokeRect(x, y, width, height);
例如
ctx.shadowColor = '#5F9CFA';
ctx.shadowBlur = 30;
ctx.lineJoin = 'bevel';
ctx.lineWidth = 15;
ctx.strokeStyle = '#5FFA7A';
ctx.strokeRect(30, 30, 90, 90);

效果如下:


2.2.2.4 clearRect(x, y, width, height)

该方法通过把像素设置为透明以达到擦除一个矩形区域的目的。

语法
void ctx.clearRect(x, y, width, height);
参数 描述
x 矩形起点的 x 轴坐标。
y 矩形起点的 y 轴坐标。
width 矩形的宽度。
height 矩形的高度。
例如

除整个画布:

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

清除画布一个区域显现出矩形:

// 绘制背景色
ctx.beginPath();
ctx.fillStyle = '#eca601';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 绘制矩形清除区域
ctx.clearRect(10, 10, 120, 100);

效果如下:

3 Canvas 路径

3.1 什么是 Canvas 路径

在Canvas中除了下一节中的矩形提供了直接绘制方法,其它的所有Canvas基本图形,包括上一小节中的直线,以及圆形、弧线、贝塞尔曲线,都是以路径为基础进行绘制的。

3.2 操作 Canvas 路径的方法

1. 操作路径

方法 描述 说明
beginPath() 清空子路径列表开始一个新的路径。 当你想创建一个新的路径时,调用此方法。
closePath() 使笔点返回到当前子路径的起始点。它尝试从当前点到起始点绘制一条直线。 如果图形已经是封闭的或者只有一个点,那么此方法不会做任何操作。
moveTo() 将一个新的子路径的起始点移动到(x,y)坐标。 参见 2.1.1 节
lineTo() 使用直线连接子路径的最后的点到x,y坐标。 参见 2.1.2 节
bezierCurveTo() 添加一个3次贝赛尔曲线路径。该方法需要三个点。 第一、第二个点是控制点,第三个点是结束点。起始点是当前路径的最后一个点,绘制贝赛尔曲线前,可以通过调用 moveTo() 进行修改。
quadraticCurveTo() 添加一个2次贝赛尔曲线路径。
arc() 绘制一段圆弧路径, 圆弧路径的圆心在 (x, y) 位置,半径为 r ,根据anticlockwise (默认为顺时针)指定的方向从 startAngle 开始绘制,到 endAngle 结束。
arcTo() 根据控制点和半径绘制圆弧路径,使用当前的描点(前一个moveTo或lineTo等函数的止点)。根据当前描点与给定的控制点1连接的直线,和控制点1与控制点2连接的直线,作为使用指定半径的圆的切线,画出两条切线之间的弧线路径。
ellipse() 添加一个椭圆路径,椭圆的圆心在(x,y)位置,半径分别是radiusX 和 radiusY ,按照anticlockwise (默认顺时针)指定的方向,从 startAngle 开始绘制,到 endAngle 结束。 实验中的功能
rect() 创建一个矩形路径,矩形的起点位置是 (x, y) ,尺寸为 width 和 height。
stroke() 使用非零环绕规则,根据当前的画线样式,绘制当前或已经存在的路径的方法。 参见 2.1.3节

2. 绘制路径

方法 描述 说明
fill() 使用当前的样式填充子路径。
stroke() 使用当前的样式描边子路径。
drawFocusIfNeeded() 如果给定的元素获取了焦点,那么此方法会在当前的路径绘制一个焦点。
scrollPathIntoView() 将当前或给定的路径滚动到窗口。
clip() 从当前路径创建一个剪切路径。在 clip() 调用之后,绘制的所有信息只会出现在剪切路径内部。
isPointInPath() 判断当前路径是否包含检测点。
isPointInStroke() 判断检测点是否在路径的描边线上。
3.2.1 beginPath() 方法

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

语法
void ctx.beginPath();
例如
// 第一条路径
ctx.beginPath();
ctx.strokeStyle = 'blue';
ctx.moveTo(30,30);
ctx.lineTo(0,200);
ctx.stroke();
// 第二条路径
ctx.beginPath();
ctx.strokeStyle = '#FE5B5C';
ctx.moveTo(30,30);
ctx.lineTo(120,120);
ctx.stroke();

效果如下:

效果图(其中边框是为了展示canvas画布区域有意添加上去的)

3.2.2 closePath() 方法

该方法 将笔点返回到当前子路径起始点 。它尝试从当前点到起始点绘制一条直线。 如果图形已经是封闭的或者只有一个点,那么此方法不会做任何操作。

语法
void ctx.closePath();
例子
不使用该方法时:
ctx.beginPath();
ctx.strokeStyle = 'blue';
ctx.moveTo(0,0);
ctx.lineTo(200,20);
ctx.lineTo(120,120);
ctx.stroke();

效果如下:

效果图(其中边框是为了展示canvas画布区域有意添加上去的)

使用该方法返回到当前子路径起始点后:
ctx.beginPath();
ctx.strokeStyle = 'blue';
ctx.moveTo(0,0);
ctx.lineTo(200,20);
ctx.lineTo(120,120);
ctx.closePath(); // 绘制三角形的最后一条线
ctx.stroke();

效果如下:

效果图(其中边框是为了展示canvas画布区域有意添加上去的)

3.2.3 bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) 方法

该方法用于绘制三次贝赛尔曲线路径。

贝塞尔曲线它是依据四个位置任意的点坐标绘制出的一条光滑曲线。它通过控制 曲线上的四个点起始点终止点 以及 两个相互分离的中间点 )来创造、编辑图形。

其中起重要作用的是位于曲线中央的控制线。这条线是虚拟的,中间与贝塞尔曲线交叉,两端是控制端点。移动两端的端点时贝塞尔曲线改变曲线的曲率(弯曲的程度);移动中间点(也就是移动虚拟的控制线)时,贝塞尔曲线在起始点和终止点锁定的情况下做均匀移动。

语法
void ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
参数 描述
cp1x 第一个控制点的 x 轴坐标。
cp1y 第一个控制点的 y 轴坐标。
cp2x 第二个控制点的 x 轴坐标。
cp2y 第二个控制点的 y 轴坐标。
x 结束点的 x 轴坐标。
y 结束点的 y 轴坐标。

例如:

ctx.beginPath();
ctx.moveTo(20,20);
ctx.bezierCurveTo(230, 30, 150, 60, 50, 100);
ctx.stroke();
// 绘制相关点
ctx.fillStyle = 'blue';
ctx.fillRect(20, 20, 10, 10);// 起点
ctx.fillRect(50, 100, 10, 10);// 终点
ctx.fillStyle = 'red';
ctx.fillRect(230, 30, 10, 10);// 控制点1
ctx.fillRect(150, 70, 10, 10);// 控制点2

效果如下:

3.2.4 quadraticCurveTo(cpx, cpy, x, y) 方法

该方法是 Canvas 2D API 新增 二次贝塞尔曲线路径 的方法。它需要2个点。 第一个点是控制点,第二个点是终点起始点当前路径最新的点,当创建二次贝赛尔曲线之前,可以使用 moveTo() 方法进行改变。

语法
void ctx.quadraticCurveTo(cpx, cpy, x, y);
参数 描述
cpx 控制点的 x 轴坐标。
cpy 控制点的 y 轴坐标。
x 终点的 x 轴坐标。
y 终点的 y 轴坐标。
例如
ctx.beginPath();
ctx.moveTo(50, 20);
ctx.quadraticCurveTo(230, 30, 50, 100);
ctx.stroke();
ctx.fillStyle = 'blue';
ctx.beginPath();
ctx.arc(50, 20, 5, 0, 2 * Math.PI);   // 起始点
ctx.arc(50, 100, 5, 0, 2 * Math.PI);  // 终点
ctx.fill();
ctx.fillStyle = 'red';
ctx.beginPath();
ctx.arc(230, 30, 5, 0, 2 * Math.PI); // 控制点
ctx.fill();

效果如下:

3.2.5 arc(x, y, radius, startAngle, endAngle, anticlockwise) 方法

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

语法
void ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);
参数 描述
x 圆弧中心(圆心)的 x 轴坐标。
y 圆弧中心(圆心)的 y 轴坐标。
radius 圆弧的半径。
startAngle 圆弧的起始点, x轴方向开始计算,单位以弧度表示。
endAngle 圆弧的终点, 单位以弧度表示。
anticlockwise 可选的Boolean值 ,如果为 true,逆时针绘制圆弧,反之,顺时针绘制。

例如:

ctx.beginPath();
ctx.arc(50, 50, 50, 0, 2 * Math.PI);
ctx.stroke();

效果如下:

3.2.6 arcTo(x1, y1, x2, y2, radius) 方法

该方法根据控制点和半径绘制圆弧路径,使用当前的描点(前一个moveTo或lineTo等函数的止点)。根据当前描点与给定的控制点1连接的直线,和控制点1与控制点2连接的直线,作为使用指定半径的圆的切线,画出两条切线之间的弧线路径。

语法
void ctx.arcTo(x1, y1, x2, y2, radius);
参数 描述
x1 第一个控制点的 x 轴坐标。
y1 第一个控制点的 y 轴坐标。
x2 第二个控制点的 x 轴坐标。
y2 第二个控制点的 y 轴坐标。
radius 圆弧的半径。
例如
ctx.setLineDash([])
ctx.beginPath();
ctx.moveTo(150, 20);
ctx.arcTo(150,100,50,20,30);
ctx.stroke();
ctx.fillStyle = 'blue';
ctx.fillRect(150, 20, 10, 10);  // 基本点
ctx.fillStyle = 'red';
ctx.fillRect(150, 100, 10, 10); // 控制点1
ctx.fillRect(50, 20, 10, 10);  // 控制点2
ctx.setLineDash([5,5])
ctx.moveTo(150, 20);
ctx.lineTo(150,100);
ctx.lineTo(50, 20);
ctx.stroke();
ctx.beginPath();
ctx.arc(120,38,30,0,2*Math.PI);
ctx.stroke();

效果如下:

3.2.7 ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise) 方法
语法
void ctx.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise);
参数 描述
x 椭圆圆心的 x 轴坐标。
y 椭圆圆心的 y 轴坐标。
radiusX 椭圆长轴的半径。
radiusY 椭圆短轴的半径。
rotation 椭圆的旋转角度,以弧度表示(非角度度数)。
startAngle 将要绘制的起始点角度,从 x 轴测量,以弧度表示(非角度度数)。
endAngle 椭圆将要绘制的结束点角度,以弧度表示(非角度度数)。
anticlockwise 可选的 Boolean 选项,如果为 true,逆时针方向绘制椭圆 (逆时针), 反之顺时针方向绘制。
3.2.8 fill() 方法

该方法根据当前的填充样式,填充当前或已存在的路径的方法。采取非零环绕或者奇偶环绕规则。

语法
void ctx.fill();
void ctx.fill(fillRule);
void ctx.fill(path, fillRule);
参数
参数 描述 说明
fillRule 一种算法,决定点是在路径内还是在路径外。 允许的值:
"nonzero": 非零环绕规则, 默认的规则。
"evenodd": 奇偶环绕规则。
path 需要填充的Path2D 路径。 -
例子
ctx.beginPath();
ctx.strokeStyle = 'blue';
ctx.moveTo(0,0);
ctx.lineTo(200,90);
ctx.lineTo(120,120);
ctx.closePath();
ctx.stroke();
ctx.fill();

效果如下:

3.2.10 drawFocusIfNeeded() 方法

该方法用来给 当前路径特定路径 绘制焦点的方法,如果给定的元素获取了焦点。

语法
void ctx.drawFocusIfNeeded(element);
void ctx.drawFocusIfNeeded(path, element);
参数 描述
element 是否需要设置焦点的元素。
path Path2D 路径。
例如
<canvas id="canvas" width="300" height="200" >
  <input id="button" type="range" min="1" max="12">
</canvas>
<script lang="ts">
let canvas = document.getElementById("canvas") as HTMLCanvasElement;
let ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
let button = document.getElementById("button") as HTMLElement ;
</script>

效果如下:

3.2.11 scrollPathIntoView() 方法

该方法将当前或给定的路径滚动到窗口的方法。类似于 Element.scrollIntoView()

语法
void ctx.scrollPathIntoView();
void ctx.scrollPathIntoView(path);
参数 描述
path 使用的Path2D 路径。
例如
ctx.beginPath();
ctx.fillRect(10, 10, 30, 30);
ctx.scrollPathIntoView();
3.2.12 clip() 方法
语法
void ctx.clip();
void ctx.clip(fillRule);
void ctx.clip(path, fillRule);
参数 描述 说明
fillRule 这个算法判断一个点是在路径内还是在路径外。 允许的值:
“nonzero”: 非零环绕原则,默认的原则。
“evenodd”: 奇偶环绕原则。
path 需要剪切的 Path2D 路径。 -
例如
ctx.beginPath();
ctx.arc(100, 100, 50, 0, Math.PI*2, false);
ctx.clip();
ctx.fillRect(0, 0, 100,100);

效果如下:

3.2.13 isPointInPath() 方法
语法
boolean ctx.isPointInPath(x, y);
boolean ctx.isPointInPath(x, y, fillRule);
boolean ctx.isPointInPath(path, x, y);
boolean ctx.isPointInPath(path, x, y, fillRule);
参数 描述 说明
x 检测点的X坐标
y 检测点的Y坐标
fillRule 用来决定点在路径内还是在路径外的算法。 允许的值:
“nonzero”: 非零环绕规则 ,默认的规则。
“evenodd”: 奇偶环绕原则 。
path Path2D应用的路径。
返回值

一个Boolean值:

返回值 描述
true 当检测点包含在当前或指定的路径内时
false 没有检测到时
例子
ctx.rect(50, 50, 50, 50);
ctx.stroke();
let _ = ctx.isPointInPath(10, 10)
console.log(_); // true

控制台输出为:true

效果如下:

3.2.14 isPointInStroke() 方法
语法
boolean ctx.isPointInStroke(x, y);
boolean ctx.isPointInStroke(path, x, y);
参数 描述
x 检测点的X坐标。
y 检测点的Y坐标。
path Path2D 路径。
返回值

一个Boolean值:

返回值 描述
true 当这个点在路径的描边线上时
false 不在路径的描边线上时
例子
ctx.rect(50, 50, 50, 50);
ctx.stroke();
let _ = ctx.isPointInStroke(10, 10)
console.log(_); // false

控制台输出为:false

效果如下:


4. Canvas实战:多边形与组合图形的绘制

4.1 绘制五边形

ctx.beginPath();
ctx.lineWidth = 5;
ctx.strokeStyle = 'blue';
ctx.moveTo(100, 0); 
ctx.lineTo(195, 69);
ctx.lineTo(159, 181);
ctx.lineTo(41, 181);
ctx.lineTo(5, 69);
ctx.closePath();
ctx.stroke();

效果如下:

4.2 绘制一个五角星

ctx.beginPath();
ctx.lineWidth = 5;
ctx.strokeStyle = 'blue';
ctx.beginPath(); 
ctx.moveTo(100, 0);
ctx.lineTo(159, 181);
ctx.lineTo(5, 69);
ctx.lineTo(195, 69);
ctx.lineTo(41, 181); 
ctx.closePath();
ctx.stroke();

效果如下:

4.3 绘制房屋

// 线条宽度
ctx.lineWidth = 5;
ctx.beginPath();
// 绘制屋顶
ctx.moveTo(50, 95);
ctx.lineTo(150, 20);
ctx.lineTo(250, 95);
ctx.closePath();
ctx.stroke();
ctx.strokeRect(100, 95, 100, 100); // 绘制墙
ctx.fillRect(130, 135, 40, 60);   // 绘制门

效果如下:

目录
相关文章
|
2月前
|
前端开发 JavaScript API
2024 新年HTML5+Canvas制作3D烟花特效(附源码)
2024 新年HTML5+Canvas制作3D烟花特效(附源码)
50 0
|
2月前
|
存储 移动开发 前端开发
HTML新特性【HTML5内联SVG、SVG_矩形、SVG 与 Canvas两者间的区别 、HTML5_MathML 】(三)-全面详解(学习总结---从入门到深化)
HTML新特性【HTML5内联SVG、SVG_矩形、SVG 与 Canvas两者间的区别 、HTML5_MathML 】(三)-全面详解(学习总结---从入门到深化)
48 0
|
1月前
|
前端开发 JavaScript 容器
编程笔记 html5&css&js 032 HTML Canvas
编程笔记 html5&css&js 032 HTML Canvas
|
3月前
|
存储 移动开发 前端开发
HTML新特性【HTML5内联SVG、SVG_矩形、SVG 与 Canvas两者间的区别 、HTML5_MathML 】(三)-全面详解(学习总结---从入门到深化)(下)
HTML新特性【HTML5内联SVG、SVG_矩形、SVG 与 Canvas两者间的区别 、HTML5_MathML 】(三)-全面详解(学习总结---从入门到深化)
29 0
|
15天前
|
前端开发 JavaScript 开发工具
【HTML/CSS】入门导学篇
【HTML/CSS】入门导学篇
23 0
|
5天前
|
数据采集 前端开发 网络协议
如何使用代理IP通过HTML和CSS采集数据
如何使用代理IP通过HTML和CSS采集数据
|
10天前
|
前端开发 搜索推荐 数据安全/隐私保护
HTML标签详解 HTML5+CSS3+移动web 前端开发入门笔记(四)
HTML标签详解 HTML5+CSS3+移动web 前端开发入门笔记(四)
18 1
|
10天前
|
PHP
web简易开发——通过php与HTML+css+mysql实现用户的登录,注册
web简易开发——通过php与HTML+css+mysql实现用户的登录,注册
|
16天前
|
JSON JavaScript 前端开发
js是什么、html、css
js是什么、html、css
|
18天前
|
XML 前端开发 JavaScript
css和html
【4月更文挑战第7天】css和html
13 0