简单来说,<canvas>
是HTML5中的标签,它是一个容器,可以使用JS在其中绘制图形或文字。
一、介绍
- canvas 是 HTML5 新增的一个标签,表示画布
-
<canvas></canvas>
- canvas 也是 HTML5 的画布技术,可以通过编码的方式在画布上描绘图像
<html>
<head>
...
</head>
<body>
<canvas></canvas>
</body>
</html><html>
<head>
...
</head>
<body>
<canvas></canvas>
</body>
</html>
-
- canvas 默认是一个行内块元素
- canvas 默认画布大小是 300 * 150
- canvas 默认没有边框, 背景默认为无色透明
1.1 canvas 画布大小
- 在绘图之前, 先要确定一个画布的大小
-
- 因为画布默认是按照比例调整
- 所以我们调整宽度或者高度的时候, 调整一个, 另一个自然会按照比例自己调整
- 我们也可以宽高一起调整
- 调整画布大小有两种方案
-
- 第一种 : 通过 css 样式 ( 不推荐 )
- 第二种 : 通过标签属性 ( 推荐 )
-
-
<canvas width="1000" height="500"></canvas>
-
- 两种方案的区别
-
- 通过 css 样式的调整方案(不推荐)
-
-
- 因为 css 并没有设置了画布的大小,而是把原来 300 * 150 的画布的可视窗口变成了 1000 * 500。所以真实画布并没有放大, 只是可视程度变大了
- 如:把一个 300 * 150 的图片,放大到 1000 * 500 的大小来看
-
-
- 通过属性的调整方案(推荐)
-
-
- 这才是真正的将画布大小调整到 1000 * 500
-
1.2 画布的坐标
-
- canvas 画布和 css 的坐标系一样。左上角为 0 0 ,向右向下延伸为正方向
二、canvas 初体验
- canvas 画布很简单,类似于 windows 电脑上的画板工具
-
- 在绘制之前,先选定一个形状工具(直线,矩形,圆形,…)
- 确定路径起点,落笔
- 移动到路径终点,抬笔
- 设定样式(粗细,颜色)
- 在 canvas 绘制也是一样的逻辑
-
- 创建一个画布工具箱
-
-
- 语法:
canvas元素.getContext('2d')
- 如:
const ctx = canvasEle.getContext('2d')
- 语法:
-
-
- 确定路径起点,落笔
-
-
- 将画笔移动到一个指定位置下笔
- 语法:
工具箱.moveTo(x轴坐标, y轴坐标)
ctx.moveTo(100, 100)
-
-
- 移动到路径终点,抬笔
-
-
- 将画笔移动到一个指定位置,画下一条轨迹(路径)
- 注意:这里暂时没有显示,因为只是画了一个轨迹(路径)
- 语法:
工具箱.lineTo(x轴坐标, y轴坐标)
ctx.lineTo(300, 100)
- 如果多个lineTo方法连续执行,表示连续绘制,即在本次抬笔的位置直接落笔。
-
-
- 设定路径样式(可在落笔之前设置)
-
-
- 语法:
工具箱.样式属性 = 样式值
- 线的宽度:
ctx.lineWidth = 10
- 线的颜色:
ctx.strokeStyle = '#000'
- 语法:
-
-
- 确定本次绘制的路径信息(生效,显现绘制效果)
-
-
ctx.stroke()
- 必须绘制完成后执行
-
-
- 如:绘制一条线段:
// 0. 获取 canvas 标签元素
const canvasEle = document.querySelector('#canvas')
// 1. 创建画布工具箱
// 语法: canvas元素.getContext('2d')
const ctx = canvasEle.getContext('2d')
// 2. 开始绘制(落笔)
// 语法: 工具箱.moveTo(x轴坐标, y轴坐标)
ctx.moveTo(100, 100)
// 3. 将笔移动到一个指定位置, 画下一条轨迹(抬笔)
// 注意: 这里是没有显示的, 因为只是画了一个轨迹
// 语法: 工具箱.lineTo(x轴坐标, y轴坐标)
ctx.lineTo(300, 100)
// 4. 设定本条线的样式
// 设定线的宽度
// 语法: 工具箱.lineWidth = 数字
ctx.lineWidth = 10
// 设定线的颜色
// 语法: 工具箱.strokeStyle = '颜色'
ctx.strokeStyle = '#000'
// 5. 确定本次绘制信息
// 把上边画下的痕迹按照设定好的样式描绘下来
// 语法: 工具箱.stroke()
ctx.stroke()// 0. 获取 canvas 标签元素
const canvasEle = document.querySelector('#canvas')
// 1. 创建画布工具箱
// 语法: canvas元素.getContext('2d')
const ctx = canvasEle.getContext('2d')
// 2. 开始绘制(落笔)
// 语法: 工具箱.moveTo(x轴坐标, y轴坐标)
ctx.moveTo(100, 100)
// 3. 将笔移动到一个指定位置, 画下一条轨迹(抬笔)
// 注意: 这里是没有显示的, 因为只是画了一个轨迹
// 语法: 工具箱.lineTo(x轴坐标, y轴坐标)
ctx.lineTo(300, 100)
// 4. 设定本条线的样式
// 设定线的宽度
// 语法: 工具箱.lineWidth = 数字
ctx.lineWidth = 10
// 设定线的颜色
// 语法: 工具箱.strokeStyle = '颜色'
ctx.strokeStyle = '#000'
// 5. 确定本次绘制信息
// 把上边画下的痕迹按照设定好的样式描绘下来
// 语法: 工具箱.stroke()
ctx.stroke()
-
- 从坐标 ( 100, 100 ) 绘制到坐标 ( 300, 100 )
- 线段长度为 200px
- 线段宽度为 10px
- 线段颜色为 ‘#000’ ( 黑色 )
三、canvas 线宽颜色问题
- 需要注意,在绘制任何图形时,尽量不要出现奇数宽度,因为canvas在划分了坐标点后,每次绘制都是绘制在点坐标上。
- 一个宽度为 1px 的线段就会以如下这种方式被画出来:
- canvas在描绘这个线段的时候,会把线段的最中心点放在这个像素点位上
- 也就是说,在描述线宽的时候,实际上会从 0.5px 的位置绘制到 1.5px 的位置,合计描述宽度为 1px
- 但是浏览器不能识别小数像素
- 也就是说浏览器没办法从 0.5 开始绘制,也没有办法绘制到 1.5 停止
- 那么就只能是从 0 开始绘制到 2。所以线宽就会变成 2px 了
- 又因为本身一个像素的黑色被强制拉伸到两个像素宽度,所以颜色就会变浅
- 就像我们一杯墨水, 倒在一个杯子里面就是黑色
- 但是到在一个杯子里面的时候, 又倒进去一杯水, 颜色就会变浅
- 所以最终呈现出来的样式,如下图:
- 所以,我们在进行 canvas 绘制时,涉及到线段的宽度时,一般不会把线段宽度设置成奇数,尽量设置为偶数
四、线段的开始与闭合
- 在绘制两条样式不同的独立线段时,如果直接进行绘制,后绘制的线段样式可能会影响先绘制的线段样式,如:
const canvas = document.querySelector(".mycanvas");
const ctx = canvas.getContext("2d");
ctx.moveTo(100, 100);
ctx.lineTo(200, 100);
ctx.lineWidth = 10;
ctx.strokeStyle = "red";
ctx.stroke();
ctx.moveTo(100, 200);
ctx.lineTo(200, 200);
ctx.lineWidth = 4;
ctx.strokeStyle = "#000";
ctx.stroke();const canvas = document.querySelector(".mycanvas");
const ctx = canvas.getContext("2d");
ctx.moveTo(100, 100);
ctx.lineTo(200, 100);
ctx.lineWidth = 10;
ctx.strokeStyle = "red";
ctx.stroke();
ctx.moveTo(100, 200);
ctx.lineTo(200, 200);
ctx.lineWidth = 4;
ctx.strokeStyle = "#000";
ctx.stroke();
- 最终呈现出下图样式
- 可以看到第二条线段的样式覆盖在了第一条线段上方
- 这是因为在绘制一条新的线段之前,需要先初始化工具箱中的样式设置。
- 也就是所谓的,准备绘制一条新的线段:
ctx.beginPath();
const canvas = document.querySelector(".mycanvas");
const ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(100, 100);
ctx.lineTo(200, 100);
ctx.lineWidth = 10;
ctx.strokeStyle = "red";
ctx.stroke();
// 设置线段的开始(初始化工具箱)
ctx.beginPath();
ctx.moveTo(100, 200);
ctx.lineTo(200, 200);
ctx.lineWidth = 4;
ctx.strokeStyle = "#000";
ctx.stroke();const canvas = document.querySelector(".mycanvas");
const ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(100, 100);
ctx.lineTo(200, 100);
ctx.lineWidth = 10;
ctx.strokeStyle = "red";
ctx.stroke();
// 设置线段的开始(初始化工具箱)
ctx.beginPath();
ctx.moveTo(100, 200);
ctx.lineTo(200, 200);
ctx.lineWidth = 4;
ctx.strokeStyle = "#000";
ctx.stroke();
- 绘制效果:
- 还可以通过绘制连续线段,组合成几何图形,如三角形:
const canvas = document.querySelector(".mycanvas");
const ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(100, 100);
ctx.lineTo(200, 100);
ctx.lineTo(100, 200);
ctx.lineTo(100, 100);
ctx.lineWidth = 8;
ctx.strokeStyle = "red";
ctx.stroke();const canvas = document.querySelector(".mycanvas");
const ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(100, 100);
ctx.lineTo(200, 100);
ctx.lineTo(100, 200);
ctx.lineTo(100, 100);
ctx.lineWidth = 8;
ctx.strokeStyle = "red";
ctx.stroke();
绘制效果:
- 注意细节:
- 当最后一条线段的终点和第一条线段的起点重复时,并没有看到一种“闭合”的效果。
- 这就需要我们主动设置连续线段的闭合:
ctx.closePath()
- 只要已经绘制了至少两条连续线段,就可以使用closePath进行闭合,它会自动将最后一条线段的终点和第一条线段的起点进行连接
const canvas = document.querySelector(".mycanvas");
const ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(100, 100);
ctx.lineTo(200, 100);
ctx.lineTo(100, 200);
ctx.lineTo(100, 100); // 本次绘制可以省略,亦可出现三角形效果
ctx.lineWidth = 8;
ctx.strokeStyle = "red";
// 闭合连续线段
ctx.closePath();
ctx.stroke();const canvas = document.querySelector(".mycanvas");
const ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(100, 100);
ctx.lineTo(200, 100);
ctx.lineTo(100, 200);
ctx.lineTo(100, 100); // 本次绘制可以省略,亦可出现三角形效果
ctx.lineWidth = 8;
ctx.strokeStyle = "red";
// 闭合连续线段
ctx.closePath();
ctx.stroke();
- 绘制效果:
五、端点样式
- 在绘制连续线段时,canvas提供了多种线段连接点的样式处理
-
- 属性为:
ctx.lineJoin = '值'
- 属性为:
-
-
- 尖角:
miter
(默认) - 圆角:
round
- 斜角:
bevel
- 尖角:
-
const canvas = document.querySelector(".mycanvas");
const ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(100, 100);
ctx.lineTo(200, 150);
ctx.lineTo(100, 200);
ctx.lineWidth = 10;
ctx.strokeStyle = "red";
// 设置尖角
ctx.lineJoin = 'miter';
ctx.stroke();
ctx.beginPath();
ctx.moveTo(200, 100);
ctx.lineTo(300, 150);
ctx.lineTo(200, 200);
ctx.lineWidth = 10;
ctx.strokeStyle = "red";
// 设置圆角
ctx.lineJoin = 'round';
ctx.stroke();
ctx.beginPath();
ctx.moveTo(300, 100);
ctx.lineTo(400, 150);
ctx.lineTo(300, 200);
ctx.lineWidth = 10;
ctx.strokeStyle = "red";
// 设置斜角
ctx.lineJoin = 'bevel';
ctx.stroke();const canvas = document.querySelector(".mycanvas");
const ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(100, 100);
ctx.lineTo(200, 150);
ctx.lineTo(100, 200);
ctx.lineWidth = 10;
ctx.strokeStyle = "red";
// 设置尖角
ctx.lineJoin = 'miter';
ctx.stroke();
ctx.beginPath();
ctx.moveTo(200, 100);
ctx.lineTo(300, 150);
ctx.lineTo(200, 200);
ctx.lineWidth = 10;
ctx.strokeStyle = "red";
// 设置圆角
ctx.lineJoin = 'round';
ctx.stroke();
ctx.beginPath();
ctx.moveTo(300, 100);
ctx.lineTo(400, 150);
ctx.lineTo(300, 200);
ctx.lineWidth = 10;
ctx.strokeStyle = "red";
// 设置斜角
ctx.lineJoin = 'bevel';
ctx.stroke();
效果如图:
- 在绘制单条线段时,线段两端的样式也可以被控制
-
- 属性:
ctx.lineCap = '值'
- 属性:
-
-
- butt,无,默认
- round,圆
- square,方
-
const canvas = document.querySelector(".mycanvas");
const ctx = canvas.getContext("2d");
console.log(ctx);
ctx.beginPath();
ctx.moveTo(100, 100);
ctx.lineTo(200, 100);
ctx.lineWidth = 20;
ctx.strokeStyle = "red";
// 无
ctx.lineCap = "butt";
ctx.stroke();
ctx.beginPath();
ctx.moveTo(100, 150);
ctx.lineTo(200, 150);
ctx.lineWidth = 20;
ctx.strokeStyle = "red";
// 圆
ctx.lineCap = "round";
ctx.stroke();
ctx.beginPath();
ctx.moveTo(100, 200);
ctx.lineTo(200, 200);
ctx.lineWidth = 20;
ctx.strokeStyle = "red";
// 方
ctx.lineCap = "square";
ctx.stroke();const canvas = document.querySelector(".mycanvas");
const ctx = canvas.getContext("2d");
console.log(ctx);
ctx.beginPath();
ctx.moveTo(100, 100);
ctx.lineTo(200, 100);
ctx.lineWidth = 20;
ctx.strokeStyle = "red";
// 无
ctx.lineCap = "butt";
ctx.stroke();
ctx.beginPath();
ctx.moveTo(100, 150);
ctx.lineTo(200, 150);
ctx.lineWidth = 20;
ctx.strokeStyle = "red";
// 圆
ctx.lineCap = "round";
ctx.stroke();
ctx.beginPath();
ctx.moveTo(100, 200);
ctx.lineTo(200, 200);
ctx.lineWidth = 20;
ctx.strokeStyle = "red";
// 方
ctx.lineCap = "square";
ctx.stroke();
效果如图:
-
注意:
-
- square 和 round 会让线段稍稍变长
- 线段端点样式的颜色会和线段颜色保持一致
六、填充
- 当使用连续线段绘制出几何图形后,还可以对图形进行颜色填充
-
- 设置填充颜色:
ctx.fillStyle = '颜色值'
- 填充:
ctx.fill()
- 设置填充颜色:
const canvas = document.querySelector(".mycanvas");
const ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(100, 100);
ctx.lineTo(200, 100);
ctx.lineTo(200, 200);
ctx.lineTo(100, 100);
ctx.closePath();
ctx.fillStyle = "pink";
ctx.fill();const canvas = document.querySelector(".mycanvas");
const ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(100, 100);
ctx.lineTo(200, 100);
ctx.lineTo(200, 200);
ctx.lineTo(100, 100);
ctx.closePath();
ctx.fillStyle = "pink";
ctx.fill();
效果如图:
- 注意:填充时可以不进行路径闭合,填充方法会自动闭合路径后,再进行填充
- 填充和描边可以同时使用
const canvas = document.querySelector(".mycanvas");
const ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(100, 100);
ctx.lineTo(200, 100);
ctx.lineTo(200, 200);
ctx.lineTo(100, 100);
ctx.closePath();
// 设置路径样式
ctx.lineWidth = 10;
ctx.strokeStyle = "black";
ctx.stroke();
// 设置填充样式
ctx.fillStyle = "pink";
ctx.fill();const canvas = document.querySelector(".mycanvas");
const ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(100, 100);
ctx.lineTo(200, 100);
ctx.lineTo(200, 200);
ctx.lineTo(100, 100);
ctx.closePath();
// 设置路径样式
ctx.lineWidth = 10;
ctx.strokeStyle = "black";
ctx.stroke();
// 设置填充样式
ctx.fillStyle = "pink";
ctx.fill();
效果如图:
七、canvas 的填充规则 - 非零填充(了解)
示例1
- 绘制一个 “回” 形
- 注意一个细节 :
-
- 里面的小正方形我们会按照 顺时针 的方向绘制
- 外面的大正方形我们也会按照 顺时针 的方向绘制
// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('.mycanvas')
// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')
// 2. 开始绘制里面的小正方形
ctx.moveTo(200, 100);
ctx.lineTo(300, 100);
ctx.lineTo(300, 200);
ctx.lineTo(200, 200);
// 3. 开始绘制外面的大正方形
ctx.moveTo(150, 50);
ctx.lineTo(350, 50);
ctx.lineTo(350, 250);
ctx.lineTo(150, 250);
// 线段样式
ctx.lineWidth = 2;
ctx.strokeStyle = '#000';
ctx.stroke();// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('.mycanvas')
// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')
// 2. 开始绘制里面的小正方形
ctx.moveTo(200, 100);
ctx.lineTo(300, 100);
ctx.lineTo(300, 200);
ctx.lineTo(200, 200);
// 3. 开始绘制外面的大正方形
ctx.moveTo(150, 50);
ctx.lineTo(350, 50);
ctx.lineTo(350, 250);
ctx.lineTo(150, 250);
// 线段样式
ctx.lineWidth = 2;
ctx.strokeStyle = '#000';
ctx.stroke();
- 填充后看效果
// 4. 填充
ctx.fill()// 4. 填充
ctx.fill()
-
我们发现,两个都被填充了
-
这是因为,在填充的时候,就是会一次性把所有的内容都会填充好
-
注意 :
-
- 和是否闭合路径 ( ctx.closePath() ) 没有关系
- 和里外正方形的绘制先后顺序没有关系
示例2
- 再绘制一个 “回” 形
- 注意一个细节:
-
- 里面的小正方形我们会按照 逆时针 的方向绘制
- 外面的大正方形我们也会按照 顺时针 的方向绘制
// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('.mycanvas')
// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')
// 2. 开始绘制里面的小正方形
ctx.moveTo(200, 100);
ctx.lineTo(200, 200);
ctx.lineTo(300, 200);
ctx.lineTo(300, 100);
// 3. 开始绘制外面的大正方形
ctx.moveTo(150, 50);
ctx.lineTo(350, 50);
ctx.lineTo(350, 250);
ctx.lineTo(150, 250);
// 线段样式
ctx.lineWidth = 2;
ctx.strokeStyle = '#000';
ctx.stroke();// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('.mycanvas')
// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')
// 2. 开始绘制里面的小正方形
ctx.moveTo(200, 100);
ctx.lineTo(200, 200);
ctx.lineTo(300, 200);
ctx.lineTo(300, 100);
// 3. 开始绘制外面的大正方形
ctx.moveTo(150, 50);
ctx.lineTo(350, 50);
ctx.lineTo(350, 250);
ctx.lineTo(150, 250);
// 线段样式
ctx.lineWidth = 2;
ctx.strokeStyle = '#000';
ctx.stroke();
填充后看效果:
// 4. 填充
ctx.fill()// 4. 填充
ctx.fill()
- 此时发现,和刚才填充出来的结果不一样了
- 可以得出结论:填充的区域和线段绘制时的 顺时针 逆时针 方向有关系!
非零填充
- 其实我们的填充和顺时针逆时针有关系,但不是简单的顺逆时针的问题
- 非零填充的概念 :
-
- 从任何一个区域向画布最外层移动
- 按照经历最少的边数量计算
- 其中经历的顺时针边,记录为 +1
- 经历的逆时针边,记录为 -1
- 只要最终总和不为 零,那么该区域填充
- 如果最终总和为 零,那么该区域不填充
示例3:
- 这次我们绘制一个稍微复杂一些的图形
- 这是两个矩形对接在一起, 一个是顺时针绘制, 一个是逆时针绘制
- 我们来分析一下看看
- 首先, 最左侧封闭图形区域
-
- 如果走最短的路线出来的话,会经历一条顺时针的边
- 记录为 +1
- 最终为 +1
- 所以该区域会被填充
-
然后, 最右侧封闭图形
-
- 经历最短路线出来的话,会经历一条逆时针的边
- 记录为 -1
- 最终为 -1
- 所以该区域会被填充
-
最后, 中间的封闭图形
-
- 经历最短路线出来的话,必然会经历一条顺时针的边 和 一条逆时针的边
- 顺时针记录为 +1
- 逆时针记录为 -1
- 合计为 0
- 所以该区域不会被填充
-
实际测试
// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('.mycanvas')
// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')
// 2. 开始绘制里面的小正方形
// 顺时针绘制
ctx.moveTo(100, 50);
ctx.lineTo(200, 50);
ctx.lineTo(200, 150);
ctx.lineTo(100, 150);
ctx.lineTo(100, 50);
// 逆时针绘制
ctx.moveTo(150, 100);
ctx.lineTo(150, 200);
ctx.lineTo(250, 200);
ctx.lineTo(250, 100);
ctx.lineTo(150, 100);
ctx.lineWidth = 2;
ctx.strokeStyle = '#000';
ctx.stroke();
// 4. 填充
ctx.fill()// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('.mycanvas')
// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')
// 2. 开始绘制里面的小正方形
// 顺时针绘制
ctx.moveTo(100, 50);
ctx.lineTo(200, 50);
ctx.lineTo(200, 150);
ctx.lineTo(100, 150);
ctx.lineTo(100, 50);
// 逆时针绘制
ctx.moveTo(150, 100);
ctx.lineTo(150, 200);
ctx.lineTo(250, 200);
ctx.lineTo(250, 100);
ctx.lineTo(150, 100);
ctx.lineWidth = 2;
ctx.strokeStyle = '#000';
ctx.stroke();
// 4. 填充
ctx.fill()
- 效果如图:
八、绘制矩形
- 绘制矩形的方法有三个:
- 矩形路径:
-
- 语法:
ctx.rect( 矩形起点 x 轴坐标, 矩形起点 y 轴坐标, 矩形宽度, 矩形高度 )
- 如:
ctx.rect(100, 100, 100, 100)
- 表示在坐标 100,100 的位置绘制一个 100*100 的矩形路径,默认无填充无描边
- 可通过
ctx.stroke()
描边,通过ctx.fill()
填充 - 可通过
lineWidth
,strokeStyle
,fillStyle
属性,分别设置描边宽度,描边色,填充色
- 语法:
- 描边矩形:
-
- 语法:
ctx.strokeRect( 矩形起点 x 轴坐标, 矩形起点 y 轴坐标, 矩形宽度, 矩形高度 )
- 如:
ctx.strokeRect(300, 100, 100, 100)
- 表示在坐标 300,100 的位置绘制一个 100*100 的描边矩形,无填充
- 可通过
lineWidth
,strokeStyle
属性,设置描边宽度,描边色
- 语法:
- 填充矩形:
-
- 语法:
ctx.fillRect( 矩形起点 x 轴坐标, 矩形起点 y 轴坐标, 矩形宽度, 矩形高度 )
- 如:
ctx.fillRect(500, 100, 100, 100)
- 表示在坐标 500,100 的位置绘制一个 100*100 的填充矩形,无描边
- 可通过
fillStyle
属性,设置填充色
- 语法:
// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('.mycanvas')
// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')
// 2. 绘制矩形路径
ctx.rect(100, 100, 100, 100);
// 3. 绘制描边矩形
ctx.strokeRect(300, 100, 100, 100);
// 4. 绘制填充矩形
ctx.fillRect(500, 100, 100, 100);// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('.mycanvas')
// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')
// 2. 绘制矩形路径
ctx.rect(100, 100, 100, 100);
// 3. 绘制描边矩形
ctx.strokeRect(300, 100, 100, 100);
// 4. 绘制填充矩形
ctx.fillRect(500, 100, 100, 100);
效果如图:
九、绘制圆形
- 什么是圆:
-
- 圆 就是从一个点出发,按照半径,画弧线,当弧线饶了一圈回到原点的时候,就是一个圆形。
- 在canvas内,绘制圆形,其实就是在绘制弧线
- 什么是弧度
-
- 这是一个圆,圆心为 o,半径为 r
-
- 以圆心 o 做坐标轴,x 轴正方向上和圆周的交点为弧度起点
-
- 在圆周上,从弧度起点,顺着圆周移动,移动的距离成为弧长,当弧长和半径一样时
- 这段弧长所对应的圆心角是 1 弧度
-
- 根据圆周公式 : 周长 = 2 * π * r
- 所以:
-
-
- 一个圆周是 : 2 * π
- 半个圆周是 : π
- 四分之一圆周是 : π / 2
-
- 了解了什么是弧度,接下来就可以开始绘制弧线了,绘制弧线有两种方式:圆弧,椭圆弧
-
- 圆弧
-
-
- 语法:
ctx.arc( x, y, r, startAngle, endAngle, counterclockwise )
- 语法:
-
-
-
-
- x:圆心的 x 轴坐标
- y:圆心的 y 周坐标
- r:圆的半径
- startAngle:绘制弧线的起点弧度
- endAngle:绘制弧线的终点弧度
- counterclockwise:方向,false 为顺时针(默认),true 为逆时针
-
-
-
-
- 如:
ctx.arc( 150, 150, 100, 0, 1, false )
- 表示绘制一个圆心在坐标 150,150,半径100,从 0 顺时针 到 1 的弧线路径,默认无填充无描边
- 如:
-
// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('.mycanvas')
// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')
// 2. 绘制圆弧
ctx.arc( 150, 150, 100, 0, Math.PI, false )
// 3. 描边
ctx.lineWidth = 2;
ctx.strokeStyle = "red";
ctx.stroke();// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('.mycanvas')
// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')
// 2. 绘制圆弧
ctx.arc( 150, 150, 100, 0, Math.PI, false )
// 3. 描边
ctx.lineWidth = 2;
ctx.strokeStyle = "red";
ctx.stroke();
-
- 椭圆弧
-
-
- 语法:
ctx.ellipse( x, y, radiusX, radiusY, rotation, startAngle, endAngle, antiClockwise )
- 语法:
-
-
-
-
- x:椭圆中心点的 x 轴坐标
- y:椭圆中心点的 y 轴坐标
- radiusX:椭圆在 x 轴方向上的半径
- radiusY:椭圆在 y 轴方向上的半径
- rotation:旋转弧度,指讲该椭圆进行旋转
- startAngle:弧线开始弧度
- endAngle:弧线结束弧度
- antiClockwise:方向,false 表示逆时针绘制(默认),true 表示顺时针绘制
-
-
-
-
- 如:
ctx.ellipse( 300, 150, 200, 100, 0, 0, Math.PI * 2, false )
- 表示绘制一个圆心在坐标 350,150,x轴半径200,y轴半径为100,不旋转,从 0 顺时针 到 Math.PI * 2 的弧线路径,默认无填充无描边
- 如:
-
// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('.mycanvas')
// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')
// 2. 绘制椭圆弧
ctx.ellipse( 300, 150, 200, 100, 0, 0, Math.PI * 2, false )
// 3. 描边
ctx.lineWidth = 2;
ctx.stroke();// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('.mycanvas')
// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')
// 2. 绘制椭圆弧
ctx.ellipse( 300, 150, 200, 100, 0, 0, Math.PI * 2, false )
// 3. 描边
ctx.lineWidth = 2;
ctx.stroke();
-
-
- 这样一个椭圆就出来了,解释一下这些内容的意义
-
-
-
- 旋转弧度,就是在现在的基础上,让整个图形进行旋转
-
ctx.ellipse( 300, 150, 200, 100, Math.PI / 2, 0, Math.PI * 2, false )ctx.ellipse( 300, 150, 200, 100, Math.PI / 2, 0, Math.PI * 2, false )
十、擦除画布
- 就像画画时的橡皮擦,擦除掉指定区域的内容
- 语法:
工具箱.clearRect( 矩形起点 x 轴坐标, 矩形起点 y 轴坐标, 矩形宽度, 矩形高度 )
-
- 如:
工具箱.clearRect( 150, 150, 30, 30 )
- 表示从坐标 150, 150 位置开始,擦除一块 30 * 30 的区域
- 如:
- 注意:
-
- clearRect默认只能擦除填充和描边,并不能擦除路径
- canvas中的绘制方法(如stroke,fill),会以“上一次 beginPath 之后的所有路径为基础进行绘制
- 如果没有使用beginPath()方法,上一次描述的路径没有被清除,这一次进行描边等操作还会绘制出之前的路径,表现出一种类似没有擦除的状态。
- 所以为了彻底擦除,在使用了clearRect后,一般都会再执行一次beginPath方法
十一、绘制文字
- 在canvas内可以直接绘制文字,不需要通过线段一笔一划的写出来
-
- 描边文字(空心):
-
-
- 语法:
ctx.strokeText("文字内容", x 坐标, y 坐标);
- 语法:
-
-
- 填充文字(实心):
-
-
- 语法:
ctx.fillText("文字内容", x 坐标, y 坐标);
- 语法:
-
// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('.mycanvas')
// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')
// 2. 绘制文字
ctx.fillText("千锋", 100, 100);
ctx.strokeText("数字智慧大前端", 100, 200); // 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('.mycanvas')
// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')
// 2. 绘制文字
ctx.fillText("千锋", 100, 100);
ctx.strokeText("数字智慧大前端", 100, 200);
- 文字样式修饰
-
- 字体大小:
ctx.font = '字体大小 字体'
- 文字水平对齐:
ctx.textAlign = 'left | center | right';
- 文字垂直对齐:
ctx.textBaseline = 'top | middle | bottom';
- 注意:文字的样式修饰需要在绘制之前设置
- 字体大小:
// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('.mycanvas')
// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')
// 2. 设置文字样式
ctx.font = "50px 黑体";
ctx.fillText("千锋", 100, 100);
// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('.mycanvas')
// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d')
// 2. 设置文字样式
ctx.font = "50px 黑体";
ctx.fillText("千锋", 100, 100);
水平对齐:ctx.textAlign | 垂直对齐:ctx.textBaseline |
---|---|
start:文本在指定的位置开始end:文本在指定的位置结束center:居中对齐left:左对齐right:右对齐 | alphabetic:默认,文本基线是普通的字母基线top:文本基线是 em 方框的顶端hanging:文本基线是悬挂基线middle:文本基线是 em 方框的正中ideographic:文本基线是 em 基线bottom:文本基线是 em 方框的底端 |
- 获取文本信息
-
- 语法:
ctx.measureText("文本")
- 获取文本宽度:
ctx.measureText("前端").width
- 用于给文本添加下划线,或边框线等操作
- 语法:
十二、阴影
- 在canvas中,还可以对路径添加阴影效果(文字也可以有阴影)
-
- 阴影 x 轴偏移:
ctx.shadowOffsetX = number;
- 阴影 y 轴偏移:
ctx.shadowOffsetY = number;
- 模糊大小:
ctx.shadowBlur = number;
- 阴影颜色:
ctx.shadowColor = '颜色值';
- 阴影 x 轴偏移:
// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('.mycanvas');
// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d');
// 2. 设置阴影效果
ctx.shadowOffsetX = 30;
ctx.shadowOffsetY = 10;
ctx.shadowBlur = 2;
ctx.shadowColor = "#aaa";
// 3. 绘制文本
ctx.font = "50px 黑体";
ctx.strokeText("千锋", 100, 50);
ctx.fillText("千锋", 300, 50);
// 4. 绘制矩形
ctx.strokeRect( 100, 80, 100, 50);
ctx.fillRect( 300, 80, 100, 50);
// 5. 绘制圆
ctx.arc( 150, 200, 50, 0, 2 * Math.PI);
ctx.stroke();
ctx.beginPath();
ctx.arc( 350, 200, 50, 0, 2 * Math.PI);
ctx.fill();// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('.mycanvas');
// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d');
// 2. 设置阴影效果
ctx.shadowOffsetX = 30;
ctx.shadowOffsetY = 10;
ctx.shadowBlur = 2;
ctx.shadowColor = "#aaa";
// 3. 绘制文本
ctx.font = "50px 黑体";
ctx.strokeText("千锋", 100, 50);
ctx.fillText("千锋", 300, 50);
// 4. 绘制矩形
ctx.strokeRect( 100, 80, 100, 50);
ctx.fillRect( 300, 80, 100, 50);
// 5. 绘制圆
ctx.arc( 150, 200, 50, 0, 2 * Math.PI);
ctx.stroke();
ctx.beginPath();
ctx.arc( 350, 200, 50, 0, 2 * Math.PI);
ctx.fill();
效果如图:
- 如需不同的阴影效果,每次绘制前可以重新配置
十三、绘制虚线
- 语法:
工具箱.setDashLine([ 第一段长度, 第二段长度, ... ])
- 如:
ctx.setDashLine([5, 10])
- 表示绘制出的虚线为:实5,虚10,实5,虚10,…
// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('.mycanvas');
// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d');
console.log(ctx);
// 2. 设置虚线方案:实线5px 和 虚线10px 重复出现
ctx.setLineDash([ 5, 10 ]);
// 3. 绘制线段
ctx.moveTo( 100, 50 );
ctx.lineTo( 400, 50 );
ctx.strokeStyle = '#000';
ctx.lineWidth = 2;
ctx.stroke();
// 4. 绘制文本
ctx.font = "100px 黑体";
ctx.strokeText("千锋", 100, 200);// 0. 获取到页面上的 canvas 标签元素节点
const canvasEle = document.querySelector('.mycanvas');
// 1. 获取当前这个画布的工具箱
const ctx = canvasEle.getContext('2d');
console.log(ctx);
// 2. 设置虚线方案:实线5px 和 虚线10px 重复出现
ctx.setLineDash([ 5, 10 ]);
// 3. 绘制线段
ctx.moveTo( 100, 50 );
ctx.lineTo( 400, 50 );
ctx.strokeStyle = '#000';
ctx.lineWidth = 2;
ctx.stroke();
// 4. 绘制文本
ctx.font = "100px 黑体";
ctx.strokeText("千锋", 100, 200);
注意:填充无法设置虚线效果
十四、总结
- 创建一个画笔对象(获取工具箱)
-
const ctx = canvasElement.getContext('2d');
- 常用属性
-
- 线条粗细:
ctx.lineWidth = number;
- 描边色:
ctx.strokeStyle = '颜色值';
- 端点样式:
ctx.lineCap = 'butt | round | square';
- 接洽点样式:
ctx.lineJoin = 'miter | bevel | round';
- 填充色:
ctx.fillStyle = '颜色值';
- 字体大小:
ctx.font = '字体大小 字体';
- 文字水平对齐:
ctx.textAlign = 'left | center | right';
- 文字垂直对齐:
ctx.textBaseline = 'top | middle | bottom';
- 阴影 x 轴偏移:
ctx.shadowOffsetX = number;
- 阴影 y 轴偏移:
ctx.shadowOffsetY = number;
- 模糊大小:
ctx.shadowBlur = number;
- 阴影颜色:
ctx.shadowColor = '颜色值';
- 线条粗细:
- 常用方法
-
- 下次绘制开启新路径(弃用已存在路径):
ctx.beginPath();
- 开始绘制线段的起点:
ctx.moveTo(x, y);
- 连线到:
ctx.lineTo(x, y);
- 闭合路径:
ctx.closePath();
- 描边:
ctx.stroke();
- 填充:
ctx.fill();
- 矩形路径:
ctx.rect(x, y, w, h);
- 描边矩形:
ctx.strokeRect(x, y, w, h);
- 填充矩形:
ctx.fillRect(x, y, w, h);
- 圆弧路径:
ctx.arc(cx, cy, r, start, end, false);
- 椭圆弧路径:
ctx.ellipse( cx, cy, xr, yr, rotate, start, end, false );
- 擦除:
ctx.clearRect(x, y, w, h);
- 填充文字:
ctx.fillText(string, x, y);
- 描边文字:
ctx.strokeText(string, x, y);
- 获取文字信息:
ctx.measureText('测试');
- 设置虚线:
setLineDash([线段1宽度, 线段2宽度, ...])
- 下次绘制开启新路径(弃用已存在路径):
十五:作业
ctx.strokeText(“千锋”, 100, 200);
注意:**填充无法设置虚线效果**
# 十四、总结
1. 创建一个画笔对象(获取工具箱)
- - `const ctx = canvasElement.getContext('2d');`
1. 常用属性
- - 线条粗细:`ctx.lineWidth = number;`
- 描边色:`ctx.strokeStyle = '颜色值';`
- 端点样式:`ctx.lineCap = 'butt | round | square';`
- 接洽点样式:`ctx.lineJoin = 'miter | bevel | round';`
- 填充色:`ctx.fillStyle = '颜色值';`
- 字体大小:`ctx.font = '字体大小 字体';`
- 文字水平对齐:`ctx.textAlign = 'left | center | right';`
- 文字垂直对齐:`ctx.textBaseline = 'top | middle | bottom';`
- 阴影 x 轴偏移:`ctx.shadowOffsetX = number;`
- 阴影 y 轴偏移:`ctx.shadowOffsetY = number;`
- 模糊大小:`ctx.shadowBlur = number;`
- 阴影颜色:`ctx.shadowColor = '颜色值';`
1. 常用方法
- - 下次绘制开启新路径(弃用已存在路径):`ctx.beginPath();`
- 开始绘制线段的起点:`ctx.moveTo(x, y);`
- 连线到:`ctx.lineTo(x, y);`
- 闭合路径:`ctx.closePath();`
- 描边:`ctx.stroke();`
- 填充:`ctx.fill();`
- 矩形路径:`ctx.rect(x, y, w, h);`
- 描边矩形:`ctx.strokeRect(x, y, w, h);`
- 填充矩形:`ctx.fillRect(x, y, w, h);`
- 圆弧路径:`ctx.arc(cx, cy, r, start, end, false);`
- 椭圆弧路径:`ctx.ellipse( cx, cy, xr, yr, rotate, start, end, false );`
- 擦除:`ctx.clearRect(x, y, w, h);`
- 填充文字:`ctx.fillText(string, x, y);`
- 描边文字:`ctx.strokeText(string, x, y);`
- 获取文字信息:`ctx.measureText('测试');`
- 设置虚线:`setLineDash([线段1宽度, 线段2宽度, ...])`
# 十五:作业
![img](https://img-blog.csdnimg.cn/img_convert/e1b4bc900b624636bb4371c576e19483.png)![img](https://img-blog.csdnimg.cn/img_convert/2a9c80b7b6a33e97f0d117da0f59f831.png)![img](https://img-blog.csdnimg.cn/img_convert/8613e17597f3c256d5f2b57a886a6ba9.png)