canvas 图形画布标签
1. 如何定义一个 图形画布 ?
- 用于
- 如何定义一个 图形画布 ? (在上面 绘制图形)
- 使用
<canvas>
图形画布 标签 - ['kænvəs]
- 使用
- 如何定义一个 图形画布 ? (在上面 绘制图形)
- 使用
<canvas>
图形画布 标签 可以 直接绘制图形吗 ?- 不可以,
<canvas>
图形画布 标签 只是 图形容器,要使用脚本 来绘制图形。
- 不可以,
<canvas>
图形画布 标签的浏览器支持- IE 8 以及更早的版本不支持
<canvas>
图形画布 标签。
- IE 8 以及更早的版本不支持
- 如何通过
<canvas>
图形画布 标签 来画出一个 红色的矩形:
<canvas id="myCanvas">your browser does not support the canvas tag </canvas>
<script type="text/javascript">
var canvas=document.getElementById('myCanvas');
var ctx=canvas.getContext('2d');
ctx.fillStyle='#ff0000';
ctx.fillRect(0,0,80,100);
</script>
- 测试
- 一个红色的矩形
1.1 canvas 图形画布 标签的属性 有哪些 ?
<canvas>
图形画布 标签的属性
属性名 | 属性值 | 用于 |
---|---|---|
① height | = number ,单位 pixels | 设置 canvas 画布的高度。 |
② width | = number ,单位 pixels | 设置 canvas 画布的宽度。 |
-
<canvas>
图形画布 标签的属性- canvas 只有 2个属性:
height
高度 属性和width
宽度 属性 - canvas-
height/width
图形画布 宽高属性的属性值- canvas-
height/width="number px"
- pixels
- canvas-
- 规定 以像素计
- 没有设置 宽高时,Canvas 图形画布的默认大小 为多少 ?
- 300像素×150像素(宽×高,像素的单位是 px)矩形画布
- canvas 只有 2个属性:
-
canvas-宽高重设 = 清空画布:
- 每当画布的高度或宽度被 重设时, 画布内容 就会被清空
-
清空画布
- 通过重设 width 或 height 属性来清空画布(使用 JavaScript)
<canvas id="myCanvas" width="200" height="200" style="border:1px solid">
Your browser does not support the canvas element.
</canvas>
<script type="text/javascript">
var c=document.getElementById("myCanvas");
var cxt=c.getContext("2d");
cxt.fillStyle="#92B901";
cxt.fillRect(50,50,100,100);
function clearCanvas()
{
c.height=c.height;
}
</script>
<br />
<button onclick="clearCanvas()">清空画布</button>
</body>
<canvas>
图形画布 标签 和 SVG 以及 VML 之间的差异- 不同点:
<canvas>
图形画布 标签 和 SVG 以及 VML 之间的不同点- 基于js:
<canvas>
有一个基于 JavaScript 的绘图 API - XML 文档: SVG 和 VML 使用一个 XML 文档 来描述绘图。
- 基于js:
- 相同点:
- 功能等同: 这两种方式在功能上是等同的,任何一种 都可以用另一种来模拟。
- 移除元素:
- 移除元素: SVG 绘图 很容易编辑,只要从其描述中移除元素就行。
- 重新绘制: 要从同一图形的一个
<canvas>
图形画布 标签中 移除元素,需要擦掉绘图 重新绘制它。
- 不同点:
1.2 canvas 图形画布的 基本用法 有哪些 ?
- ⑴ 如何使用
<canvas>
图形画布 标签 绘图 ?-
图形 API定义: 大多数 Canvas 绘图 API 定义在 什么上 ?
- 都没有 定义在
<canvas>
图形画布 标签 本身上,而是定义在 通过画布的 getContext() 方法 获得的一个“绘图环境”对象上。
- 都没有 定义在
-
图形 路径的定义: 路径由 一系列的方法调用 来定义,比如调用 beginPath() 和 arc() 方法,而不是描述为 字母和数字的字符串。
-
图形 路径操作: 一旦定义了路径,其他的方法,如 fill(),都是对此路径 操作。
- 绘图环境的各种属性,比如 fillStyle,说明了这些操作 如何使用。
-
API 紧凑原因: = 无绘制文本支持. 一个原因上 它没有对 绘制文本 提供任何支持。
-
画布上加文本: 要把文本加入到一个
<canvas>
图形画布 上- 自己绘制它 再用位图图像 合并它
- 在
<canvas>
图形画布 上方 使用 CSS 定位 来覆盖 HTML 文本。
-
- ⑵ 如何定义 canvas 图形画布的大小 ?
- 用 CSS 定义画布宽高: 可以使用 CSS 来定义大小,但在绘制时 图像会伸缩 以适应它的框架尺寸
- 画布扭曲: 如果 CSS 的尺寸 与初始画布的 比例不一致,它会出现扭曲。
- 使用
<canvas>
图形画布 标签的height/width
属性:- 如果绘制出来的图像 是扭曲的, 尝试用 标签的
width/height
属性为画布 明确规定宽高,而不是使用 CSS。( 难道标签的属性 比 css 的属性 更好用 ?) - 总结:
<canvas>
图形画布的宽高,最好用 标签的宽高属性 canvas-height/width
定义,而不是 css .
- 如果绘制出来的图像 是扭曲的, 尝试用 标签的
- ⑶
<canvas>
图形画布 的样式- 画布 能设置样式:
<canvas>
图形画布 元素可以像 普通图像一样 设置样式吗 ?- 可以. 有 margin,border,background 等等属性。
- 可以为 画布 设置边框,背景色等.
- 画布样式 不会影响在canvas 画布中的实际图像。
- 可以. 有 margin,border,background 等等属性。
- 画布默认样式 - 透明:如果没有为 canvas 画布 规定样式,画布会 完全透明。
- 示例1: 想让画布变成可见,可以为画布 规定一个边框
- 画布 能设置样式:
<body>
<canvas id="myCanvas" style="border:1px solid">
Your browser does not support the canvas element.
</canvas>
</body>
- 测试
- 画布 没有设置宽高时,默认是 300*150 的 矩形画布
- 在 canvas-
style
样式属性中,设置边框后,透明画布 可见了
- 示例2: 定义一个 200px*200px 的正方形画布,并且为画布设置一个绿色边框,让画布可见
<canvas id="myCanvas" height="200px" width="200px" style="border:1px solid green">
Your browser does not support the canvas element.
</canvas>
- 测试
- 指定了宽高 边框 的正方形画布
- ⑷
<canvas>
图形画布 标签的替代内容- 为什么要设置 canvas 画布的 替代内容 ?
- 老版本浏览器不支持 canvas 图形画布 标签:
- 由于某些较老的浏览器(尤其是IE9之前的IE浏览器)
- 浏览器类型不支持:
- 文本浏览器 不支持HTML元素"canvas"
- 在这些浏览器上 不能显示画布,要有替代内容,才知道内容是什么。
- 老版本浏览器不支持 canvas 图形画布 标签:
- canvas 图形画布的 替代内容的显示
- 不支持
<canvas>
画布 的浏览器:- 会忽略画布容器 ,显示 替代内容。
- 支持
<canvas>
画布 的浏览器:- 忽略 canvas 标签中的替代内容,只渲染 canvas 画布图形。
- 总结: 支持 画布标签 = 显示 画布图形内容,不支持画布,才显示画布标签中的替代内容
- 不支持
- 用什么做 canvas 图形画布的 替代内容
- 画布的文字描述: 对 canvas 图形画布的内容的文字描述
- 画布的静态图片: 是提供动态生成内容 相对应的静态图片
- 不支持 canvas 标签的浏览器,才会显示 对应的文字描述 或者图片
- 不设置图形的替代内容,怎么表示 ?
- 不写标签内容:
<canvas id="foo" ...></canvas>
- 在所有支持 canvas 的浏览器中 , 都是完全兼容的。
- 不写标签内容:
- 为什么要设置 canvas 画布的 替代内容 ?
<canvas id="stockGraph" width="150" height="150">
current stock price: $3.15 +0.15
</canvas>
<canvas id="clock" width="150" height="150">
<img src="images/clock.png" width="150" height="150" alt=""/>
</canvas>
- ⑸ 画笔下笔地方: 渲染上下文 (The rendering context)
- 画布起始空白: canvas 图形画布 起初是空白的,一张白纸一样,什么都没画。
- 找一块下笔处:
- 在什么上面 绘制图形 ?
- 为了展示,首先脚本需要找到 渲染上下文( 就是图形上下笔的地方),然后在它的上面 绘制图形。
- 这个下笔的地方,不是 canvas 图形画布标签,相当于 canvas 画布上,找一个更具体的位置了.(个人理解的)
- 在什么上面 绘制图形 ?
- 怎么找到这个下笔处 ?
- canvas-
getContext()
获取上下文 方法<canvas>
图形画布 元素 有一个叫做getContext()
的方法 , 用来获得 渲染上下文(下笔处)和它的绘画功能。
- canvas-
getContext()
获取上下文 方法介绍getContext()
回去上下文 方法 只有一个参数,上下文的格式。- 对于 2D图像而言,可以使用 CanvasRenderingContext2D。
- 什么是 2D图像 ?
- 2D图形内容 只有 水平的X轴向与垂直的Y轴向,传统手工漫画、插画等都属于2D类.
- 什么是3D图像 ?
- 3D图,即空间立体图形,三维图 所有的图形是在X Y Z三维空间里。常用的三维绘图软件如:3DMax.
var canvas = document.getElementById('tutorial');
var ctx = canvas.getContext('2d');
- 代码解析
- 获取对象:
<canvas>
图形画布的对象:- 第一行通过使用
document.getElementById()
用id 获取元素 方法 来为<canvas>
画布 元素得到 DOM 对象。
- 第一行通过使用
- 获取对象的上下文(画笔下笔处):
- 一旦有了元素对象,你可以通过使用它的
getContext()
方法来 访问绘画上下文。
- 一旦有了元素对象,你可以通过使用它的
- 获取对象:
- ⑹ 检查 canvas 图形画布 标签的支持性
- 替换内容是用于 哪里 ?
- 用在不支持
<canvas>
图形画布 标签的浏览器中展示的。
- 用在不支持
- 检查标签的支持性:
- 通过 测试
getContext()
获取上下文 方法的存在,脚本可以 检查编程支持性。 - 这个方法是 canvas 图形画布 对象的,标签存在,方法才存在(在不支持 canvas 标签的浏览器中,这个标签 相当于不存在吗 ?)
- 通过 测试
- 替换内容是用于 哪里 ?
<script type="text/javascript">
// 通过元素 唯一名称 id ,获取 canvas 元素对象
var canvas = document.getElementById('tutorial');
if (canvas.getContext){//如果 getContext() 获取上下文 方法 存在, 为真
var ctx = canvas.getContext('2d');
// drawing code here(绘图代码)
} else {
// canvas-unsupported code here(不支持 canvas 图形画布 标签的代码)
}
</script>
1.3 如何使用 canvas 图形画布 标签 来绘制图形 ?
1.3.1 画布栅格和坐标空间
- ⑴ 画布栅格和坐标空间
- 画布栅格(canvas grid)
- 画布被网格覆盖:
- canvas 图形画布 默认被网格所覆盖。
- 网格中的一个单元(1个正方形) = canvas 元素中的一像素。
- 画布栅格的起点
- 画布栅格的起点 定义在什么位置 ?坐标是多少 ?
- 左上角(坐标为(0,0))。
- 画布栅格的起点 定义在什么位置 ?坐标是多少 ?
- 相对位置: 所有元素的位置 都是相对于什么位置的 ?
- 相对于 原点 = 左上角 = (0,0)。
- 下图中 蓝色方形 左上角的坐标 为(x,y)
- x: 距离左边(y轴)x像素
- y 距离上边(x轴)y像素
1.3.2 绘制矩形
- 什么是矩形: 至少有三个内角 都是直角的四边形 = 矩形 = 长方形 + 正方形
- ⑴ 绘制图形和路径:
- 原生图形绘制: 矩形 (不需要生成路径)
- canvas 图形画布 标签 只支持一种 原生的图形绘制,是什么图形 ?
- 矩形。
- 绘制图形 需要路径: 其他的图形的绘制,都至少需要 生成一条路径。
- 原生图形绘制: 矩形 (不需要生成路径)
- ⑵ 绘制矩形的 3 种方法:
- ① 填充矩形
- 使用
fillRect(x, y, width, height)
填充矩形 方法 , 绘制一个填充的矩形 - ② 画矩形边框
- 使用
strokeRect(x, y, width, height)
画矩形 方法, 绘制一个矩形的边框 .(单纯就是一个矩形)
- 使用
- ③ 清除矩形区域:
- 使用
clearRect(x, y, width, height)
清除矩形区域 方法, 清除 指定矩形区域,让清除部分完全透明。
- 使用
- 3 种矩形绘制方法 的相同点
- 方法 参数相同。
- x与y: 矩形的左上角坐标
- (在 canvas 画布上 所绘制的矩形的左上角, 相对于原点的坐标)。
- width和height: 设置矩形的 宽高尺寸.
- 示例1: 绘制一个 宽高 都为100px的矩形 (宽高相等 = 正方形,)
- 标签嵌套关系
<body>
文档主体 标签<canvas> - id width height style
图形画布 标签<script> type
脚本 标签(放在 body 标签中才起作用,为什么?)
- 标签嵌套关系
<body>
<canvas id="myCanvas" height="200px" width="200px" style="border:1px solid green">
边长 100 像素的 黑色正方形
</canvas>
<script type="text/javascript">
var canvas=document.getElementById("myCanvas");//获取 canvas 图形画布元素
if(canvas.getContext){//判断canvas 的获取上下文方法是否存在
var ctx=canvas.getContext("2d");//获取上下文
ctx.fillRect(25,25,100,100);//在获取的上下文中 画一个填充矩形
}
</script>
</body>
- 对象 关联关系
- document>canvas > ctx >fillRect() 填充矩形
- 没有指定矩形的颜色,默认颜色为: 黑色.
- 画布的边框,设置的是绿色(这个不是绘制的矩形,是画布容器设置的边框)
-
示例2: 在上方 正方形中,清除一个 宽高50像素的正方形 (相当于 用橡皮擦 涂掉了)
ctx.clearRect(50,50,50,50);
1个填充正方形和 1个清除清除正方形 坐标和距离 -
测试
- 关联关系: document>canvas>ctx>clearRect() 清除矩形区域 方法
- 如何让被清除 矩形在 黑色矩形的正中间 ?
- 取决于 清除矩形区域的左上角 坐标 (x1,y1) =
ctx.clearRect(x1,y1,50,50);
- 假设黑色正方形 左上角位置坐标 (x,y) =
ctx.fillRect(25,25,100,100);
- x1= 25+ (100-50)/2 = 50
- y1=25+(100-50)/2 = 50
- x到x1的横向距离 = x-x1= (黑色正方形的宽 - 清除矩形区域的宽)/2 (垂直距离同理)
- 取决于 清除矩形区域的左上角 坐标 (x1,y1) =
- 示例3: 在清除矩形 区域中,生成一个 宽高 20像素 的矩形边框
ctx.strokeRect(65,65,20,20);
画矩形边框 方法
-
关联关系
- document>canvas>ctx>strokeRect()画矩形边框 方法
-
三个矩形绘制方法
- 关联关系: document>canvas>ctx>
- fillRect()
- clearRect()
- strokeRect)
- 关联关系: document>canvas>ctx>
ctx.fillRect(25,25,100,100);
ctx.clearRect(50,50,50,50);
ctx.strokeRect(65,65,20,20);
1.3.3 绘制路径
- ⑴ 图形基本元素
- 图形的基本元素是什么 ?
- 路径。
- 图形的基本元素是什么 ?
- ⑵ 路径:
- 什么是路径 ?
- 路径 = 点的集合 = 线段 + 曲线
- 是通过 不同颜色和宽度的 线段或曲线 相连形成的不同形状的 点的集合。
- 一个路径,甚至一个子路径,都是闭合的(这个是什么意思 ?线段是闭合的吗 ?闭合: 首尾相连,线段不是闭合的)。
- ▲总结: 图形的基本元素 = 路径 = 点的集合 = 线段 + 曲线
- 什么是路径 ?
- ⑶ 使用路径 绘制图形的步骤。
- ① 新建路径:
- 首先,你需要创建 路径起始点。
- 使用
beginPath()
开始路径 方法- 新建一条路径,生成之后,图形绘制命令 被指向到路径上 生成路径。
- 这个
beginPath()
开始路劲 方法,表示我要 新建路径了,但是这个时候 还没有绘制任何可见的路径- (就好像人画画,指明我下面的目标是 准备画个轮廓,但是还没有画,个人理解的)
- 第一条路径构造命令 一般是 :
moveTo()
移动到某点 函数, 在这个函数里 设置了 下笔的第一个起始点
- 使用
- 首先,你需要创建 路径起始点。
- ② 画路径: 然后你使用画图命令 去画出路径。
- 使用
stroke()
绘画 方法,lineTo()绘制直线 方法等.- 通过线条来 绘制图形轮廓。
- 使用
- ③ 闭合路径: 之后你把路径封闭。
- 使用
closePath()
闭合路径 方法 closePath()
闭合路径方法,不是必需的。- 当前点到开始点的直线: 闭合路径方法 会通过绘制一条 从当前点到开始点的直线 来闭合图形。
- 如果图形是已经闭合了的,即当前点为开始点,该函数什么也不做。
- 自动闭合方法: 当你调用
fill()
填充 函数时,所有没有闭合的形状 都会自动闭合,所以不需要调用closePath()
闭合路径 函数。 - 不自动闭合方法: 但是调用
stroke()
绘画 函数时 不会自动闭合,需要使用closePath()
闭合路径 方法。
- 使用
- ④ 描边和填充路径区域:
- 一旦路径生成,你就能通过 描边或填充路径区域 来渲染图形。
- 使用
fill()
填充 方法 - 通过 填充路径的 内容区域 生成实心的图形
- 总结: 描边/填充 = 实心图形
- ① 新建路径:
- 关联关系
- beginPath>moveTo>lineto>closePath>fill
- 画好轮廓 再填充: 开始创建路径>路径起始点>画直线>关闭路径>填充图形
- 示例1: 绘制一个三角形
<body>
<canvas id="myCanvas" height="200px" width="200px" style="border:1px solid green">
一个填充三角形
</canvas>
<script type="text/javascript">
var canvas=document.getElementById("myCanvas");//获取 canvas 图形画布元素
if(canvas.getContext){//判断canvas 的获取上下文方法是否存在
var ctx=canvas.getContext("2d");//获取上下文
ctx.beginPath();//准备开始绘制路径
ctx.moveTo(60,60);//路径的第一个 起始点
ctx.lineTo(60,120);//第一条线段的终点
ctx.lineTo(120,120);//第二条线段的终点
ctx.fill();//填充图形
}
</script>
</body>
- 测试
- 关联关系
- document>canvas>ctx>
- beginPath()
- moveTo()
- lineTo()
- fill()
- document>canvas>ctx>
- 图形的显示:
- 在没有
fill()
填充 函数之前,画布上都看不见图形,有了填充函数,才能看到.
- 在没有
- 图形的颜色
- 没有指定颜色,默认是黑色
- 关联关系
1.3.4 移动笔触
- 移动笔触
- ① 移动到指定点:
- 使用
moveTo(x, y)
移动到点 函数 - 将笔触 移动到指定的坐标点(x,y).
- 把画笔的笔尖 从一个点抬起,在另一个点落下.
- 使用
- ② 设置起点:
- 使用
moveTo()
移动到点 函数,设置起点。
- 使用
- ③ 不连续路径:
- 使用
moveTo()
移动到点 函数绘制一些 不连续的路径。 - 因为可以重新设置一个起点,所以可以不连续,画完一个,重新设置起点,画笔抬起,重新再画另一个.
- ▲总结: 移动笔触 =
moveTo(x, y)
移动到点 函数 = 移动到指定点 = 设置新起点 = 可以画不连续路径
- 使用
- ① 移动到指定点:
- 示例1:在填充三角形 旁边,重新画一个没有填充颜色的 三角形边框,就是描边三角形
<body>
<canvas id="myCanvas" height="200px" width="200px" style="border:1px solid green">
1个填充三角形图形,1个填充三角形图形
</canvas>
<script type="text/javascript">
var canvas=document.getElementById("myCanvas");//获取 canvas 图形画布元素
if(canvas.getContext){//判断canvas 的获取上下文方法是否存在
var ctx=canvas.getContext("2d");//获取上下文
//(1)填充三角形
ctx.beginPath();
ctx.moveTo(60,60);
ctx.lineTo(60,120);
ctx.lineTo(120,120);
ctx.fill();
//(2)描边三角形
//ctx.beginPath();可以省略新建路径,因为moveTo()可以设置新起点,画不连续的图形
ctx.moveTo(70,60);//新起点,描边三角形的新起点
ctx.lineTo(130,120);//第一个线段的终点
ctx.lineTo(130,60);//第二个线段的终点
ctx.closePath();//封闭路径
ctx.stroke(); //绘制图形
}
</script>
</body>
- 测试
- 图形显示:
- 使用
stroke()
绘制图形 方法,画布上才会出现一个描边三角形,不使用这个,前面指定的线段路径,都不会显示出来
- 使用
- 新建图形 = 新建起点:
- 第一个填充三角形画完之后,再画第二个图形,可以不使用
beginPath()
新建开始路径 方法 - 直接使用
moveTo()
移动到点 方法,新建一个 起点
- 第一个填充三角形画完之后,再画第二个图形,可以不使用
- 图形显示:
- 示例2: 把两个图形连在一起 :去掉第二个图形的新起点 = 删除 代码段
ctx.moveTo(70,60);
- 测试
- 上一个图形的最后一个终点 = 第二个图形的起点
- 上一个图形起点 = 第二个图形的终点
-
示例3: 如果前一个 填充三角形,使用了
closePath()
闭合路径 方法,那么填充三角形的 终点,就不是ctx.lineTo(120,120);
而是自己的开始点了 ,整个图形 就会发生变化(因为前一个图形的终点坐标,发生了变化)
-
测试:
- 1图形终点变化 = 2图形起始点变化:
- 因为前一个图形执行了路径封闭,导致它的终点变化了 = 第二个没有设置起始点的图形 的起始点也变化了
- 绘制路径变化: 描边三角形的绘制路径发生 变化
- 1图形终点变化 = 2图形起始点变化:
1.3.5 绘制 线段
- ⑴ 到指定点的线段:
- 绘制线段,需要用到的方法
lineTo()
画直线到点 函数。lineTo(x, y)
画直线到点 函数- 绘制一条 从当前位置到 指定坐标点(x,y)的直线。
- 直线和线段的区别 ?
- 线段,指的是两端都有端点,不可延伸。
- 直线,是两端都没有端点,都能延伸。
- 这里的画直线,一般都是指画一条 直的线段.
- (x,y) = 直线终点。
- 绘制线段,需要用到的方法
- ⑵ 线段的开始点 一般是什么 ?
- ① 前一个路径的结束点
- ② 新起点:
moveTo()
移动到点 函数 设置的新起点
1.3.6 绘制 圆弧和圆弧
- ⑴ 绘制 圆弧或者圆 的方法
- ① 使用
arc()
圆弧 方法。- 参数 6 个:
arc(x, y, radius, startAngle, endAngle, anticlockwise)
- arc [ɑrk] n.圆弧
- = 圆心坐标,半径,起始角,结束角,逆时针方向
- 圆心: 画一个以
(x,y)
为圆心坐标 - 半径: 以
radius
为半径的圆弧(圆) - 开始和结束: 从
startAngle
开始到endAngle
结束按照anticlockwise
逆时针生成.startAngle
以及endAngle
参数 用弧度 定义了 开始以及结束的弧度。都是以x 轴为基准。
- 方向: 逆时针方向(不设置这个, 默认为顺时针)来生成
- 参数
anticlockwise
为一个 布尔值。为true
时,是逆时针方向,false 为顺时针方向, 不设置这个属性值 = 默认顺时针.
- 参数
- 圆心: 画一个以
- 参数 6 个:
- ② 使用
arcTo()
圆弧到点 方法- 这个方法的实现还不好,暂时不使用.
arcTo(x1, y1, x2, y2, radius)
圆弧到点 方法- 根据给定的 控制点和半径 画一段圆弧,再 以直线 连接两个控制点。
- ① 使用
- ⑵
arc()
圆弧 方法的 开始角和结束角的单位:startAngle
和endAngle
arc()
圆弧 函数中 表示角的单位是 弧度,不是角度。- 角度与弧度的 js 表达式: 弧度=(Math.PI/180)*角度。
- 角的单位 = 弧度 = (π/180°)x角度 (rad), (弧度 = 角度除以180,乘以 π)
- 弧度单位 rad, 常省略. 角度单位 °,不省略.
- 角度 = 弧度/π x 180 ° (弧度除以π,乘以 180)
- 弧度 π = > π/π x 180° = 180°
- (π = 180°,2π = 360°)
- 弧度和角度值的形式: 弧度 = …π, 角度 = …°
- 弧度 π = > π/π x 180° = 180°
- ⑶ 开始角结束角 在坐标系上的弧度
- 注意,y 轴的方向,和平时的相反,因为 网页中坐标,一般以 左上角为 原点.(这里把原点作为圆心的位置,建立坐标系.)
- Math.PI = π = 180° (在圆心的正左方)
- 0.5*Math.PI = 0.5π = 0.5x180° = 90°(在圆心的正下方)
- 1.5*Math.PI = 1.5π = 1.5x180° = 270°(在圆心的正上方)
- 2*Math.PI = 2π = 2x180° = 和起始点重合 (一个圆)
- 示例1: 逆时针 画一段圆弧,顺时针 画一个圆弧,开始角和结束角都一样.
<body>
<canvas id="myCanvas" height="200px" width="200px" style="border:1px solid green">
两条圆弧
</canvas>
<script type="text/javascript">
var canvas=document.getElementById("myCanvas");//获取 canvas 图形画布元素
if(canvas.getContext){//判断canvas 的获取上下文方法是否存在
var ctx=canvas.getContext("2d");//获取上下文
//(1) 逆时针画一段 圆弧
ctx.beginPath();
ctx.arc(50,50,40,0,Math.PI*0.5,true);
ctx.stroke();
//(2) 顺时针画一段圆弧
ctx.beginPath();
ctx.arc(150,50,40,0,Math.PI*0.5,false);
ctx.stroke();
}
</script>
</body>
2 条圆弧 | 开始角和结束角 (绘图分析) |
![]() | ![]() |
- 测试
- 开始角和结束角的位置: 开始角和结束角的位置相同, 绘画方向不同 = 图形不同 (除了圆)
- ( 从开始位置 向结束位置, 逆时针和顺时针 画圆弧)
- 开始角和结束角的表示: 除了 0 ,其他的用 数字*Math.PI 表示.
- Math 和 PI 的写法: Math 首字母大写,PI 全大写. 只能这样写,不能改成 其他形式,会无法识别.
- 方向: 逆时针,anticlockwise = true,顺时针,anticlockwise = false
- 关联关系
- document> canvas> ctx >
- beginPath()
- arc()
- stroke()
- 没有 设置起点和闭合路径:
arc()
画圆弧 方法,不需要和下面的两个方法 搭配使用:moveTo()
创建起始点 方法,closePath()
关闭路径 方法- 没有使用
moveTo()
移动到点 方法 设置起点,因为开始角和半径结合 指明了 开始的位置. - 没有使用
closePath()
闭合路径 方法 设置闭合路径,因为 结束角和半径结合 指明了结束位置,圆弧 也不需要 闭合路径.(圆弧 不是封闭图形)
- 没有使用
- document> canvas> ctx >
- 示例2: 画一个描边圆和一个填充圆
- (对于圆来说,绘制方向 不影响 图形形状,因为圆是 封闭了的图形,都是一样的形状)
<body>
<canvas id="myCanvas" height="200px" width="200px" style="border:1px solid green">
1个描边圆图形,1个填充圆图形
</canvas>
<script type="text/javascript">
var canvas=document.getElementById("myCanvas");//获取 canvas 图形画布元素
if(canvas.getContext){//判断canvas 的获取上下文方法是否存在
var ctx=canvas.getContext("2d");//获取上下文
//(1) 绘制一个描边圆,没有填充色
ctx.beginPath();
ctx.arc(50,50,40,0,2*Math.PI,true);
ctx.stroke();
//(2) 绘制一个填充圆,有填充色
ctx.beginPath();
ctx.arc(150,50,40,0,2*Math.PI,false);
ctx.fill();
}
</script>
</body>
- 测试
- 描边圆: 使用
stroke()
绘画 方法 - 填充圆: 使用
fill()
填充 方法- 填充方法,默认的颜色是 黑色
- 新建路径的必要性: 画完一段圆弧,开始画另一段圆弧,一定要用
beginPath()
新建路径 方法,新建路径,否则可能会发生 圆弧之间的关联: 颜色覆盖,或者路径连接在一起.
- 描边圆: 使用
第二条圆弧 不使用`beginPath()`新建路径 方法 | 第二条圆弧 不使用`beginPath()`新建路径 方法 |
![]() | ![]() |
- 超出画布的部分: 会被裁剪,看不见
- ▶ 知识拓展:圆,扇形,圆弧 和弧度制
- ① 圆
- 圆 = 在同一平面内,到定点的距离 等于定长的 点的集合 = 封闭图形
- 图形表示 ⊙ = 这个定点是 圆的圆心
- 圆心角 = 顶点在圆心上的角
- 圆周率 = π = 圆周/直径 = 圆周长度与圆的直径长度的比值 = 圆周率。
- 它是一个无限不循环小数 ≈3.1415926535…
- 圆的周长:C = 2πr = πd
- 圆周长的一半 c=πr
- 半圆的周长: c=πr+2r
- 圆的面积计算公式: π r²
- ② 扇形
- 扇形 = 一条弧和经过这条弧两端的两条半径 所围成的图形 = 封闭图形
- 扇形(符号:⌔),是圆的一部分 = 两个半径和和一段弧围成
- θ = 扇形的角弧度, r = 圆的半径,L = 扇形的弧长
- ③ 圆弧 Arc
- 弧形 = 圆上任意两点间的部分 = 简称弧 = 不是封闭图形
- 弧的表示: 用符号“⌒”表示
- 圆弧读法: 以A、B为端点的圆弧 读做 圆弧AB或弧AB。
- 优弧和劣弧: 大于半圆的弧 = 优弧,小于半圆的弧 = 劣弧。
- 圆弧的度数: = 这段圆弧 所对圆心角的度数
- ④ 弧度制
- 弧度制 = 弧长/半径 = 用弧长与半径之比度量 对应圆心角角度的方式,叫做 弧度制
- 弧度的单位: 用符号 rad 表示,读作弧度。
- 1弧度的角 = 等于半径长的圆弧 所对的圆心角。
- 弧度数,与半径无关: 由于圆弧长短与圆半径之比,不因为圆的大小而改变,所以弧度数也是一个与圆的半径无关的量。
- 弧度单位: 角度以弧度给出时,通常不写弧度单位。
- 1°=π/180 (rad)
- 弧度 = (π/180)x角度 (rad), 弧度单位 rad 常省略.
- ① 圆
1.3.7 二次 贝塞尔曲线 和三次 贝塞尔曲线
1.3.8 绘制 矩形路径
- ⑴
rect(x, y, width, height)
矩形路径 方法和下方 3种矩形方法的区别- ① 绘制的是图形: = 能直接显示出 图形形状 (不需要搭配
stroke()
绘制方法或者fill()
填充方法)fillRect(x, y, width, height)
填充矩形 方法strokeRect(x, y, width, height)
描边矩形方法clearRect(x, y, width, height)
清除矩形区域 方法
- ② 绘制的是路径: = 不是图形,不能直接在画布上显示图形,
- 要显示图形, 需要搭配
stroke()
绘制方法或者fill()
填充方法,才会在画布上 显示出图形 - 关联关系
rect()
+stroke()
=strokeRect()
- 矩形路径 + 绘制 = 绘制描边矩形
fill()
不会填充strokeRect()
,这两个都是绘制图形方法, 但会 填充路径绘制的 路径描边矩形rect()
+stroke()
- 矩形路径 + 绘制 = 绘制描边矩形
rect()
+fill()
=fillRect()
- 矩形路径 + 填充 = 绘制 填充矩形
- 要显示图形, 需要搭配
- ① 绘制的是图形: = 能直接显示出 图形形状 (不需要搭配
- ⑵
rect(x, y, width, height)
的参数- ,绘制一个左上角坐标为(x,y),宽高为width以及height的矩形。
- ⑶
rect()
左上角坐标(x,y) 和起始点 关系- 绘制矩形路径时,左上角坐标(x,y) 始终是 相对于 原点(0,0) 的坐标 =
moveTo()
自动设置 坐标参数(0,0)。当前笔触 自动重置回 原点。 - (x,y) : = 矩形路径的左上角 是相对于原点(0,0)的坐标。
- width 和height 设置: = 矩形的宽高尺寸.
- 绘制矩形路径时,左上角坐标(x,y) 始终是 相对于 原点(0,0) 的坐标 =
- 示例1: 用两种方法 绘制描边矩形和填充矩形
<body>
<canvas id="myCanvas" height="200px" width="200px" style="border:1px solid green">
2个描边矩形和2个填充矩形
</canvas>
<script type="text/javascript">
var canvas=document.getElementById("myCanvas");//获取 canvas 图形画布元素
if(canvas.getContext){//判断canvas 的获取上下文方法是否存在
var ctx=canvas.getContext("2d");//获取上下文
ctx.rect(25,25,50,50);//第二条线段的终点
ctx.stroke();//填充图形
ctx.strokeRect(100,25,50,50);
ctx.beginPath();//新建路径,防止fill()对上一条 用路径绘制的矩形 进行填充
ctx.rect(25,90,50,50);//第二条线段的终点
ctx.fill();//填充图形
ctx.fillRect(100,90,50,50);
}
</script>
</body>
- 测试
- 不同的方法,绘制出相同的图形
1.3.9 Path2D 对象
- 绘制图形的一般方式: 路径 + 绘画命令
- 可以使用一系列的路径和绘画命令来 把对象“画”在画布上。
- 为什么使用 Path2D 对象 ?
- 为了简化代码
- 为了能 快速地回顾路径
- 因为 Path2D 对象,可以用来 缓存 绘制路径命令 (缓存下来,方便调用)
- Path2D 对象的浏览器支持
- 可以在 较新版本的浏览器中使用
- 如何产生一个 Path2D对象 ?
- 使用
Path2D()
2D路径 方法 = 会返回一个(新初始化的)Path2D对象
- 使用
Path2D()
2D路径 方法的变量- 变量 = 某一个路径——创建一个它的副本
- 变量 = 一个包含SVG path 数据的字符串
new Path2D(); // 空的Path对象
new Path2D(path); // 克隆Path对象
new Path2D(d); // 从SVG建立Path对象
- 配合的路径方法: 可以在
Path2D
中使用路径方法 有哪些 ?- 所有的路径方法,比如
moveTo, rect, arc
或quadraticCurveTo
等
- 所有的路径方法,比如
- 路径结合: 用什么将 path 路径 结合起来 ?
addPath()
添加路径 方法void path.addPath(path [, transform]);
addPath()
添加路径 方法的参数- path : 需要添加的 Path2D 路径。
- transform: 可选 SVGMatrix 作为新增路径的 变换矩阵。
- 参考文档:
- 示例1: 一个描边正方形,一个填充圆形
<body>
<canvas id="myCanvas" height="200px" width="200px" style="border:1px solid green">
一个描边正方形,一个填充圆形
</canvas>
<script type="text/javascript">
var canvas = document.getElementById('myCanvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
var rectangle = new Path2D();//创建一个Path2D 路径对象
rectangle.rect(10, 10, 50, 50);//用路径对象 缓存 1个矩形路径 = 2D路径对象调用路径方法
var circle = new Path2D();//创建一个Path2D 路径对象
circle.arc(100, 35, 25, 0, 2 * Math.PI);//用路径对象,缓存 1个圆形路径
ctx.stroke(rectangle);//绘制矩形 = 绘制方法中,变量 = 缓存了 矩形路径的 2D路径对象
ctx.fill(circle);//填充圆形 = 填充方法中,变量 = 缓存了 圆形路径的 2D路径对象
}
</script>
</body>
- 测试
- 关联关系
- 缓存路径: = 2D对象 调用路径方法
new Path2D()
>rectangle>rect()
new Path2D()
>circle>arc()
- 路径,都被存为 Path2D 对象
- 绘制图形: ctx.绘画命令(参数变量 = 2D对象)
- document>canvas>ctx>
stroke() | fill()
- 绘画命令:
stroke() | fill()
- document>canvas>ctx>
- 缓存路径: = 2D对象 调用路径方法
- 关联关系
1.3.10 使用 SVG paths
- SVG
- abbr. 可伸缩向量图形(Scalable Vector Graphics);
- 路径重用: canvas 路径 = SVG path data
- 可以使用 SVG path data 来初始化 canvas上的路径。
- 因此,获取路径时 可以 (以SVG或canvas的方式) 来重用它们。
- 使用路径: 使用 SVG创建的路径对象 = 绘画命名的参数变量.
- 可以使用 SVG path data 来初始化 canvas上的路径。
- 示例1: 这条路径将 先移动到点 (M10 10) ,再水平移动80个单位(h 80),再下移80个单位 (v 80),再左移 80个单位 (h -80),最后回到起点处 (z)。
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
let p = new Path2D('M10 10 h 80 v 80 h -80 Z');
ctx.fill(p);
- 测试
- 创建的是一个矩形路径,再进行填充.
1.4 给图形 添加样式和颜色
1.4.1 给图形添加颜色
- ⑴ 给图形上色的方法: = 2个属性
- ① 填充颜色: 使用
fillStyle
填充样式 属性fillStyle = color
- 设置图形的 填充颜色。
- ② 轮廓线条颜色: 使用
strokeStyle
轮廓样式属性strokeStyle = color
- 设置图形 轮廓的颜色。
color
颜色的取值 =- CSS 颜色值的字符串
- 渐变对象
- 图案对象
color
颜色值的格式- 颜色名称
- 十六进制颜色值
- rgb()
- rgba()
- ① 填充颜色: 使用
// 这些 fillStyle 的值均为 '橙色'
ctx.fillStyle = "orange";
ctx.fillStyle = "#FFA500";
ctx.fillStyle = "rgb(255,165,0)";
ctx.fillStyle = "rgba(255,165,0,1)";
- ⑵ 默认颜色: 默认情况下,线条和填充颜色 是什么颜色 ?
- 是黑色(CSS 颜色值 #000000)。
- ⑶ 设置不同颜色
- 一旦设置了
strokeStyle
轮廓线条样式 或者fillStyle
填充样式的值- 这个颜色 = 后面 所有 新绘制的图形 的默认颜色。
- 如果要给之后的每个图形 设置不同的颜色,该怎样 ?
- 重新设置 颜色样式属性: 需要重新设置
fillStyle
或strokeStyle
填充样式和轮廓线条样式 的值。
- 重新设置 颜色样式属性: 需要重新设置
- 总结:不同的颜色,要用
fillStyle
或strokeStyle
填充样式和轮廓线条样式 属性 重新设置.
- 一旦设置了
- 示例: 一个 红色描边正方形,一个 绿色填充圆形
<body>
<canvas id="myCanvas" height="200px" width="200px" style="border:1px solid green">
一个描边正方形,一个填充圆形
</canvas>
<script type="text/javascript">
var canvas = document.getElementById('myCanvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
var rectangle = new Path2D();//创建一个Path2D 路径对象
rectangle.rect(10, 10, 50, 50);//用路径对象 缓存 1个矩形路径 = 2D路径对象调用路径方法
var circle = new Path2D();//创建一个Path2D 路径对象
circle.arc(100, 35, 25, 0, 2 * Math.PI);//用路径对象,缓存 1个圆形路径
ctx.strokeStyle= "red";//添加轮廓线条颜色
ctx.stroke(rectangle);//绘制矩形 = 绘制方法中,变量 = 缓存了 矩形路径的 2D路径对象
ctx.fillStyle = "green";//添加填充颜色
ctx.fill(circle);//填充圆形 = 填充方法中,变量 = 缓存了 圆形路径的 2D路径对象
}
</script>
</body>
- 测试
- 属性和绘画命令的搭配使用
fillStyle
填充颜色,不会给stroke()
绘制命令的图形 添加颜色- ×
fillStyle
不搭配stroke()
, - √
fillStyle
搭配fill()
- ×
strokeStyle
轮廓线条颜色 ,也不会给fill()
填充命令的图形,添加轮廓颜色- ×
Stroketyle
不搭配fill()
, - √
Stroketyle
搭配stroke()
- ×
- 属性和绘画命令的搭配使用
1.4.2 透明度: 设置图形透明度
-
⑴ 设置图形透明度的方法: 如何设置有一定透明度的图形 ?
- 透明属性: 使用
globalAlpha
全局透明度 属性 - 透明颜色: 使用
rgba()
透明颜色 (作为轮廓或填充的样式。strokeStyle,fillStyle
)
- 透明属性: 使用
-
⑵
globalAlpha
全局透明度 属性- ①
globalAlpha
全局透明度 属性的使用范围: 语句下方 所有图形globalAlpha = transparencyValue
- 设置 canvas 里 所有图形的透明度所有图形 = 设置透明度后,语句下方的所有图形)
- 适合绘制大量 相同透明度的图形。
- ②
globalAlpha
全局透明度 有效的值范围是:0.0~1.0- 0.0 (看不见, 完全透明)
- 1.0(完全不透明),默认是 1.0。
- ①
-
rgba()
透明颜色 方法的使用
// 指定透明颜色,用于描边和填充样式
ctx.strokeStyle = "rgba(255,0,0,0.5)";
ctx.fillStyle = "rgba(255,0,0,0.5)";
- 示例1: 1个 描边正方形,2个填充圆形
<body>
<canvas id="myCanvas" height="200px" width="200px" style="border:1px solid green">
一个描边正方形,二个填充圆形
</canvas>
<script type="text/javascript">
var canvas = document.getElementById('myCanvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
ctx.fillStyle= "red";//添加轮廓线条颜色
ctx.fillRect(10, 10, 50, 50);//用路径对象 缓存 1个矩形路径 = 2D路径对象调用路径方法
ctx.arc(80,80,40,2*Math.PI,false);
ctx.fill();
ctx.globalAlpha=0.5;//设置透明度为0.5,仅对下方的圆形有作用,对上面的两个图形没作用
ctx.beginPath();
ctx.arc(100,100,40,2*Math.PI,false);
ctx.fill();
}
</script>
</body>
- 测试
globalAlpha
全局透明度属性作用范围: 只作用于 设置透明度后, 语句下方的图形- 不会对 设置透明度属性之前的图形,进行透明操作
- 示例2: 让3个圆形都透明
<body>
<canvas id="myCanvas" height="200px" width="200px" style="border:1px solid green">
一个描边正方形,一个填充带透明圆形
</canvas>
<script type="text/javascript">
var canvas = document.getElementById('myCanvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
ctx.fillStyle= "red";//添加轮廓线条颜色
ctx.fillRect(10, 10, 50, 50);//用路径对象 缓存 1个矩形路径 = 2D路径对象调用路径方法
ctx.beginPath();
ctx.fillStyle= "green"//设置填充颜色
ctx.globalAlpha=0.5;//设置透明度
ctx.arc(80,80,40,2*Math.PI,false);
ctx.fill();
ctx.beginPath();//新建路径
ctx.arc(140,80,40,2*Math.PI,false); //绘制圆形路径
ctx.fill();//填充颜色
ctx.beginPath();
ctx.arc(110,130,40,2*Math.PI,false);
ctx.fill();
}
</script>
</body>
- 测试
- 重叠和透明: 重叠部分的透明度 会降低,越重叠,越不透明(本来0.2,重叠一次,0.2+0.2=0.4 ,数字越大,越不透明了
- (想要相反效果,可以把图形颜色设置为白色,再用其他图形 设置其他的背景色,白色越重叠,越不透明,越接近白色,就看上去像是越来越透明了)
- 透明度和填充色的 语句位置: 只设置了一次透明度和填充颜色,下方三个图形 都有相同填充色和透明度了
- 重叠和透明: 重叠部分的透明度 会降低,越重叠,越不透明(本来0.2,重叠一次,0.2+0.2=0.4 ,数字越大,越不透明了
- 示例3: 使用透明颜色,作为填充颜色,不设置专门的透明属性
ctx.fillStyle= "rgba(0,150,0,0.8)"
- 知识拓展
- ⑴ RGB颜色
- 什么是 RGB颜色 ?
- 颜色标准: RGB 色彩模式是工业界的一种颜色标准
- 产色原理: 颜色发光+ 颜色通道的 (变化+叠加)
- 通过对红( R)、绿(G)、蓝(B) 三个颜色通道的变化 以及它们相互之间的叠加 来得到各式各样的颜色的.
- RGB 是从颜色发光的原理来设计定的,
- 色彩和亮度: = 色彩相混合+亮度相叠加
- 像有红、绿、蓝三盏灯,当它们的光 相互叠合的时候,色彩相混,而亮度 却等于 三者亮度之总和,越混合亮度越高。
- 亮度: 每种色各分为256 阶亮度
- 在0时“灯”最弱——是关掉的,而在 255 时“灯”最亮
- 灰色: 当三色 亮度数值相同时,产生 不同灰度值的灰色调
- 三色亮度都为 0 时,是最暗的黑色调—
- 三色亮 度都为 255 时,是最亮的白色调
- 什么是 RGB颜色 ?
- ⑵
rgb()
和rgba()
的区别rgba()
多了一个a,就是透明度.- 灰色1:
rgba(100,100,100,0.8)
,越暗,灰色越浓,越接近黑色
- 灰色2:
rgba(200,200,200,0.8)
,越亮,灰色越淡,越接近白色
1.4.3 线型:线的样式
- 设置 线的样式的属性和方法
- ① 线条宽度:(= 线条粗细)
lineWidth = value
- 设置线条宽度。
- 当前 绘线的粗细。
- 线宽属性值 必须为正数。线宽 默认值是1.0
- ② 线条末端样式:
lineCap = type
- 设置线条末端样式。
- ③ 线条交接:
lineJoin = type
- 设定线条与线条间接合处的样式。
- ④ 线条交接 斜接长度:
miterLimit = value
- 限制 当两条线相交时 交接处最大长度;所谓交接处 长度(斜接长度)是指线条交接处内角顶点到外角顶点的长度。
- ⑤ 返回虚线样式:
getLineDash() 方法
- 返回一个 包含当前虚线样式,长度为 非负偶数的数组。
- ⑥ 设置虚线样式:
setLineDash(segments) 方法
- 设置当前虚线样式。
- ⑦ 虚线样式 起始偏移量:
lineDashOffset = value
- 设置 虚线样式的 起始偏移量。
- ① 线条宽度:(= 线条粗细)
-
⑴ 设置线宽(线的粗细) :
lineWidth
线宽属性- 线宽的定位: 路径的中心 到两边的粗细。在路径绘线的两边 各绘制 线宽的一半。
- 画布的坐标和像素:
- 画布坐标,不和像素直接对应
- 画布图形坐标和像素边缘:
- 用网格 来代表 canvas 的坐标格,每一格 对应屏幕上一个像素点。
- 图形的坐标,在像素的边缘上 (一个正方形的网格 = 一个像素,图形的坐标 = 在像素网格的右边框上),不是和整个像素直接 对应重合的.
- 示例 绘制填充了 (2,1) 至 (5,5) 的矩形,整个区域的边界 刚好落在像素边缘上,这样得到的矩形 有着清晰的边缘.
- 用网格 来代表 canvas 的坐标格,每一格 对应屏幕上一个像素点。
-
线宽的近似渲染问题 (准确的线条)
- 绘制一条从
(3,1)
到(3,5)
,宽度是1.0
的线条 - 线宽的近似渲染分析
- 实际填充区域(= 深蓝色部分)延伸至 路径两旁 各一半像素(0.5+0.5=1.0)。
- 晕染部分:(= 浅蓝色区域)而这两个’'半个像素",没有到像素的边缘, 又会 以近似的方式 进行渲染,以更浅的颜色到达像素的边缘
- 整个线宽 = 深蓝色+浅蓝色 = 以(实际颜色 一半色调)的颜色 来填充整个区域(浅蓝和深蓝的部分)
- 渲染颜色和线宽
- 渲染颜色: 无论深蓝色,还是浅蓝色,都不是原来的颜色,整个区域,只有真实颜色的一半色调,不是实色,看上去有点模糊
- 渲染线宽: 因为渲染,线宽增大了,不只是
1.0
了,不是实线,看上去有点模糊
- 端点的半渲染:
- 端点渲染: = 当
y
坐标不在网格线上- 在这个竖线的例子中,其
Y
坐标刚好落在网格线上, y
坐标不在网格线上,端点上 同样会出现 半渲染的像素点
- 在这个竖线的例子中,其
- 端点渲染: = 当
- 端点渲染受 线端点类型 影响:
- 这种行为的表现 取决于当前的
lineCap
风格,它默认为butt
; - 通过将
lineCap
线末端 样式 设置为square
正方形,来得到 与奇数宽度线 的半像素坐标 (整数+0.5)相一致的笔画,这样,端点轮廓的外边框 将被自动扩展 以完全覆盖 整个像素格)。
- 这种行为的表现 取决于当前的
- 纠正渲染线宽问题: = 让线宽的边缘 可以到达 像素的边缘
- 绘制从
(3.5,1)
到(3.5,5)
的线条,线宽边缘 正好落在 像素边界,填充出来就是准确的宽为 1.0 的线条。
- 绘制从
- 绘制一条从
- 示例1: 绘制6条线条,验证线条的渲染问题
<body>
<canvas id="myCanvas" height="200px" width="200px" style="border:1px solid green">
6条线条
</canvas>
<script type="text/javascript">
var canvas = document.getElementById('myCanvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
//线宽是奇数,横坐标是整数,出现晕染
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(10,5);
ctx.lineTo(10,140);
ctx.stroke();
//线宽是奇数.横坐标带小数 .5,没出现晕染
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(20.5,5);
ctx.lineTo(20.5,140);
ctx.stroke();
//线宽是偶数,横坐标是整数,没出现晕染
ctx.lineWidth = 4;
ctx.beginPath();
ctx.moveTo( 40,5);
ctx.lineTo(40,140);
ctx.stroke();
//线宽是奇数,横坐标带小数 .5.出现晕染
ctx.lineWidth = 4;
ctx.beginPath();
ctx.moveTo(50.5,5);
ctx.lineTo(50.5,140);
ctx.stroke();
//线宽是奇数,横坐标是整数,出现晕染
ctx.lineWidth = 5;
ctx.beginPath();
ctx.moveTo(70,5);
ctx.lineTo(70,140);
ctx.stroke();
//线宽是奇数.横坐标带小数 .5,没出现晕染
ctx.lineWidth = 5;
ctx.beginPath();
ctx.moveTo(80.5,5);
ctx.lineTo(80.5,140);
ctx.stroke();
}
</script>
</body>
- 测试
- 出现晕染
- 线宽是奇数,横坐标是 整数 (线宽边缘 ≠ 像素边界)
- 线宽是偶数,横坐标是 半像素坐标 如(整数+0.5) (线宽边缘 ≠ 像素边界)
- 不出现晕染: 奇数线宽+半像素坐标 = 不晕染 的实际线宽和颜色
- 线宽是奇数,横坐标是 半像素坐标 (整数+0.5= 小数部分是5,线宽边缘 = 像素边界)
- 线宽是偶数,横坐标是 整数 (线宽边缘 = 像素边界)
- 出现晕染
- ⑵ 设置 线段端点样式:
lineCap
线盖类型 属性(线段端点 = 线盖)lineCap
线盖 属性的3个属性值lineCap = ['butt','round','square'];
- 平齐端点 ( 与起始点和终点平齐) :
butt
(烟头一样的效果) - 圆形端点:
round
两端各一个半圆,直径= 线宽 - 正方形端点:
square
两端各半个正方形, 边长 = 线宽
- 示例1: 创建2条水平参考线,3条 带线宽和线帽 的竖线
<body>
<canvas id="myCanvas" height="200px" width="200px" style="border:1px solid green">
2条水平参考线,3条 带线宽和线帽 的竖线
</canvas>
<script type="text/javascript">
var canvas = document.getElementById('myCanvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
//创建一个数组,存储线帽lineCap 属性的属性值[],方便一个一个调用
var lineCap = ['butt','round','square'];
//创建2条线段,作为参照
ctx.strokeStyle = 'red';
ctx.beginPath();
//线段1
ctx.moveTo(10,10);//起始点
ctx.lineTo(140,10);//终点
//线段2
ctx.moveTo(10,140);//起始点
ctx.lineTo(140,140);//终点
ctx.stroke();//绘制线条
//3个设置了线宽的线条
ctx.strokeStyle = 'black';//设置绘线的颜色
for (var i=0;i<lineCap.length;i++){//以线帽lineCap 属性值的个数 为长度,进行循环
ctx.lineWidth = 15;//设置线宽
ctx.lineCap = lineCap[i];//设置线帽样式: 循环依次调用 线帽lineCap[]数组的值[0],[1],[2],里面存储的是线帽的类型
//3个线条
ctx.beginPath();//新建路径
ctx.moveTo(25+i*50,10);//起始点,横坐标x,每次 比上次增加50(0*50=0,1*50=50,2*50=100)
ctx.lineTo(25+i*50,140);//终点,横坐标x,每次 比上次增加50(0*50=0,1*50=50,2*50=100)
ctx.stroke();//绘制线条
}
}
</script>
</body>
- 测试
- 数组: 提前存储 需要用到的值
- for 循环语句: 依次调用(结合数组) + 循环增加 等值(结合变量i)
for(var i=0;i<lineCap.length;i++){}
- for(变量定义和起始值;条件;自增)
- ⑶ 设置 线段交接处(拐角) 类型:
lineJoin
线拐角类型 属性lineJoin
线拐角类型 属性的属性值- 3个值:
round, bevel and miter
。线交接处 类型 默认值是miter
。- ① 扇形拐角:
round
- 扇形: 填充一个额外的扇形(圆心在相连部分末端的),绘制 拐角的形状。
- 圆角的半径 = 线段的宽度。
- ② 三角形拐角:(三角的底部的线段):
bevel
- (在相连部分的末端) 填充一个额外的 以三角形为底的区域
- 每个部分 都有 各自独立的矩形拐角。
- ③ 菱形拐角(菱形拐角的尖角):
miter
['maɪtə]- 延伸相连部分的外边缘,使其相交于一点,形成一个额外的 菱形区域。
这个设置可以通过miterLimit
属性看到效果。
- 延伸相连部分的外边缘,使其相交于一点,形成一个额外的 菱形区域。
- ① 扇形拐角:
- 3个属性值的拐角形状: “
miter
” >"round
" ) “bevel
” ] lineJoin
线交接处类型 属性使用范围- 设置线交接处类型:连接处 不平滑,有交接的斜角
- 不设置交接处类型: 注意:如果 2个相连部分在同一方向(平滑的,没有交接的斜角),那么
lineJoin
不会产生任何效果,因为在那种情况下 不会出现连接区域。
- 示例1: 画3条折线,设置不同的 线拐角样式
<body>
<canvas id="myCanvas" height="200px" width="200px" style="border:1px solid green">
三条折线
</canvas>
<script type="text/javascript">
var canvas = document.getElementById('myCanvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
var lineJoin=["round","bevel","miter"];//设置 线拐角类型的集合 数组,要加引号,作为属性值 需要引号
ctx.lineWidth = 10;//设置线宽
for(var i=0;i<lineJoin.length;i++){
ctx.beginPath();//新建路径,少这个语句,三个路径就成1个路径了,所有拐角样式 会被最后设置的那个样式覆盖
ctx.lineJoin = lineJoin[i];//调用直接存储的属性值
//只改y纵坐标,每次向下 平移40
ctx.moveTo(20,20+i*40);
ctx.lineTo(50,60+i*40);
ctx.lineTo(80,20+i*40);
ctx.lineTo(110,60+i*40);
ctx.stroke();
}
}
</script>
</body>
- 测试
- 默认是
miter
菱形拐角的尖角
- 默认是
- ⑷ 设置 尖角限制:
miterLimit
尖角限制 属性 = 外延交点与连接点 的最大距离- 尖角限制 使用条件: 必须配合
lineJoin="miter"
使用- 只有当
lineJoin
线拐角 显示为 “>” 时,miterLimit
尖角限制 才有效。
- 只有当
- 使用原因和效果: 边角的 角度越小,斜接长度 就会越大。
- 原因: 为了避免 斜接长度过长(尖角太长),使用
miterLimit
尖角限制 属性。 - 效果: 如果斜接长度超过
miterLimit
尖角限制的值,边角会以lineJoin="bevel"
的 " ] " 类型来显示(类似 切去多余的尖角). - 总结:尖角太长,切去尖角.
- 外延交点: 线段的外侧边缘 会延伸交汇于 一点上,设定 外延交点与连接点的最大距离,如果交点距离 大于此值,连接效果 会变成了 bevel。
- 原因: 为了避免 斜接长度过长(尖角太长),使用
- 尖角限制 使用条件: 必须配合
<script type="text/javascript">
var canvas = document.getElementById('myCanvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
ctx.lineWidth = 10;//设置线宽
ctx.beginPath();
ctx.lineJoin = "miter";//尖角拐角类型 >
ctx.miterLimit = 2; //尖角限制
ctx.moveTo(20,20);
ctx.lineTo(50,80);
ctx.lineTo(80,20);
ctx.lineTo(110,80);
ctx.stroke();
}
</script>
没有设置尖角限制(尖角) | 设置了 尖角限制(尖角,变 bevel ] ) |
![]() | ![]() |
- ⑸ 设置虚线样式:
setLineDash([a,b...])
- 线段和间隙的交替:
setLineDash()
方法接受一个 数组,来指定 虚线线段与空白间隙的交替; - 起始偏移量: lineDashOffset 属性 设置 起始偏移量.
void ctx.setLineDash(segments);
- 参数: segments = 一个Array数组。[a,b,…]
- 奇数元素: = 虚线线段的长度(1,3,5…)
- 偶数元素: = 空白间距 (2,4,6…)
- 交替 绘制 线段长度和间距长度 的数字。 (坐标空间单位)
- 奇数数目: 如果数组元素的数量 是奇数, 数组的元素 会被复制并重复成 偶数。
- [5, 15, 25] = [5, 15, 25, 5, 15, 25]。6个数字 = 3个线段 +3个间隙
- 参数: segments = 一个Array数组。[a,b,…]
- 线段和间隙的交替:
- 示例1:画一条虚线
setLineDash([20,10]);
<body>
<canvas id="myCanvas" height="200px" width="200px" style="border:1px solid green">
一条虚线
</canvas>
<script type="text/javascript">
var canvas = document.getElementById('myCanvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
ctx.lineWidth = 10;//设置线宽
ctx.setLineDash([20,10]);//设置虚线长度和虚线之间的空白间隙
ctx.beginPath();//新建路径
ctx.moveTo(20,20);//起始点
ctx.lineTo(150,20);//终点
ctx.stroke();//绘线
}
</script>
</body>
2个数组元素,生成的虚线 | 虚线分析 |
![]() | ![]() |
- 测试
setLineDash([20,10])
- 数组[20,10]
- 奇数 = 线段长度 = 20
- 偶数 = 空白间隙 = 10
- 虚线单位: 20线段长度+ 10空白间隙 = 1个虚线单位
- 整个虚线,就是在 不停地 重复这个虚线单位
- 示例2:
ctx.setLineDash([15,10,5]);
//设置虚线长度和虚线之间的空白间隙 - 数组元素[15,10,5],3个元素,奇数,会重复成 [15,10,5,15,10,5]
- 奇数 = 3个线段的长度 (1,3,5)
- 偶数 = 3个空白间隙的长度 (2,4,6)
- 虚线单位:这3个线段+3个间隙 = 1个虚线单位,整个虚线的 就是 重复这个虚线单位
- 示例3:
ctx.setLineDash([15]);
[15] = [15,15] ,奇数,自动重复成偶数- 数组只有一个值 = 自重复 = (虚线线段长度 = 空白间隙)
- 数组只有一个值 = 自重复 = (虚线线段长度 = 空白间隙)
- ⑹ 设置 虚线偏移量:
lineDashOffset
虚线偏移量 属性ctx.lineDashOffset = value;
- 属性值: value = 偏移量 = float精度的数字。 初始值为 0.0。
- 属性值的正负和偏移方向
- 正: 向起始点方向偏移
- 负: 向终点方向偏移
- 示例1:
ctx.lineDashOffset = 2;
(2,4,6,8…)不断增大偏移量,看它的 偏移的移动方向变化
- 示例2:1条竖线,互换 起始点和终点,设置偏移量,看偏移方向
- 偏移量逐步增加(2,4,6,8…)
<script type="text/javascript">
var canvas = document.getElementById('myCanvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
ctx.lineWidth = 5;//设置线宽
ctx.setLineDash([20,10]);//设置虚线长度和虚线之间的空白间隙
ctx.lineDashOffset = 0;
ctx.beginPath();//新建路径
ctx.moveTo(20,150);//起始点
ctx.lineTo(20,10);//终点
ctx.stroke();//绘线
}
</script>
起始点 在下方 | 偏移量为正: 向下移动 |
![]() | ![]() |
起始点 在下方 | 偏移量为正:向上移动 |
![]() | ![]() |
- 测试
- 横线同理
- 正: 向起始点方向偏移
- 负: 向终点方向偏移
- 横线同理
- 示例3: 描边矩形的移动方向
ctx.lineDashOffset = 2;
(2,4,6,8…)不断增大偏移量- 偏移量为正: 逆时针偏移
- 偏移量为负: 顺时针偏移
<script type="text/javascript">
var canvas = document.getElementById('myCanvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
ctx.lineWidth = 3;//设置线宽
ctx.setLineDash([15,10]);//设置虚线长度和虚线之间的空白间隙
ctx.lineDashOffset = 0;
ctx.strokeRect(10,10, 100, 100);//绘制一个描边矩形
}
</script>
偏移量为正: 逆时针偏移 | 偏移量为负: 顺时针偏移 |
![]() | ![]() |
- ⑺ 返回数组: 设置虚线的数组
ctx.getLineDash();
console.log(ctx.getLineDash()); // [5, 15]
- 按F12,在浏览器的console 控制台 ,可以看到数组的信息,数组是用
getLineDash()
返回的,作为console.log()
的参数
- 按F12,在浏览器的console 控制台 ,可以看到数组的信息,数组是用
1.4.4 设置 图形渐变色 Gradients
- ⑴ 渐变色 Gradients 的应用: 填充+描边 ( gradient ['ɡredɪənt] )
- 填充+描边: (用线性 或者 径向的渐变色) 来填充或描边。
- 用在
fillStyle
描边样式 或strokeStyle
填充样式 ,作为颜色值.
- 用在
- 渐变色分类: 线性渐变色 + 径向渐变色
- 填充+描边: (用线性 或者 径向的渐变色) 来填充或描边。
- ⑵ 新建 图形渐变色的方法:
- 新建一个 canvasGradient 图形渐变色 对象,并且 赋给图形的
fillStyle
或strokeStyle
属性。 - ① 创建 线性渐变:
createLinearGradient(x1, y1, x2, y2)
( Linear ['lɪnɪɚ])- 4 个参数: 2个点
- 渐变的起点 (x1,y1)
- 终点 (x2,y2)。
- ② 创建 径向渐变: (由内向外)
createRadialGradient(x1, y1, r1, x2, y2, r2)
( radial ['redɪəl])- 6 个参数: 2个圆
- 前三个参数: 一个以 (x1,y1) 为原点,半径为 r1 的圆
- 后三个参数: 另一个以 (x2,y2) 为原点,半径为 r2 的圆
- 新建一个 canvasGradient 图形渐变色 对象,并且 赋给图形的
var lineargradient = ctx.createLinearGradient(0,0,150,150);//创建 线性渐变色对象
var radialgradient = ctx.createRadialGradient(75,75,0,75,75,100);//创建 径向渐变色对象
- ⑶ 给渐变色对象 上色: 创建出 canvasGradient 对象后,我们就可以用
addColorStop()
方法给它上色了。- ① 添加色标方法:
gradient.addColorStop(position, color)
- 2 个参数: 相对位置 + 颜色
- 相对位置: position 参数 = 数值, 必须是一个 0.0 与 1.0 之间的数值,表示 渐变中 颜色所在的相对位置。
- 0.5 表示颜色会出现在正中间。
- 颜色: color 参数 必须是一个有效的 CSS 颜色值(如 #FFF, rgba(0,0,0,1) etc.)。
- ② 色标个数: 可以添加 任意多个色标(color stops)。
- ① 添加色标方法:
⑷ 创建 线性渐变色 示例
- 示例1:
<body>
<canvas id="myCanvas" height="200px" width="200px" style="border:1px solid green">
2种颜色的线性渐变 填充矩形
</canvas>
<script type="text/javascript">
var canvas = document.getElementById('myCanvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
var lingrad = ctx.createLinearGradient(10,10,100,10);//创建 线性渐变色对象
//设置 2 个色标
lingrad.addColorStop(0.5, "green");//对渐变色对象 上色
lingrad.addColorStop(1, "red");//对渐变色对象 上色
ctx.fillStyle = lingrad;//把渐变色 作为 填充样式颜色
ctx.fillRect(10,10, 100, 100);//绘制一个填充矩形
}
</script>
</body>
- 测试
- 在色标位置,为实色
(0.5, "green") , (1, "red")
- 0.5: 开始到一半的位置,是第一个色标的颜色的实色
- 1:在最后的位置,第二个色标变成实色
- 一个色标,不渐变: 只设置一个色标, 不会出现渐变色 (因为没有第二个颜色可以渐变过去)
- 关联关系
- document>canvas>
ctx >lingrad>lingrad.addColorstop()
ctx.fillStyle = lingrad;
- 使用渐变色: 创建渐变色对象 + 给对象上色 + 渐变色赋给
fillStyle,strokeStyle
- document>canvas>
- 增加渐变色部分: 要把渐变的部分加大一点,可以把第一个色变的位置,向前移动些
- 在色标位置,为实色
- 示例2: 把填充矩形变成 描边矩形,看下渐变色效果
ctx.lineWidth = 10;//设置线宽
ctx.strokeStyle = lingrad;//把渐变色 作为 填充样式颜色
ctx.strokeRect(10,10, 100, 100);//绘制一个填充矩形
- 测试
- 效果像是删除了内部一个矩形,保留了边
- 示例3: 设置 3种和 4 种颜色的渐变色, 3个色标和4个色标
lingrad.addColorStop(0, "green");//对渐变色对象 上色
lingrad.addColorStop(0.5, "white");//对渐变色对象 上色
lingrad.addColorStop(1, "black");//对渐变色对象 上色
lingrad.addColorStop(0, "green");//对渐变色对象 上色
lingrad.addColorStop(0.5, "red");//对渐变色对象 上色
lingrad.addColorStop(0.5, "white");//对渐变色对象 上色
lingrad.addColorStop(1, "black");//对渐变色对象 上色
3种颜色: 3个色标 | 4种颜色:4个色标 |
![]() | ![]() |
- 测试
- 改变 色标的实色位置,可以改变 渐变色的渐变样式
-
⑸ 创建 径向渐变色 示例
-
示例1: 一个 径向渐变色的 填充矩形
<body>
<canvas id="myCanvas" height="200px" width="200px" style="border:1px solid green">
径向填充颜色的 填充矩形
</canvas>
<script type="text/javascript">
var canvas = document.getElementById('myCanvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
var radgrad = ctx.createRadialGradient(40,40,2,50,50,30);//创建 径向渐变色对象
radgrad.addColorStop(0, 'white');//第一个色标: 给径向渐变色对象 上色
radgrad.addColorStop(0.5, 'red');//第二个色标
radgrad.addColorStop(1, 'black');//第三个色标
ctx.fillStyle = radgrad;//把渐变色 作为 填充样式颜色
ctx.fillRect(10,10, 100, 100);//绘制一个填充矩形
}
</script>
</body>
- 测试
- 1个色标,不会出现渐变色: 没有颜色渐变的方向,因为就一个颜色
- 色变位置和渐变色样式: 改变色标位置, 可以改变渐变色样式
- 前一个色标 相对位置不变, 后一个色标的位置 越大,前一个色标的颜色 占据渐变区域面积 越大 (相对位置差 变大,前一个颜色占据面积大)
- 半径和颜色: 半径增大,色标颜色 占据面积增大
- 半径控制: 不要让 内圆和外圆的 最外面的一周重合,会变形,看不出来渐变色
- 最后的色标颜色: 会填充剩余的整个图形
- 示例2: 不断增加 第二个色标的相对位置,第一个色标的颜色 渐变区域 变大
- (因为第二个颜色的相对位置 变远了,渐变区域 也跟着变大了 )
- (因为第二个颜色的相对位置 变远了,渐变区域 也跟着变大了 )
- 示例3: 不断增大 内圆和外圆的半径
增加内圆的半径: 内圆颜色面积变大 | 增加外圆的半径: 外圆颜色面积变大 |
![]() | ![]() |
- 测试
- 内圆半径限制: 内圆半径增加到 内圆的外边周和外圆的重合了,渐变就变形了
- 外圆半径不断增大: 第三个色标的颜色 占据面积 不断减小
- 示例4: 2个色标,2个颜色的 径向渐变色 填充矩形
- 示例5: 设置透明色标
- 径向渐变效果的最后一个色标: 透明色。如果想要 两色标直接的过渡 柔和一些,只要两个颜色值 一致就可以了。
radgrad.addColorStop(0, 'white');
radgrad.addColorStop(0.4, 'red');
radgrad.addColorStop(1, 'rgba(255,0,0,0)');//设置的色,透明度0,完全透明
- 测试
- 最后一个色标颜色和前一个相同,且透明度为 0 ,完全透明, 看不见原来的填充矩形了
- 最后一个色标,改成
radgrad.addColorStop(1, 'rgba(0,200,0,0.5)');
1.4.5 图案样式 Patterns
- ⑴ 实现 图案效果的方法:
createPattern()
创建图案 方法createPattern(image, type)
- 2个参数。
- ① 图像: Image = 对一个 Image 对象的引用 | canvas 对象。
- ( Image 参数 = Canvas 对象, 在 Firefox 1.5 (Gecko 1.8) 中是无效的)
- 重复图像源的对象。可以是下列之一,image 参数 =
- HTMLImageElement (
<img>
), - HTMLVideoElement (
<video>
), - HTMLCanvasElement (
<canvas>
), - CanvasRenderingContext2D,
- ImageBitmap,
- ImageData,
- Blob.
- HTMLImageElement (
- ① 重复类型: Type 必须是下面的字符串值之一:repeat,repeat-x,repeat-y 和 no-repeat。
- 水平和垂直: “repeat” (both directions),
- 水平: “repeat-x” (horizontal only),
- 垂直: “repeat-y” (vertical only),
- 不重复: “no-repeat” (neither).
- ① 图像: Image = 对一个 Image 对象的引用 | canvas 对象。
- ⑵ 图案的应用: 创建图案 + 赋给
fillStyle , strokeStyle
- 跟渐变类似的,创建出一个 pattern 之后,赋给
fillStyle
填充样式 属性 或strokeStyle
描边样式 属性即可。
- 跟渐变类似的,创建出一个 pattern 之后,赋给
var img = new Image();//创建一个image 图片对象
img.src = 'someimage.png';//对图片对象 指定资源
var ptrn = ctx.createPattern(img,'repeat');//创建图案对象 = image 图片对象 +重复类型
- ⑶ 确认 image 图片对象装载完毕: 创建图案方法 image参数 = image 图片对象时,需要确认 创建的
image
对象已经 装载完毕,否则图案 可能效果不对的。- 确认图片对象 装载完毕方法:
img.onload = function() {}
- 这个不写,有时也能 显示成功,写了更稳妥点
- 确认图片对象 装载完毕方法:
- 示例1: 创建一个 用图案填充的矩形
<body>
<canvas id="myCanvas" height="200px" width="200px" style="border:1px solid green">
一个图案填充的矩形
</canvas>
<script type="text/javascript">
var canvas = document.getElementById('myCanvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
var img = new Image();//创建一个图片对象
img.src="https://mdn.mozillademos.org/files/222/Canvas_createpattern.png";///给图片对象,指定资源
img.onload = function(){
var pattern = ctx.createPattern(img,"repeat");//创建 一个图案对象
ctx.fillStyle = pattern;//把图案 作为 填充样式颜色
ctx.fillRect(10,10, 100, 100);//绘制一个填充矩形
}
}
</script>
</body>
- 测试
- 创建 image 图片对象
- new Image() >img >src
- 创建图案
- document >canvas>ctx >createPattern() >pattern
- 关联关系
ctx.fillStyle | ctx.strokeStyle = pattern
- 创建 image 图片对象
1.4.6 阴影 Shadows
- 设置阴影的属性
- ① 在x,y轴的阴影延伸距离
- 在 x 轴的延伸距离:
shadowOffsetX = float
阴影偏移 x 属性 - 在 y 轴的延伸距离:
shadowOffsetY = float
阴影偏移 y 属性- 设定阴影 在 x 轴和 y 轴的延伸距离,不受 变换矩阵所影响。
- 负值: 表示阴影 会往上或左延伸,左上方.
- 正值: 表示阴影 会往下或右延伸,右下方.
- 默认值,都为 0。默认不偏移延伸阴影.
- 值为 Infinity 无穷大 或者NaN(Not a number) 非数字 都会被忽略。
- 在 x 轴的延伸距离:
- ② 阴影的模糊程度:
shadowBlur = float
- 设定 阴影的模糊程度,模糊程度数值 不跟像素数量挂钩,也不受变换矩阵的影响
- 阴影模糊程度,默认为 0。默认不模糊.
- 负数、 Infinity 无穷大 或者 NaN 非数字 都会被忽略。
- ③ 阴影的颜色:
shadowColor = color
- 设定 阴影颜色
- 阴影默认颜色: 全透明的黑色。默认黑色.
- ① 在x,y轴的阴影延伸距离
- 示例1: 给图案填充的矩形,加一个向右下方偏移的阴影,绿色.
ctx.shadowColor = "green";//设置阴影颜色 为绿色
ctx.shadowBlur = 2; // 设置阴影模糊 为2
ctx.shadowOffsetX =2;//设置 向右偏移 2
ctx.shadowOffsetY =2;//设置 向下偏移 2
- 测试
- 增大 x 和y 偏移量, 会整体 向右和向下 移动,看上去像是 延长 x和y 轴上的阴影
- 增大 阴影模糊程度,阴影会变得更加模糊
增大 x 偏移量: 向右偏移 | 增大 y 偏移量: 向下偏移 |
![]() | ![]() |
增大 blur 模糊程度: 越来越模糊,不是实色了 | 把x,y偏移量设置为负: 在左上角 |
![]() | ![]() |
1.4.7 Canvas 图形填充规则
-
⑴ 填充规则 参数:
fill(nonzero | evenodd)
-
根据
fill()
的 2个参数(nonzero | evenodd)
, 决定是否被填充- 根据某处 在路径的外面或者里面 来决定该处是否被填充,这对于 自己与自己路径相交或者路径被嵌套的时候 是有用的。
-
⑵ 指定如何判断 图形的“内部”:内部的填充,不是内部的,不填充
- ① 非0规则:
nonzero
根据 路径生成方向- 非零 规则 判断: 射线与路径交点,顺时针路径 +1,逆时针 -1,结果非0 = 点在内部.
- 作射线: 要判断一个点 是否在图形内,从该点 作任意方向的一条射线,
- 计算交点: 然后检测射线与图形路径的 交点情况。
- 从 0 开始计数,路径 从左向右(顺时针)穿过射线 则计数加1,从右向左(逆时针)穿过射线 则计数减1。
- 得出计数结果,
- 结果 = 0,穿过了(顺时针和逆时针路径)组合, 则认为 点在图形外部
- 结果非0 = 点在内部,只穿过了一个顺时针方向,或者1个逆时针方向的
- ( 非0 =填充部分 = 逆时针路径和顺时针路径 中间的部分,同方向路径 的都填充)
- 下图 演示了nonzero 非零规则:
- 非零 规则 判断: 射线与路径交点,顺时针路径 +1,逆时针 -1,结果非0 = 点在内部.
- ① 非0规则:
-
② 奇偶规则:
evenodd
,根据 路径数目- 奇偶规则判断: 射线和路径交点个数,结果是奇数 = 点在内部
- 作射线: 判断一个点 是否在图形内部,从该点作任意方向的一条射线
- 射线和路径的交点数量: 检测 射线与图形路径的交点的数量。
- 计数结果
- 结果是奇数 = 点在内部
- (奇数 = 填充部分 = 只穿过了 1个路径,最外部的路径,最外部路径和相邻路径 之间的部分) 跟路径方向无关,只跟路径数目 有关
- 结果是偶数 = 点在外部
- 下图 演示了evenodd 奇偶规则:
- 奇偶规则判断: 射线和路径交点个数,结果是奇数 = 点在内部
- ⑶ 填充规则 应用示例
- 示例1: 一个逆时针,一个顺时针路径,不使用填充规则参数
<body>
<canvas id="myCanvas" height="200px" width="200px" style="border:1px solid green">
</canvas>
<script type="text/javascript">
var canvas = document.getElementById('myCanvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.arc(50, 50, 30, 0, 2*Math.PI, true);//外圆逆时针
ctx.arc(50, 50, 15, 0, 2*Math.PI, false);//内圆顺时针
ctx.fill();//没有使用 填充规则参数的 fill()方法
}
</script>
</body>
- 示例2: 两个都是逆时针路径 (两个都是顺时针的路径)
ctx.arc(50, 50, 30, 0, 2*Math.PI, true);//外圆逆时针
ctx.arc(50, 50, 15, 0, 2*Math.PI, true);//内圆逆时针
ctx.fill();//没有使用 填充规则参数的 fill()方法
ctx.arc(50, 50, 30, 0, 2*Math.PI, false);//外圆逆时针
ctx.arc(50, 50, 15, 0, 2*Math.PI, false);//内圆顺时针
ctx.fill();
- 测试
- 非0 规则,根据路径的方向 进行填充
- 同方向路径全部填充: 两个路径,都是顺时针,或者逆时针,填充效果一样
- 相邻路径 方向相反,则 两路径夹在中间的部分 填充
fill()
填充方法,默认使用nonzero
非0 规则,只穿过1个方向的部分,为内部,进行填充- 顺时针和逆时针的中间部分
- 都是顺时针或者逆时针的 内部
fill() = fill("nonzero")
- 非0 规则,根据路径的方向 进行填充
- 示例3: 根据奇偶规则 填充图形 : 2个路径
ctx.arc(90, 90, 30, 0, 2*Math.PI, true);//外圆逆时针
ctx.arc(90, 90, 15, 0, 2*Math.PI, false);//内圆顺时针
ctx.fill("evenodd");
- 测试
- 奇偶规则,和路径的方向无关,只跟 路径的数目 有关
- 最外层,一定会被填充
- 增加 路径的数目,进行测试
2个路径 | 3个路径 |
![]() | ![]() |
4个路径 | 5个路径 |
![]() | ![]() |
- 测试
- 奇偶规则:
evenodd
填充区域从最层开始,逐步向内 间隔一个夹层,填充一次 - 最外层,始终是 有填充色的
- 奇偶规则:
1.5 在 canvas 图形中 绘制文本
1.5.1 绘制文本的 2 种方法
- ⑴ 绘制文本的 2 种方法:
- ① 填充文本:
fillText(text, x, y [, maxWidth])
填充文本 方法- 指定的文本: text
- 指定的位置: (x,y)
- 指定的最大宽度: [maxWidth] ,[ ] = 可选的.
- ② 绘线文本:
strokeText(text, x, y [, maxWidth])
绘线文本 方法- 指定的文本: text
- 指定的位置: (x,y)
- 指定的最大宽度: [maxWidth] ,[ ] = 可选的.
- 总结: 填充文本和绘线文本,2个方法,参数是一样的
- 参数 = 文本 + 位置 +最大宽度(可选)
- ① 填充文本:
- 示例1: 一个填充文本
<body>
<canvas id="myCanvas" height="200px" width="200px" style="border:1px solid green">
</canvas>
<script type="text/javascript">
var canvas = document.getElementById('myCanvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
ctx.fillText("Hello world~",40,40,80);//设置一个 填充文本
// ctx.strokeText("Hello world~",40,40,80); 设置一个绘线文本
}
</script>
</body>
填充文本 | 绘线文本 |
![]() | ![]() |
- 测试
- 只设置了 文本内容,默认显示字体和大小如上
10px sans-serif
,默认字体 = 10像素的 无衬线字体
- 关联关系
- document>canvas>ctx >
strokeText() ,fillText()
- document>canvas>ctx >
- 只设置了 文本内容,默认显示字体和大小如上
1.5.2 设置文本的样式
- 改变 canvas 图形 显示文本的属性:(都是
ctx
的属性)- ① 文本字体 (字体大小和字体种类):
font = value
- 绘制文本的样式.
- 这个字符串 使用和 CSS
font
字体属性 相同的语法. - 默认的字体 =
10px sans-serif
[,sæn 'serif] 10像素 无衬线字体
- ② 文本对齐:
textAlign = value
- 文本对齐选项.
- 可选的值包括:
- start, end, left, right or center.
- 默认对齐 = start。
- ③ 基线对齐:
textBaseline = value
- 基线对齐选项.
- 可选的值包括:
- top, hanging, middle, alphabetic, ideographic, bottom。
- 默认值基线对齐 = alphabetic。
- ④ 文本方向:
direction = value
- 可能的值包括:
- ltr, rtl, inherit。
- 默认值文本方向 = inherit。(在浏览器中测试,无效 19.5.16)
- 可能的值包括:
- ① 文本字体 (字体大小和字体种类):
-
⑴ 文本字体 (字体大小和字体种类):
font = value
-
示例1: 设置文本的大小和字体种类
ctx.font = "30px sans-serif";//设置文本的字体大小和字体种类
ctx.strokeText("Hello world~",20,60,130);//设置文本内容+位置+最大宽度
无衬线 字体: sans-seri (无棱角笔锋,圆润) | 有衬线 字体:serif (有棱角笔锋) |
![]() | ![]() |
- 测试
- 字体的大小,会根据
fillText()
中的最大宽度 和font
中的字体大小 而改变
- 字体的大小,会根据
- ⑵ 文本对齐:
textAlign = value
-
① 对齐基于位置: 对齐是基于
ctx.fillText()
填充文本 方法的 x 横坐标的值。textAlign="center"
。- 文本一半在 x 的左边,一半在 x 的右边
- ( 计算x的位置时 从默认文字的左端,改为文字的中心,因此只需要考虑 x的位置 即可)。
- 文本 在整个图形居中 = 将
fillText()
的 指定位置的 x 值,设置成canvas
图形画布 的宽度的一半。x = canvas.width/2
- 文本一半在 x 的左边,一半在 x 的右边
-
② 文本对齐的属性值
ctx.textAlign = "left" || "right" || "center" || "start" || "end";
- left = 文本左对齐 (本地从左向右)
- right = 文本右对齐 (本地从右向左)
- center = 文本居中对齐
- start = 文本对齐 界线开始的地方
- end = 文本对齐 界线结束的地方
- 默认 文本对齐 = start = 对齐 界限开始的地方
-
③ 关联属性:
direction
文本方向 U型direction
方向 属性会对 对齐属性产生影响。direction ="ltr"
,则 left和start 的效果相同,right和end 的效果相同;(默认效果)direction="rtl"
,则 left和end 的效果相同,right和start 的效果相同。
-
- 示例1: 设置一个左对齐的文本
<body>
<canvas id="myCanvas" height="200px" width="200px" style="border:1px solid green">
</canvas>
<script type="text/javascript">
var canvas = document.getElementById('myCanvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
ctx.font = "30px sans-serif";//设置 文本的字体大小 和字体种类
ctx.textAlign= "left";//设置文本的 对齐方式
ctx.strokeText("Hello world~",60,60,130);//绘制 绘线文本的内容和位置
}
</script>
</body>
-
测试
- 不同对齐方式,效果一样: ( 默认,
direction ="ltr"
) - ① 左对齐 = 界限开始处 对齐
ctx.textAlign= "left";
文本左对齐和ctx.textAlign= "start";
文本 从界限开始处对齐 效果一样,如上图 ↑- 对齐开始处:都是从
strokeText(text,x,y,maxwidth)
的(x,y)处 开始
- ② 右对齐= 界限结束处 对齐
ctx.textAlign= "right";
从指定位置 x 处,从右向左对齐,不是整个图形的最右方,ctx.textAlign= "end";
从界限结束处 对齐,效果一样,如下图 ↓
- 不同对齐方式,效果一样: ( 默认,
-
示例2: 文本居中对齐: 对齐位置,受字体大小影响,一半文本 在 x的左边,一半文本 在 x的右边
- x = canvas.width/2 = 200/2=100 ,文本在 整个图形中居中
- ⑶ 文本基线: = 文本垂直对齐:
textBaseline = value
- 文本基线的位置: ( = 决定文字 垂直方向的对齐方式)。
textBaseline
文本基线属性 的语法ctx.textBaseline = "top" || "hanging" || "middle" || "alphabetic" || "ideographic" || "bottom";
- 文本块的顶部:
top
- 文本基线 在文本块的顶部。
- 悬挂基线:
hanging
- 文本基线是 悬挂基线。
- 文本块的中间:
middle
- 文本基线 在文本块的中间。
- 字母基线:
alphabetic
[,ælfə’bɛtɪk]- 文本基线是标准的字母基线。
- 表意字基线:
ideographic
[,ɪdɪə’græfɪk]- 文字基线是 表意字基线;
- 如果字符本身 超出了
alphabetic
字母
基线,那么ideograhpic
表意字 基线位置 在字符本身的底部。
- 文本块的底部:
bottom
- 文本基线 在文本块的底部。
- 与
ideographic
表意字基线的区别:ideographic
表意字 基线 不需要考虑 下行字母。 - 默认值是
alphabetic
字母基线(字母 x 的下端沿)。
- 与
- 文本基线 在文本块的底部。
- 基线: = 英文字母 x 的下端沿 =
alphabetic
字母基线- 基线定义: (base line)并不是 汉字文字的下端沿,而是 英文字母“x”的下端沿。(a,c等类似字母 ,下端沿和x相同),等于
alphabetic
字母基线 - 汉字的下端沿: = 字母 j 的下端沿,比基线(字母基线)偏低一些,高于内容区底线
- 内容区: = 底线和顶线 包裹的区域 = 中文字符上边沿和下边沿之间的区域 ,英文字符上边沿,没有到达顶线的位置
- 字体高度< 内容区高度 < 盒子高度
- 字体 没有撑满整个内容区,内容区,也没有撑满整个盒子
- 悬挂基线: 低于顶线,低于汉字上边沿,高于中间线
- 基线定义: (base line)并不是 汉字文字的下端沿,而是 英文字母“x”的下端沿。(a,c等类似字母 ,下端沿和x相同),等于
- 参考博文: 深入理解 CSS 中的行高与基线
- 示例1: 不设置
textBaseline
和设置ctx.textBaseline = "alphabetic";
显示效果一样, 文本基线默认 = 字母基线
<body>
<canvas id="myCanvas" height="200px" width="200px" style="border:1px solid green">
</canvas>
<script type="text/javascript">
var canvas = document.getElementById('myCanvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
ctx.font = "20px sans-serif";
ctx.strokeText("Hello world~",30,60,150);
}
</script>
</body>
- 示例2: 字母基线 > 文本块顶部: 下移 . 文本基线,从字母基线,改为 在文本块的顶部 :
ctx.textBaseline = "top";
整个文本下移了
- 示例3: 字母基线 > 悬挂基线: 下移. 文本基线,从默认的字母基线,改为悬挂基线
ctx.textBaseline = "hanging";
: 整个文本下移了一小段
字母基线 > 文本块顶部: 下移 | 字母基线 > 悬挂基线: 下移 | 字母基线> 文本块中间: 下移 |
![]() | ![]() | ![]() |
字母基线> 表意字基线: 上移 | 字母基线> 文本块底部: 上移 | 文本块顶部 > 文本块文本块底部: 上移 |
![]() | ![]() | ![]() |
- 测试
- 文本移动方向:
top>bottom
,基线从顶部变成底部, 规则:textBaseline
基线下移 = 文本内容上移 - 基线高度的 6个位置:
top > hanging > middle > alphabetic > ideographic > bottom
- 顶部> 悬挂> 中间> 字母> 表意字> 底部
- 基线变化方向和内容移动方向 反向: 高基线—>低基线,文本内容上移 (基线高到低(上到下),文本内容反向,从下移动到上) 什么原理 ?
- 文本移动方向:
1.5.3 预测量文本
- ⑴ 获得文本细节:
measureText()
测量文本 方法- 获得 更多的文本细节时,测量文本的方法
- ⑵
ctx.measureText(text)
测量文本方法 的语法- 参数
- text: 需要测量的String 字符串。
- 返回值: TextMetrics 文本度量 对象
- 表示: 文本的尺寸信息 (Metrics ['mɛtrɪks] 度量)
- 创建方法: 通过
ctx.measureText()
方法创建。 - 关联关系: document>canvas>ctx>measureText()>metrics>
width
- 参数
- 示例1:
<body>
<canvas id="myCanvas" height="200px" width="200px" style="border:1px solid green">
</canvas>
<script type="text/javascript">
var canvas = document.getElementById('myCanvas');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
var text = ctx.measureText("hello world~");//创建 TextMetrics 文本度量对象 text.
alert(text.width);//弹出对话框 显示 文本"hello world~"的宽度 61
}
</script>
</body>
- 360 浏览器显示 ↓
-
谷歌浏览器显示
-
测试
alert()
弹出警告提示框 方法 = 显示带有( 一条指定消息+一个 OK 按钮) 的警告框。- 语法:
alert(message)
- 参数: message = 要在window 上弹出的对话框中 显示的纯文本
- 两个浏览器显示的数值不一样,有浏览器差异?
- 语法:
TextMetrics.width
属性,只读的,包含 文本先前的宽度(行内盒子的宽度)- 使用 CSS 像素计算。
- 浏览器兼容性
- 只有
width
宽度属性 实现得比较好,点击查看 更多 TextMetrics 文本度量对象的属性
- 只有
1.6 canvas 图形画布的 图像操作能力: 使用图像
-
⑴
canvas
图形画布的 的图像操作能力- 用于: 动态的图像合成 + 作为 图形的背景+ 游戏界面(Sprites)等等。
-
⑵ 浏览器支持图片格式: 任意格式的外部图片
- (比如 PNG、GIF或者JPEG,同一个页面中 其他
canvas
元素生成的图片 作为图片源)
- (比如 PNG、GIF或者JPEG,同一个页面中 其他
-
⑶ 引入图像到
canvas
图形画布里 的 2个步骤:- ① 获得图片: 3个方法
- 获得一个指向
HTMLImageElement
网页图片元素 的对象 - 获得 另一个
canvas
图形画布 元素的引用 作为源 - 提供一个URL的方式 来使用图片.
- 获得一个指向
- ② 绘制图片 到画布上: 使用
drawImage()
画图片 函数 将图片 绘制到画布上 (draw [drɔ])
- ① 获得图片: 3个方法
1.6.1 获得 需要绘制的图片
- 图片的源:
canvas
图形画布 的API 可以使用 下面这些类型中的一种 作为图片的源-
API: = 接口, Application Programming Interface 应用程序接口
-
① 图片元素:
HTMLImageElement
=Image()
图片 函数的图片+<img>
图片元素- 这些图片是由
Image()
图片 函数构造出来的,或者任何的<img>
元素
- 这些图片是由
-
② 视频的帧:
HTMLVideoElement
- 用一个HTML的
<video>
元素作为图片源,可以从视频中 抓取当前帧 作为一个图像
- 用一个HTML的
-
③ 图形画布:
HTMLCanvasElement
- 可以使用另一个
<canvas>
图形画布 元素 作为的图片源。
- 可以使用另一个
-
④ 位图:
ImageBitmap
- 这是一个高性能的位图,可以低延迟地 绘制,可以从 上述的所有源 以及其它几种源中生成。
- 总结: canvas 图片源 = ❶
Image()
图片 函数的图片+❷<img>
图片元素 + ❸<video>
元素的帧 + ❹<canvas>
图形画布 +❺ 位图
-
- 图片源的引用: 这些源 统一由
CanvasImageSource
类型 来引用。
- 获取 在canvas上使用的图片 的方式
- 使用 相同页面内的图片
- 获得与
canvas
图形画布 相同页面内的 图片的引用 的方法: document.images
集合document.getElementsByTagName()
方法document.getElementById()
通过指定图片的ID,获得这个图片
- 获得与
- 使用 其它域名下的图片
- 跨源属性: 在
HTMLImageElement
上使用crossOrigin
跨源 属性,可以请求加载 其它域名上的图片。- 如果图片的服务器 允许跨域访问 这个图片,可以使用这个图片 而不污染
canvas
图形画布,否则,使用这个图片将会污染canvas
。
- 如果图片的服务器 允许跨域访问 这个图片,可以使用这个图片 而不污染
- 跨源属性: 在
- 使用其它
canvas
图形画布 元素- 和引用页面内的图片类似地,用
document.getElementsByTagName
或document.getElementById
方法来获取其它canvas
图形画布 元素。引入的应该是 已经准备好的canvas
图形画布。 - 一个常用的应用 就是将第二个
canvas
作为另一个大的canvas
的缩略图。
- 和引用页面内的图片类似地,用
- 由零开始 创建图像
- 用脚本创建一个新的
HTMLImageElement
图片元素 对象。 - 使用
Image()
图片 构造函数。
- 用脚本创建一个新的
var img = new Image(); // 创建一个<img>元素
img.src = 'myImage.png'; // 设置图片源地址```
- 测试
-
当脚本执行后,图片开始装载。
-
画图,要确保图片加载完毕: 若调用
drawImage
时,图片没装载完,那什么都不会发生(在一些旧的浏览器中可能会抛出异常)。 -
保证图片加载完毕 方法: 因此要 用
load
事件 来保证 不会在加载完毕之前 使用这个图片:
-
var img = new Image(); // 创建 img 图片元素
img.onload = function(){
// 执行drawImage语句
}
img.src = 'myImage.png'; // 设置图片源地址,这句应该写在 drawImage 前面
- 通过
data: url
方式嵌入图像- 引用图像:
data:url
方式来 引用图像。 - 定义图片方式: Data urls 允许用 一串 Base64 编码的字符串 的方式 来定义一个图片。
- 引用图像:
img.src = 'data:image/gif;base64,R0lGODlhCwALAIAAAAAA3pn/ZiH5BAEAAAEALAAAAAALAAsAAAIUhA+hkcuO4lmNVindo7qyrIXiGBYAOw==';
data:url
优点:- 即时可用: 图片内容 即时可用,无须再到服务器兜一圈。
- 封装和迁移: 可以将 CSS,JavaScript,HTML 和 图片全部 封装在一起,迁移起来十分方便。
data:url
缺点- 无法缓存: 图像没法缓存
- 数据长: 图片大的话, 内嵌的 url 数据 会相当的长
- 使用视频帧
- 视频帧: 可以使用
<video>
视频元素 中的视频帧(即便视频是不可见的)。 - 通过 视频元素id: 一个ID为“myvideo”的
<video>
元素,你可以这样做:
- 视频帧: 可以使用
function getMyVideo() {
var canvas = document.getElementById('canvas');
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
return document.getElementById('myvideo');
}
}
- 返回视频元素对象: 它将为这个视频 返回
HTMLVideoElement
对象,它可以作为Canvas
图片源。
1.6.2 在图形画布中 绘制图片
- 一旦获得了 源图对象,使用
drawImage()
画图片 方法将它渲染到canvas
图形画布 里。 drawImage()
绘制图片 方法的 三种形态-
① 绘制图片:
drawImage(image, x, y)
:- 3个参数 = 图片对象+起始坐标
- image: =
image
或者canvas
对象 (图片 + 图形画布) - x 和 y: 在目标 canvas 里的起始坐标
- SVG图像 必须在
<svg >
根指定元素的 宽度和高度
- SVG图像 必须在
-
② 绘制带宽高的图片:
drawImage(image, x, y, width, height)
:- 5个参数 = 图片对象+ 起始坐标+宽高
- 多了2个参数:
width
和height
,设置图片的宽高 (缩放图片的效果)
-
③ 绘制切片图片:
drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
- 9个参数 = 图片对象+ 切片位置和宽高 + 显示位置和宽高
- 第一个参数:和另两个方法相同 = 一个图像或者另一个 canvas 的引用。
- 其它8个参数
- 前4个: 切片 = 把图片切下来 = 图像源上 切片的位置和宽高
- 后4个: 显示 = 显示在图形画布中 = 切片 显示位置和宽
- 9个参数 = 图片对象+ 切片位置和宽高 + 显示位置和宽高
-
- 示例1: 在图形画布中添加一个图片
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="200" height="200" style="border:1px solid">
Your browser does not support the canvas element.
</canvas>
<script type="text/javascript">
var ctx = document.getElementById('myCanvas').getContext('2d');
var img = new Image();//创建图片对象
img.onload = function(){
ctx.drawImage(img,10,10);//绘画图片,将图片,放在左上角(10,10)(类似背景图片,图形可以在上方绘制)
ctx.beginPath();//新建路径
ctx.arc(50,60,20,0,2*Math.PI,true);//画个圆
ctx.stroke();
}
img.src = "https://mdn.mozillademos.org/files/222/Canvas_createpattern.png";//给图片对象,指定资源
</script>
</body>
</html>
- 测试
drawImage()
类似于添加了背景图片,影响在上面绘制图形
- 示例2: 设置图片的宽高,进行图片的缩放
ctx.drawImage(img,10,10,180,180);
示例3: 设置一个图片切片,把一个动物裁剪,放在相框里
- 2个 图片源 (代码中直接调用这两个图片的 url)
相框 | 动物 |
![]() | ![]() |
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="300" height="300" style="border:1px solid">
Your browser does not support the canvas element.
</canvas>
<br />
<!-- 在html中放置两个隐藏的图片,用来调用 -->
<img id="photoframe" src="https://i-blog.csdnimg.cn/blog_migrate/c37e8f1236ac7b44f14244fad7e7600f.png" style="display:none" />
<img id="animal" src="https://i-blog.csdnimg.cn/blog_migrate/ec6e6a4424c4306fa3cb3647c7097f0c.png" style="display:none" />
<script type="text/javascript">
var ctx = document.getElementById('myCanvas').getContext('2d');
var photoframe = document.getElementById("photoframe");//获取相框图片对象
var animal = document.getElementById("animal");//获取动物图片对象
ctx.drawImage(photoframe,10,10);//把相框,显示在图形画布上
ctx.drawImage(animal,15,10,100,130,24,25,95,136);//把一只动物从图片上切下来,显示在图形画布上
</script>
</body>
</html>
- 测试
- 图片覆盖遮挡: 后一个
drawImage()
绘制图片 方法,会覆盖遮挡住 前一个drawImage()
的图片,所以先画 面积大的相框,防止被遮挡住 . - 隐藏图片: 在网页中插入隐藏图片,使用 img-
style="display:none"
把图片隐藏起来. - 图片合成效果:把一个完整图片中的一部分,切下来,放在了图形画布中,把另一个图片 也放在了画布中,实现了 图片合成的效果,非常有意思.
- 图片覆盖遮挡: 后一个
1.7 变形: 对图形画布的网格 旋转和缩放
- 变形: 移动原点+ 网格旋转和缩放
- 可以将原点 移动到另一点、对网格 进行旋转和缩放。
- 一般情况下,只是使用默认的网格,改变整个画布的 宽高大小。
1.7.1 图形画布 状态的 保存和恢复
-
图形状态(样式和变形)的保存和恢复 ( Saving and restoring state)
-
⑴
ctx.save()
保存 方法和ctx.restore()
恢复 方法 ([rɪ’stɔr] 恢复)- 保存和恢复 图形状态: = 保存和恢复 图形样式和变形
- 用来保存和恢复
canvas
图形画布 状态的,都没有 参数。
- 用来保存和恢复
canvas
图形画布 状态: = 样式和变形的快照 = 当前画面 应用的所有样式和变形的 一个快照。- 存储位置: 栈中.
canvas
图形画布状态(样式和变形) 存储在栈中,每当save()
保存 方法 被调用后,当前的状态 就被推送到栈中 保存。 - 保存一次,存一个当前的样式和变形 到栈中
- 保存和恢复 图形状态: = 保存和恢复 图形样式和变形
-
⑵ 一个绘画状态(
canvas
图形画布 状态)包括:当前状态 = 变形+样式+裁切路径- ① 变形: 当前应用的变形(即移动,旋转和缩放,见下)
- ② 样式:
strokeStyle, fillStyle, globalAlpha, lineWidth, lineCap, lineJoin, miterLimit, shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor, globalCompositeOperation
的值 - ③ 裁切路径: 当前的裁切路径(clipping path)
-
⑶
save()
保存方法 调用次数: 可以 任意多次 调用save()
保存 方法。 -
⑷
restore()
恢复 方法和状态关系: 每一次调用restore()
恢复 方法,(先使用 最后一个 保存的状态,再把它 就从栈中弹出 )restore()
> 恢复设定成 栈中最后一次保存的状态,然后弹出这个状态- 下一次再次使用
restore()
>> 恢复设定成 栈中最后一次保存的状态,然后弹出这个状态 - 总结: 先使用,再弹出,让最后一个栈状态 = 上一个状态
- 示例1: 8个填充矩形,5个颜色,保存3个状态颜色到栈,恢复使用栈中 3个颜色,还有2个颜色没有保存
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="300" height="300" style="border:1px solid">
Your browser does not support the canvas element.(图形是什么内容的提示信息)
</canvas>
<script type="text/javascript">
var ctx = document.getElementById('myCanvas').getContext('2d');
//第 1个填充矩形,黑色,保存,栈1=黑色
ctx.fillRect(10,10,150,150);//使用默认颜色,绘制一个填充矩形
ctx.save();//保存下当前的样式状态,颜色 = 黑色 (栈中第一个颜色,黑色)
//第 2个填充矩形,蓝色,保存,栈2=蓝色
ctx.fillStyle = "#00f"; //改变填充颜色,设置为蓝色
ctx.fillRect(25,25,120,120);//绘制一个填充矩形
ctx.save();//保存当前的样式状态,颜色=蓝色 (栈中第二个颜色,蓝色)
//第 3个填充矩形,绿色,保存,栈3=绿色
ctx.fillStyle = "rgba(0,255,0,0.8)";//改变填充颜色,设置为绿色
ctx.fillRect(35,35,100,100);//绘制一个填充矩形 (没有使用 save()方法,样式不在栈中)
ctx.save();//保存当前的样式状态,颜色 = 绿色 (栈中的第三个颜色,绿色)
//第 4个填充矩形,红色,状态没放在栈中
ctx.fillStyle = "rgba(255,0,0,0.9)";//改变填充颜色,设置为 红色
ctx.fillRect(45,45,80,80);//绘制红色填充矩形,没有保存状态,红色不在栈中
//第 5个填充矩形,黄色,状态没放在栈中
ctx.fillStyle = "rgba(255,255,0,0.9)";//改变填充颜色,设置为 黄色
ctx.fillRect(55,55,60,60);//绘制 黄色填充矩形,没有保存状态,黄色不在栈中
//第 6个填充矩形,恢复状态,绿色 = 栈3 ,弹出栈3
ctx.restore();//恢复栈中最后一次保存的状态 = 栈中第三个颜色 = 绿色,然后弹出这个状态颜色,栈中最后一个颜色 = 栈中第二个颜色 = 蓝色
ctx.fillRect(65,65,40,40);
//第 7个填充矩形,恢复状态,蓝色 = 栈2 ,弹出栈2
ctx.restore();//恢复栈中最后一次保存的状态 = 栈中第二个颜色 = 蓝色,然后弹出这个状态颜色,栈中最后一个颜色 = 栈中第一个颜色 = 黑色
ctx.fillRect(75,75,20,20);
//第 8个填充矩形,恢复状态,黑色 = 栈1
ctx.restore();//恢复栈中最后一次保存的状态 = 栈中第一个颜色 = 黑色,然后弹出这个状态颜色,栈中最后一个颜色 = 栈中没有存储色了
ctx.fillRect(80,80,10,10);
</script>
</body>
</html>
- 测试
- 连续使用: 可以连续使用
restore()
恢复 方法,倒着使用栈中的状态 - 后存先用,用完弹出: 先用 后存的状态,用完就弹出,即使没使用,使用了
restore()
恢复 方法,也弹出一个(比如,连续两次使用restore()
恢复方法,第一次的样式状态 没用,第二个才使用) - 用1次
restore()
,当前样式状态 = 倒数第一个 (如下图 = 栈3) - 用2次
restore(),
当前样式状态 = 倒数第二个 (如下图 = 栈2) - 用n次
restore(),
当前样式状态 = 倒数第n个 (栈中没有存储的状态了,就使用默认值)
- 连续使用: 可以连续使用
1.7.2 移动: 移动 图形画布原点
- ⑴ 移动 (Translating) :
translate(x, y)
移动 方法 (不旋转地 改变(图形或物体)在空间中的位置)- 移动图形画布原点:
canvas
图形画布 和它的原点 到一个不同的位置 - 2个参数:左右偏移量 + 上下偏移量
- x : 左右偏移量
- y : 上下偏移量,如下图所示。
- 移动图形画布原点:
- ⑵ 先保存,再变形: 在做变形之前 先保存状态。
- 恢复 图形状态快: 一般来说,调用
restore()
方法 比手动恢复 原先的状态 要简单得多。 - 方便恢复 图形状态( 预防图形超出范围不见): 如果在 一个循环中 做位移 , 但没有 保存和恢复
canvas
图形画布 的状态,很可能到最后 会发现怎么有些东西不见了,那是因为它很可能 已经超出canvas
图形画布 范围以外了。
- 恢复 图形状态快: 一般来说,调用
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="300" height="300" style="border:1px solid">
Your browser does not support the canvas element.
</canvas>
<script type="text/javascript">
var ctx = document.getElementById('myCanvas').getContext('2d');
for (var i = 0; i < 3; i++) { // i=0,1,2
for (var j = 0; j < 3; j++) {// j=0,1,2
ctx.save();//保存颜色值 到栈中
ctx.fillStyle = 'rgb(' + (51 * i) + ', ' + (255 - 51 * i) + ', 255)';//利用外层循环,控制颜色值 (i控制颜色,1行1个色)
ctx.translate(10 + j * 50, 10 + i * 50); //利用内层循环,控制x 左右移动偏移量,利用外层循环 控制y上下移动偏移量 (j= 控制x= 列数,i=控制y =行数)
ctx.fillRect(0, 0, 25, 25);//一个边长25的矩形
ctx.restore();//从栈中 调用颜色值和弹出颜色值
}
}
</script>
</body>
</html>
- 形成图形和图形分析
形成的图形 | 图形 形成分析 |
![]() | ![]() |
- 执行流程 分析
- 第一步,i=0,i<3,满足条件,进入内部,执行内部代码 = 第2层循环,
- ①i=0, j=0,j<3,满足条件,进入内部,执行内部代码
save()
保存当前状态 (还没有设置样式和变形,保存的最开始的状态 ,比如,画布原点的最初位置(0,0) )- (i =0)
ctx.fillStyle = "rgb(0,255,255)"
,i 控制颜色 - (i=0,j=0)
ctx.translate(10,10);
(图形画布原点,向右和向下移动,各10像素) - 1个填充正方形
restore()
从栈中调用最后一栈的状态 = 最开始的状态,弹出最开始的状态
- ② i=0,j++,j=1,j<3,满足条件,进入内部执行代码 = 第2层循环,
save()
保存当前状态 =restore()
从栈中调用最后一栈的状态 = 最开始的状态 (原点 = 没移动过的原点)- 保存图形的最初原点>移动 图形原点 >恢复最初原点 >保存最初原点…
- (i =0)
ctx.fillStyle = "rgb(0,255,255)"
(i值没变,所以颜色值没变) - (i=0,j=1)
ctx.translate(60,10);
(图形画布原点,向右移动60,和向下移动10 ,是相对于本来的原点的,不是变形后的原点) - 1个填充正方形
restore()
从栈中调用最后一栈的状态 = 最开始的状态,弹出最开始的状态
- ③ i=0,j++,j=2,j<3,满足条件,进入内部执行代码 = 第2层循环,
save()
保存当前状态 = 最开始的状态 (原点 = 没移动过的原点)- (i =0)
ctx.fillStyle = "rgb(0,255,255)"
(i值没变,所以颜色值没变) - (i=0,j=2)
ctx.translate(110,10);
(图形画布原点,向右移动110,和向下移动10 ,是相对于本来的原点的,不是变形后的原点) - 1个填充正方形
restore()
从栈中调用最后一栈的状态 = 最开始的状态,弹出最开始的状态
- ④ j++,j=3,j<3,条件不满足,2层循环 结束循环(= 1层循环的内部代码执行完毕了),返回1层循环,执行 i++
- ①i=0, j=0,j<3,满足条件,进入内部,执行内部代码
- 第二步,i++,i=1,i<3,条件满足,进入内部代码,再次执行 2 层循环
- i=1,j=0…
- i=1,j=1…
- i=1,j=2…
- ▲ 总结:
- ① 执行顺序:
- 第一步 i=0时,满足条件,进入2层循环,
- 2层循环次数 : 2层循环,j=(0,1,2),循环了3次,
- 因为 j 值改变,而改变的值,只有
translate()
移动方法的横坐标,(10+j*50),每次右移50,移动了3次,形成了水平线上 3个颜色一样的正方形 - 因为 i 没变,所以 2层循环,循环了3次,但 i 控制的颜色和
translate()
移动方法的纵坐标,都没有改变
- 第二步 i=1,i=0,变成了i=1,所以颜色和原点的纵坐标移动,都发生了变化 + j =(0,1,2) = 原点的横坐标向右移,形成了第二行 第二种颜色的方块
- 第一步 i=0时,满足条件,进入2层循环,
- ② 执行次数:
- 1层循环,执行3次 (每执行一次,2层循环 就执行3次)
- 2层循环,3*3=9,执行9次
- ③ 填充矩形的参数: 始终没有改变
ctx.fillRect(0, 0, 25, 25)
;- 1个边长25的矩形,左上角位置始终是(0,0),右下角位置,始终是(25,25)
- ④ 移动原点,改图形位置: 从1个矩形变成 9个 矩形,改的不是
fillRect()
填充矩形的参数,而是translate()
移动原点 方法 参数- 不断移动 原点的x,y坐标,让 原来位置的矩形,在不同的位置,形成了图形
- ⑤ 保存和恢复状态和变形的顺序: 保存状态> 变形> 恢复状态
- 先保存,再变形:
save()
保存状态 方法,一定要写在translate()
移动原点方法前面- 这样才能确保 每次移动原点的偏移量,都是针对 最初的原点
(0,0)
进行的,而不是 移动位置后的原点
- 这样才能确保 每次移动原点的偏移量,都是针对 最初的原点
- 先变形,再恢复 :
translate()
移动原点方法,一定要写在restore()
恢复状态 之前- (保存最初原点的位置,变形,恢复成最初原点的位置,再保存 最初原点的位置)
- 恢复的状态,即使没有应用,立刻进行保存,保存的也是这个恢复的状态
- 先保存,再变形:
- ⑥ 关键点: 利用
save()
和restore()
,进行 最初原点(0,0)的 保存和恢复,让变形移动translate()
一直都是针对 最初原点(0,0)
进行的. - ⑦ 属性值中的 数学表达式:
ctx.fillStyle = 'rgb(' + (51 * i) + ', ' + (255 - 51 * i) + ', 255)';
- 数学表达式,用 () 圆括号,括起来
- 字符串,用单引号 ’ ’ 或者双引号 " " 括起来
- 字符串和数学表达式之间,用 + 加号连接.
- ⑧ 方法中的 数学表达式:
ctx.translate(10 + j * 50, 10 + i * 50);
- 可以 直接代入变量,直接写表达式,不用圆括号
- ① 执行顺序:
- 知识拓展: 双层
for(){}
循环讲解 - 示例1: 用 * 星号 打印直角三角形
public class Demo1 {
public static void main(String[] args) {
int i, j;
for (i = 0; i <= 7; i++) { // 外层循环控制行数
for (j = 1; j <= i; j++) { // 内层循环打印 *
System.out.print("*"); // 注意不是 println
}
System.out.print("\n"); //换行
}
}
-
执行流程 分析
-
⑴ 1层循环满足 = 执行2层循环: 第 1 层
for
循环 的条件满足—> 执行第 1 层的内部代码 = 第 2 层for
循环 (因为 第2层循环,是1层循环的内部代码),当第 2 层条件满足时—>执行第 2 层的内部代码- 2层循环满足 = 执行2层循环: 第 2 层的条件一直满足时,就会一直循环执行 第2层内部的代码
- 2层循环不满足 = 执行1层循环: 直到 第 2 层的条件不满足时,退出第 2 层的循环,回到第 1 层的循环
- (此时,第二层的循环相当于continue,会跳出当前循环,去下一个循环 )
-
⑵ 更新数值:
for
循环 满足条件,执行一次循环之后才更新数值 (进行自增 i++,j++) -
⑶ 从初始值开始:
- 当 1层循环条件满足,执行2层循环(假如,此时 2层循环 j 已经从1自增到3,4或其他数值),直到2层循环 条件不满足,
- 回到1层循环,再次执行2层循环时,j 的数值 = 重新 从2层循环的初始值 j=1开始. (而不是回到 1层循环前 ,已经自增多次的那个值)
- 结束循环和进入循环: 结束循环,从别的循环 重新再进入时,j 变量 数值 = 初始值 (不是上次循环多次,自增多次的数值)
- 当 1层循环条件满足,执行2层循环(假如,此时 2层循环 j 已经从1自增到3,4或其他数值),直到2层循环 条件不满足,
-
⑷
i++
和++i
的区别:i++
:是先运行,再加1++i
:是先加1,再运行- 总结: + 在前,先加1,+ 在后,先运行
1.7.3 旋转: 对图形旋转
- 旋转 Rotating:
rotate(angle)
['rotet]- 旋转的中心:原点. 以原点为中心 旋转
canvas
图形。- 如果要改变 图形原点,用
translate(x,y)
移动原点 方法。
- 如果要改变 图形原点,用
- 一个参数:顺时针 旋转角度
- angel = 旋转的角度(angle)
- 方向和单位:
- 正值: 顺时针方向的,以弧度 为单位的值
- 负值: 逆时针
- x轴和y轴的变化: 旋转后x,y轴 都改变位置了
- 旋转的中心:原点. 以原点为中心 旋转
- 示例1: 把1个矩形,顺时针旋转 30° = π/6
<script type="text/javascript">
var ctx = document.getElementById('myCanvas').getContext('2d');
ctx.fillRect(50,50,70,50);//1个填充矩形(不会旋转)
ctx.rotate(Math.PI/6);//正方向顺时针 旋转30度
ctx.fillRect(50,50,70,50);//一个填充矩形(前面有旋转命令,会旋转)
</script>
图形和旋转后的图形 | 旋转分析 |
![]() | ![]() |
- 测试
- 旋转轴: 旋转的是 x轴和 y轴,原本的图形,跟着 x轴, y轴一起旋转,
- 相对位置: 旋转后的图形 和 旋转后的 x轴, y轴,保持着原本的相对位置
- 如上图 ↑ , 原本平行于 x轴和y轴的图形,旋转后, 还是平行于 旋转后的 x轴,y轴
- 图形超出和消失: 当旋转后的图形,位置 超出画布 ,会消失
- 语句命令位置(只旋转 旋转语句后面的图形):
rotat()
只旋转 写在这个语句后面的图形.
- 示例2: 把旋转角度从 π/9,π/8…增大到 π/2
- 测试
- 顺时针: 角度增大,旋转图形的移动方向:左下方
- 在旋转角度 不断增大的过程中,图形不断 向左下方移动,旋转角度为 π/2时,图形完全超出 画布,从画布上消失
- 旋转角度和旋转后的坐标轴: 因为 旋转角度 π/2,旋转 90°时,旋转后的 x轴,完全和本来的 y轴重合了,所以图形,随着 完全超出了 图形画布的左边界
- 顺时针: 角度增大,旋转图形的移动方向:左下方
- 示例3: 把旋转角度 从 π/9,π/8…增大到 π/2,进行逆时针旋转 (角度值加 - ,变成负值 = 逆时针旋转)
ctx.rotate(-Math.PI/9);
- 测试
- 逆时针旋转,角度增大,向右上方移动
- 图形消失: 当角度 = -π/2时,从右上方消失,因为这是,旋转后的y轴 = 原本的x轴,图形完全在x轴上方,超出上边界了,所以画布上看不见了
- 总结:
- 旋转方向和图形移动方向
- 顺时针,向左下方移动
- 逆时针,向右上方移动
- 图形消失: 当旋转角度 = [π/2,-π/2]
- 旋转方向和图形移动方向
- 示例4: 用
for
循环和rotate()
旋转方法,画6个圆形
<script type="text/javascript">
var ctx = document.getElementById('myCanvas').getContext('2d');
ctx.translate(70,70);//移动原点到(70,70)
ctx.fillStyle = "rgb(200,200,0)";
ctx.arc(0,14,6,0,Math.PI*2,true);//图形没旋转之前的位置,设置不同的颜色,和旋转后的图形 加以区分
ctx.fill();
for (var j=0;j<5;j++){ //j=[0,1,2,3,4],执行5次,5个圆形
ctx.fillStyle = "rgb(50,200,0)";//设置填充颜色
ctx.rotate(Math.PI/3);//顺时针旋转60°
ctx.beginPath();//新建路径
ctx.arc(0,14,6,0,Math.PI*2,true);//圆心在 y轴上的 圆形路径
ctx.fill();//填充圆形路径
}
</script>
- 测试1
- 旋转参照的坐标轴: 旋转后的坐标轴
- j=0,循环体执行第一次时,y轴,顺时针旋转了60°
- j=1,循环体执行第二次时,y轴,又顺时针旋转了60°,这次不是在最初的 y轴上旋转了,而是在第一次 旋转后的 y轴的上旋转的,距离最初的 那个垂直的y轴 有120°
- j=2,第三次执行时,也在上次旋转后 y轴的基础上旋转的
- 总结:每次旋转,参照的是 旋转后的坐标轴
- 旋转参照的坐标轴: 旋转后的坐标轴
6个圆形 | 图形 形成分析 |
![]() | ![]() |
- 测试2
- 画布边界 和图形 旋转后消失问题: 图形画在 y轴上,跟着y轴一起 顺时针旋转,进行左移,这个时候,就会超出画布左边界,就会看不见旋转后的图形
- 移动原点: 所以,为了防止 图形超出画布 消失,重新指定了画布的原点
translate(70,70)
,把画布的原点从(0,0),移动到了 原画布右下方的 (70,70)位置 相当于画布 在左边和上边各增加了 70像素长度- (新原点,不在画布的左上角了),这个时候顺时针旋转,图形不会超出 画布左边界和消失了
1.7.4 图形的缩放
- 缩放 Scaling:
scale(x, y)
缩放 方法 ([skel])- 增减图形在
canvas
画布 中的 像素数目,对形状,位图进行 缩小或者放大。 - 两个参数: x轴和y轴的 缩放倍数 = 缩放像素
- 缩放因子和正值
- x,y : 横轴和纵轴的 缩放因子,都必须是正值 R+。
- 缩放和值1:
- 值< 1.0 ,表示 缩小
- 值> 1.0 ,表示 放大
- 值= 1.0 , 不缩放,什么效果都没有。
- 增减图形在
- 参数值,表示 缩放倍数:
- 默认情况下,
canvas
画布 的 1 单位 = 1 个像素。 - 缩小一半: x=0.5,y=0.5,缩放因子是 0.5,1 个单位= 0.5 个像素,这样绘制出来的形状就会是原先的一半。
- 设置为 2.0 时,1 个单位= 2 像素,绘制的图形 放大了 2 倍。
- 默认情况下,
- 示例1:
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="300" height="300" style="border:1px solid">
Your browser does not support the canvas element.
</canvas>
<script type="text/javascript">
var ctx = document.getElementById('myCanvas').getContext('2d');
ctx.fillStyle = "rgb(100,150,0)";
ctx.fillRect(10,10,60,40);
ctx.save();//保存初始状态,没有缩放
//画1个参照图形,对比x轴 缩放图形
ctx.fillStyle = "rgb(100,100,100)";
ctx.fillRect(10/2,110,55/2,40);//把即将缩放的图形x1/2,x2/2 效果 = (0.5,1)
ctx.scale(0.5,1);//缩放下方的图形 x 横轴
ctx.fillStyle = "rgb(0,100,100)";
ctx.fillRect(10,60,55,40);//和初始矩形 宽高相等,使用缩放后,横坐标减小一半
ctx.restore();//恢复成初始 没有缩放的状态(防止连续两次缩放,进行缩放叠加,0.5*0.5=0.25)
//画1个参照图形 ,对比y轴缩放图形
ctx.fillStyle = "rgb(100,100,100)";
ctx.fillRect(140,10/2,55,40/2);把即将缩放的图形y1/2,y2/2 效果 = (1,0.5)
ctx.scale(1,0.5);//缩放下方 初始矩形 y纵轴,和初始矩形宽高相同
ctx.fillStyle = "rgb(0,100,100)";
ctx.fillRect(80,10,55,40);
</script>
</body>
</html>
缩放图形 | 缩放分析 |
![]() | ![]() |
- 测试
- 缩放和图形坐标:
- 缩小x轴,缩小了两个参数:
scale(0.5,1)
对 x轴进行缩小0.5 =fillRect(x1*0.5,y1,x2*0.5,y2)
= 所有 x坐标*0.5- 不仅宽度 变窄一半,矩形起始点的 x坐标也变小了一半
- 缩小y轴,缩小了两个参数`:
scale(1,0.5)
对 y轴进行缩小0.5 =fillRect(x1,y1*0.5,x2,y2*0.5)
= 所有 y坐标*0.5- 不仅宽度 变窄一半,矩形起始点的 y坐标也变小了一半
- 缩小x轴,缩小了两个参数:
- 放大和这个同理.如下 ↓
- 缩放和图形坐标:
1.7.5 变形 : 一次性设置 (缩放 + 倾斜 + 移动)
- ⑴ 变形 Transforms:
transform(m11, m12, m21, m22, dx, dy)
变形 方法-
修改变形矩阵: 允许对变形矩阵 直接修改。
-
这个方法是 将当前的变形矩阵 乘上一个基于自身参数的矩阵,在这里我们用下面的矩阵:
-
m11 m21 dx
m12 m22 dy
0 0 1
-
① 6个参数: 缩放 +倾斜 + 移动 (先水平x,后垂直y方向)
-
m11:水平方向的缩放
-
m12:水平方向的倾斜偏移
-
m21:竖直方向的倾斜偏移
-
-
m22:竖直方向的缩放 (倾斜参数,在缩放参数中间,根据矩阵对角线来的)
-
dx:水平方向的移动
-
dy:竖直方向的移动
-
-
② 参数无限大: 如果任意一个参数 是无限大,变形矩阵 也必须被标记 为无限大,否则会抛出异常。
- ⑵ 取消当前变形和设置指定新变形:
setTransform(m11, m12, m21, m22, dx, dy)
取消和设置变形 方法- 重置为单位矩阵: 将当前的变形矩阵 重置为单位矩阵,然后用相同的参数 调用
transform()
方法。 - 从根本上来说,该方法是取消了 当前变形,然后 设置为指定的变形,一步完成。
- 重置为单位矩阵: 将当前的变形矩阵 重置为单位矩阵,然后用相同的参数 调用
-
⑶ 重置变形:
resetTransform()
,不进行任何变形,没有任何缩放,倾斜和移动- 重置当前变形为单位矩阵,它和调用以下语句是一样的:
resetTransform()
=ctx.setTransform(1, 0, 0, 1, 0, 0);
- 示例: 变形+取消变形和指定新变形+取消所有变形
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="300" height="300" style="border:1px solid">
Your browser does not support the canvas element.
</canvas>
<script type="text/javascript">
var ctx = document.getElementById('myCanvas').getContext('2d');
//第一个矩形,作为参照
ctx.fillStyle = "rgb(100,150,0)";
ctx.fillRect(10,10,60,40);
ctx.save();//保存初始状态,没有缩放
//第二个矩形
ctx.fillStyle = "rgb(100,100,100)";
ctx.transform(2,0,0,1,0,0);//把下方 所有图形的x坐标放大2倍
ctx.fillRect(10,70,60,40);
//第三个矩形
ctx.setTransform(0.5,0,0,1,0,0);//取消上面设置的变形,指定成新的变形矩阵,把下方 所有图形的x坐标缩小2倍
ctx.fillRect(10,120,60,40);
//第四个矩形
ctx.resetTransform();//取消上面设置的所有的变形,不进行缩放,倾斜,移动
ctx.fillRect(10,170,60,40);
</script>
</body>
</html>
形成图形 | 变形分析 |
![]() | ![]() |
- 测试
- 变形范围:
transform()
变形 方法 会让语句下方的所有图形 发生变形 - 取消变形:
setTransform()
,或者resetTransform()
,新图形前面 使用这两种方法,可以取消之前设置的变形- 2种取消变形的区别:
setTransform()
还能指定新的变形
- 2种取消变形的区别:
- 变形范围:
1.8 图形的 合成与裁剪
- ⑴ 图形和图形的位置关系: 一般是 将一个图形 画在另一个之上 (默认的覆盖关系)
- ⑵ 改变图形 位置和合成关系:
globalCompositeOperation
全局合成 属性- Composite [kɑm’pɑzɪt] 合成的;合成物
- ⑶ 隐藏裁剪图形:
clip
属性 裁剪 属性- 隐藏不想看到的部分图形
1.8.1 图形的合成
- ⑴ 新图形和现有画布(合成方式):
globalCompositeOperation = type
全局合成操作 属性- 这个属性设定了 在画新图形时 采用的合成方式
- 可以在已有图形后面 再画新图形
- 可以 遮盖指定区域
- 清除画布中的某些部分(清除区域不仅限于矩形,如
clearRect()
方法) - 其他操作。
- 属性值 :
type
= 一个 字符串。26 个属性值
- 这个属性设定了 在画新图形时 采用的合成方式
- ⑵
globalCompositeOperation = type
全局合成操作 属性的 26 个属性值(分为5类)-
❶ 新图形的位置
- ① 画布之上(默认值):
source-over
- 这是默认设置,并在现有画布上下文之上 绘制新图形。
- ② 重叠的部分,其他透明:
source-in
- 新图形 只在新图形和目标画布 重叠的地方 绘制。其他的 都是透明的(主要显示新图形,不重叠部分+原来的现有图形 会透明 消失)。
- ③ 不重叠的部分,其他透明:
source-out
- 在 不与 现有画布内容重叠的地方 绘制新图形,其他透明(原来的图形会消失)。
- ④ 重叠的部分
source-atop
- 新图形 只在 与现有画布 内容重叠的地方 绘制。
- ⑤ 画布后面:
destination-over
- 在现有的画布 内容后面 绘制新的图形。
- ① 画布之上(默认值):
-
❷ 现有内容保持:
- ⑥ 重叠的位置:
destination-in
- 现有的画布 内容保持在 新图形和现有画布内容 重叠的位置。其他的 都是透明的。(新图形透明消失了,原图形 保留重叠部分)
- ⑦ 不重叠的内容:
destination-out
- 现有内容 保持在新图形 不重叠的地方。
- ⑧ 现有内容保留重叠,新图形在后面:
destination-atop
(atop [ə’tɑp] ,在顶上)- 现有的画布 只保留与新图形 重叠的部分,新的图形 是在画布内容 后面绘制的。
- ⑥ 重叠的位置:
-
❸ 颜色的亮暗
- ⑨ 重叠颜色部分颜色 = 颜色值相加:
lighter
(更亮色)- 两个重叠图形的颜色 是通过颜色值相加 来确定的。
- 因为颜色值相加,颜色值增大,颜色更亮 (颜色的数值越低越暗,越高越亮)
- ⑩ 只显示新图形:
copy
(相当于完全覆盖原来的图形)- 只显示新图形。
- ⑪ 重叠部分透明:
xor
- 图像中,那些 重叠 和正常绘制 之外 的其他地方 是透明的。
- xor:abbr. “异”或(逻辑运算)(Exclusive OR)
- a⊕b = (¬a ∧ b) ∨ (a ∧¬b)
- 如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。
- ⑫ 颜色更暗:
multiply
- 将 顶层像素与底层相应像素 相乘,结果是一幅 更黑暗的图片。
- ⑬ 更亮色:
screen
- 像素 被倒转,相乘,再倒转,结果是一幅更明亮的图片。
- ⑭ 暗的更暗,亮的更亮:
overlay
(套图)multiply
和screen
的结合,原本暗的地方更暗,原本亮的地方更亮。
- ⑮ 保留最暗:
darken
- 保留两个图层中最暗的像素。
- ⑯ 保留最亮:
lighten
- 保留两个图层中最亮的像素。
- ⑨ 重叠颜色部分颜色 = 颜色值相加:
-
❹ 底层,顶层和反置
- ⑰
color-dodge
- 将 底层除以顶层 的反置。
- ⑱
color-burn
- 将 反置的底层除以顶层,然后将结果反过来。
- ⑲
hard-light
- 屏幕相乘(A combination of multiply and screen)类似于叠加,但上下图层互换了。
- ⑳
soft-light
- 用 顶层减去底层 或者相反来得到一个正值。
- ㉑ 柔和强光:
difference
- 一个柔和版本的强光(hard-light)。纯黑或纯白 不会导致纯黑或纯白。
- ⑰
-
❺ 亮度(luma) ,色度(chroma),色调 (hue)的选用
- ㉒ 对比度低的柔和强光:
exclusion
- 和
difference
相似,但对比度较低。
- 和
- ㉓ 顶层色调:
hue
- 保留了底层的亮度(luma)和色度(chroma),同时采用了 顶层的色调(hue)。
- ㉔ 顶层色度:
saturation
- 保留底层的亮度(luma)和色调(hue),同时采用顶层的色度(chroma)。
- ㉕ 顶层色调和色度:
color
- 保留了底层的亮度(luma),同时采用了顶层的色调(hue)和色度(chroma)。
- ㉖ 顶层的亮度:
luminosity
- 保持底层的色调(hue)和色度(chroma),同时采用顶层的亮度(luma)。
- ㉒ 对比度低的柔和强光:
-
- 参考链接:MDN - 组合和裁剪教程
ctx.globalCompositeOperation
全局合成操作 属性,26 个属性值的存储和说明(定义成全局变量,方便看清和取用)
var gco = [ 'source-over','source-in','source-out','source-atop',
'destination-over','destination-in','destination-out','destination-atop',
'lighter', 'copy','xor', 'multiply', 'screen', 'overlay', 'darken',
'lighten', 'color-dodge', 'color-burn', 'hard-light', 'soft-light',
'difference', 'exclusion', 'hue', 'saturation', 'color', 'luminosity'
].reverse();
var gcoText = [
'这是默认设置,并在现有画布上下文之上绘制新图形。',
'新图形只在新图形和目标画布重叠的地方绘制。其他的都是透明的。',
'在不与现有画布内容重叠的地方绘制新图形。其他的都是透明的.',
'新图形只在与现有画布内容重叠的地方绘制。',
'在现有的画布内容后面绘制新的图形。',
'现有的画布内容保持在新图形和现有画布内容重叠的位置。其他的都是透明的。',
'现有内容保持在新图形不重叠的地方。',
'现有的画布只保留与新图形重叠的部分,新的图形是在画布内容后面绘制的。',
'两个重叠图形的颜色是通过颜色值相加来确定的。',
'只显示新图形。',
'图像中,那些重叠和正常绘制之外的其他地方是透明的。',
'将顶层像素与底层相应像素相乘,结果是一幅更黑暗的图片。',
'像素被倒转,相乘,再倒转,结果是一幅更明亮的图片。',
'multiply和screen的结合,原本暗的地方更暗,原本亮的地方更亮。',
'保留两个图层中最暗的像素。',
'保留两个图层中最亮的像素。',
'将底层除以顶层的反置。',
'将反置的底层除以顶层,然后将结果反过来。',
'屏幕相乘(A combination of multiply and screen)类似于叠加,但上下图层互换了。',
'用顶层减去底层或者相反来得到一个正值。',
'一个柔和版本的强光(hard-light)。纯黑或纯白不会导致纯黑或纯白。',
'和difference相似,但对比度较低。',
'保留了底层的亮度(luma)和色度(chroma),同时采用了顶层的色调(hue)。',
'保留底层的亮度(luma)和色调(hue),同时采用顶层的色度(chroma)。',
'保留了底层的亮度(luma),同时采用了顶层的色调(hue)和色度(chroma)。',
'保持底层的色调(hue)和色度(chroma),同时采用顶层的亮度(luma)。'
].reverse();
- ⑶
ctx.globalCompositeOperation
属性的 属性值应用示例
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="300" height="300" style="border:1px solid">
Your browser does not support the canvas element.
</canvas>
<script type="text/javascript">
var ctx = document.getElementById('myCanvas').getContext('2d');
ctx.fillStyle = "rgb(255,0,0)";
ctx.fillRect(30,30,60,40);
ctx.globalCompositeOperation = "source-over";//作用于下方的图形
ctx.fillStyle = "rgb(0,255,0)";
ctx.fillRect(70,50,60,40);
</script>
</body>
</html>
- 原图形:红色矩形 (先画的图形)
- 新图形:绿色矩形 (后画的图形)
原图:新图形 在原图形之上( 默认值 source-over) | source-in: 在重叠部分,其他地方 透明(原图形透明 消失了) |
![]() | ![]() |
source-out: 在不重叠的部分,绘制新图形,其他透明(原图形透明 消失了) | source-atop: 在重叠部分,绘制新图形 |
![]() | ![]() |
destination-over:新图形 在原图形后面 | destination-in: 原图形 保留重叠部分,其他透明(新图形透明 消失了) |
![]() | ![]() |
destination-out: 原图形 保留不重叠的部分,其他透明(新图形透明 消失了) | destination-atop: 原图形 保留重叠部分,置于新图形上方. |
![]() | ![]() |
lighter: 重叠部分的颜色 = 2个图形 颜色值相加(红+绿=黄) | copy: 只显示 新图形 (原图形透明 消失了) |
![]() | ![]() |
xor: 重叠部分 透明 | multiply: 重叠部分 变得更暗 |
![]() | ![]() |
screen: 重叠部分 变得更亮(效果类似 lighter) | |
![]() |
- 测试
- 更多示例,两个纯色的图形无法演示,可见 参考链接:MDN - 组合和裁剪教程
- 只显示 新图形的属性值:
source-in , source-out,copy
- 只显示 原图形的属性值:
destination-in,destination-out
- 只显示 重叠部分的属性值:
source-in,destination-in
前者是 新图形,后者是原图形
1.8.2 裁剪: 隐藏 裁剪区外的图形
-
裁切路径:
clip()
裁剪 方法- 隐藏图形: 裁切路径和画一个普通的
canvas
图形差不多,作用是遮罩,用来隐藏 不需要的部分。
- 隐藏图形: 裁切路径和画一个普通的
-
clip()
裁剪 方法的用途- 隐藏: 在 裁剪路径以外的部分 都不会在
canvas
画布上显示 - 不绘制图形: 裁切路径不会在
canvas
画布 上绘制东西,不受新图形的影响 - 指定区域: 在特定区域里 绘制图形
- 隐藏: 在 裁剪路径以外的部分 都不会在
-
绘制图形的3个方法:
stroke()
绘线 方法和fill()
填充 方法,第三个方法clip()
裁剪 方法。 -
裁剪区域: = 当前路径 (被裁剪的区域 = 当前路径外部) . 将当前正在构建的路径 转换为当前的裁剪路径。
- 裁剪区域默认值: = 整个画布(不隐藏任何图形,因为裁剪区域外的 才隐藏)默认情况下,canvas 有一个与它自身一样大的裁切路径(也就是没有裁切效果)。
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="300" height="300" style="border:1px solid">
Your browser does not support the canvas element.
</canvas>
<script type="text/javascript">
var ctx = document.getElementById('myCanvas').getContext('2d');
ctx.fillStyle = "rgba(255,0,0,0.5)";//红色填充色
ctx.fillRect(30,30,100,100);//1个填充矩形
ctx.beginPath();//新建路径
ctx.arc(80,80,30,0,Math.PI*2,true);//新建1个圆形路径
ctx.clip();//裁剪
ctx.fillStyle = "rgba(0,255,0,0.5)";//绿色 填充色
ctx.fillRect(40,40,80,80);//1个填充矩形
ctx.fillStyle = "rgba(0,0,255,0.5)";//绿色 填充色
ctx.fillRect(30,65,100,30);//1个填充矩形
</script>
</body>
</html>
未裁剪的 图形 | 裁剪后的 图形 |
![]() | ![]() |
- 测试
- 裁剪和不裁剪:
- 绘制路径+
ctx.clip();
裁剪语句 之前的图形 不进行裁剪 - 裁剪语句之后 形成的所有图形,只显示 裁剪区域之内的部分,裁剪区域外部的部分,隐藏,看不见
- 语句次序 和要显示的裁剪区域: 把形成图形 ,放在裁剪语句之后
- 绘制路径+
- 裁剪区域: 由绘制的路径决定,例子中是 中心区域
arc()
绘制的圆形
- 裁剪和不裁剪:
1.9 动画: 会动的图形
1.9.1 基本动画
- ⑴ 操控图形: 用 JavaScript 去操控
<canvas>
对象, - ⑵ 动画 画出一帧的步骤 : 清空+保存+动画+恢复
- ① 清空
canvas
画布- 除非接下来要画的内容 会完全充满
canvas
画布 (例如背景图),否则你需要清空所有。 - 最简单的做法: 用
clearRect()
方法。
- 除非接下来要画的内容 会完全充满
- ② 保存
canvas
状态- 如果你要改变一些 会改变
canvas
状态的设置(样式,变形之类的),又要在 每画一帧之时 都是原始状态的话,你需要先保存一下。
- 如果你要改变一些 会改变
- ③ 绘制 动画图形(animated shapes)
- 重绘动画帧。
- ④ 恢复
canvas
画布 状态- 如果已经保存了
canvas
画布 的状态,可以先恢复它,然后 用原始状态 重绘下一帧。
- 如果已经保存了
- ① 清空
- ⑶ 操控动画
- 实现动画: 用 定时执行重绘 的方法。
- 在设定的时间点上 执行重绘:
setInterval()
和setTimeout()
方法来控
- 在设定的时间点上 执行重绘:
- 设定 定期执行 一个指定函数: 时间和动画
- ① 按时间间隔:
setInterval(function, delay)
设置时间间隔 方法 (Interval ['ɪntɚvl] ,间隔)- 当 设定好 间隔时间后,function 函数 会定期执行。
- ② 按某时间之后:
setTimeout(function, delay)
设置时间之后 方法- 在设定好的时间之后 执行函数
- ③ 更新动画: window.requestAnimationFrame(callback) 请求动画帧 方法
- 更新动画: 告诉浏览器 希望执行一个动画,并在 重绘之前,请求浏览器 执行一个特定的函数 来更新动画。
- 参数: callback = 回调函数 = 动画函数 = 更新动画 (下一次重绘之前 执行)
- 更新动画: 要求浏览器 在下次重绘之前 调用指定的回调函数 更新动画。
- 执行时机: 该回调函数 会在浏览器 下一次重绘之前执行
- 执行次数: 回调动画函数执行次数 通常是 每秒60次,回调函数执行次数 通常与 浏览器 屏幕刷新次数 相匹配
- 返回值
- 一个 long 整数,请求 ID ,是回调列表中 唯一的标识。是个非零值,没别的意义。
- 取消回调(取消动画): 可以传这个值给
window.cancelAnimationFrame()
取消动画帧 方法 以取消回调函数。
- ① 按时间间隔:
setInterval(),setTimeout()
的使用选择- 不互动: 时间间隔.
- 如果不需要与用户互动,可以使用
setInterval()
设置时间间隔 方法,可以定期执行 指定代码。
- 如果不需要与用户互动,可以使用
- 互动: 事件监听 + 设置时间之后.
- 如果需要做一个游戏,可以使用 键盘或者鼠标事件 配合上
setTimeout()
方法来实现。- 通过设置 事件监听,可以捕捉 用户的交互,并执行 相应的动作。
- 如果需要做一个游戏,可以使用 键盘或者鼠标事件 配合上
- 不互动: 时间间隔.
- 示例:太阳系的动画
<!DOCTYPE html>
<html>
<body>
<canvas id="canvas" width="300" height="300" style="border:1px solid">
Your browser does not support the canvas element.
</canvas>
<script type="text/javascript">
var sun = new Image();//创建图像对象1 = 太阳
var moon = new Image();//创建图像对象2 = 月球
var earth = new Image();//创建图像对象3 = 地球
function init(){
sun.src = 'https://mdn.mozillademos.org/files/1456/Canvas_sun.png';
moon.src = 'https://mdn.mozillademos.org/files/1443/Canvas_moon.png';
earth.src = 'https://mdn.mozillademos.org/files/1429/Canvas_earth.png';
window.requestAnimationFrame(draw);//动画命令
}
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
ctx.globalCompositeOperation = 'destination-over';//谁先画,谁在上方(覆盖关系: 新图形画在 前一个图形的后面)
ctx.clearRect(0,0,300,300); //清空画布,要更新动画, 必须清空画布 clear canvas
ctx.fillStyle = 'rgba(0,0,0,0.3)';//指定填充颜色
ctx.strokeStyle = 'rgba(0,153,255,0.4)';//指定绘线颜色
ctx.save();//保存状态 栈1 = 最初状态,原点在(0,0),没有移动过
// 画地球 Earth
ctx.translate(150,150);//把画布原点 从(0,0) 移动到 (150,150)
var time = new Date();//获取当前的日期和时间
ctx.rotate( ((2*Math.PI)/60)*time.getSeconds() + ((2*Math.PI)/60000)*time.getMilliseconds() );//从具体的日期和时间中提取出秒和毫秒字段,把顺时针旋转角度和 当前时间的 秒和毫秒变化 关联在一起
ctx.translate(105,0);//第二次移动原点,(150,150)水平向右移动105,把地球放在 绘线上
ctx.fillRect(0,-12,50,24); // 画1个矩形,作为地球图形上的另一半遮盖住,当作阴影 Shadow
ctx.drawImage(earth,-12,-12);//把地球图形,添加到画布上
//画月球 Moon
ctx.save();//保存当前的状态 栈2
ctx.rotate( ((2*Math.PI)/6)*time.getSeconds() + ((2*Math.PI)/6000)*time.getMilliseconds() );//月球旋转角度,和当前时间变换关联在一起
ctx.translate(0,28.5);//第三次 移动原点,(150+105,150)下移 28.5 (150+105,150+28.5)
ctx.drawImage(moon,-3.5,-3.5);//画月球
//画地球旋转轨道
ctx.restore();//恢复状态,当前状态=栈2
ctx.restore();//恢复状态,当前状态=栈1=最初状态 (原点没有移动过)
ctx.beginPath();
ctx.arc(150,150,105,0,Math.PI*2,false); // Earth orbit,关联关系,半径 = 地球相对第二原点(150,150) 向右移动的距离
ctx.stroke();//绘线
//画太阳
ctx.drawImage(sun,0,0,300,300);
window.requestAnimationFrame(draw);//更新动画命令
}
init();//重新获取图片资源和进行更新动画命令,图片资源不放在这里面,直接更新动画 也可以,效果不变
</script>
</body>
</html>
形成的动图 | 图形相对关系 | 原点移动分析 |
![]() | ![]() | ![]() |
- 知识拓展:
- 获取当前日期和时间:
new Date()
Date
对象用于: 处理日期和时间。- 定义
Date
对象: 通过new
关键词- 以下代码定义了名为 myDate 的
Date
对象: var myDate=new Date() ;
- 以下代码定义了名为 myDate 的
- 注释:Date 对象 自动使用 当前的日期和时间 作为其初始值。
- 获取秒:
getSeconds()
方法- 返回秒:
getSeconds()
方法可返回 时间的秒。 - 语法:
dateObject.getSeconds()
- 返回值: 一个整数 ( 0 ~ 59 之间)
dateObject
的分钟字段,以本地时间显示。
- 返回秒:
- 获取毫秒:
getMilliseconds()
方法- 1秒(s)=1000毫秒(ms)
getMilliseconds()
方法可返回 时间的毫秒。- 语法:
dateObject.getMilliseconds()
- 返回值: 一个整数 (0 ~ 999 之间)
dateObject
的毫秒字段,以本地时间显示。
1.10 像素操作
1.10.1 ImageData 对象
- ⑴ 操纵像素数据: 通过
ImageData
图片数据 对象 操纵像素数据- 读取数据+写入数据: 直接读取 或将数据数组写入该对象中。
- (控制图像使其平滑(反锯齿)以及从
Canvas
画布中 保存图像)。
- (控制图像使其平滑(反锯齿)以及从
- 读取数据+写入数据: 直接读取 或将数据数组写入该对象中。
- ⑵
ImageData
图像数据 对象- 存储:=
canvas
画布对象的 真实像素数据:ImageData
对象中 存储着canvas
对象 真实的像素数据
ImageData
图像数据 对象的 3个 只读属性:宽+高+数据- 宽度: width
- 图片宽度,单位是像素
- 高度: height
- 图片高度,单位是像素
- 数据,一维数组: data
- Uint8ClampedArray 类型的一维数组,包含着 RGBA格式 的整型数据,范围在 0至255之间(包括255)。
- Uint8ClampedArray(8位 无符号整型 固定数组) 类型化数组
- 表示一个由 值固定在0-255区间的 8位无符号整型 组成的数组;
- 取值:
- 如果你指定一个在 [0,255] 区间外的值,它将被替换为0或255;
- 如果你指定一个非整数,那么它将被设置为最接近它的整数。
- (数组)内容被初始化为0。
- 引用: 一旦(数组)被创建,你可以使用 对象的方法 引用数组里的元素,或使用标准的 数组索引语法(即使用方括号标记)。
- 宽度: width
- 存储:=
- ⑶ ImageData-
data
数据属性-
① 返回值: 一个数组,
Uint8ClampedArray
,查看 初始像素数据。 -
② 每个像素的表示: 用4个1byte值 来代表。(按照红,绿,蓝和透明值的顺序; 这就是"RGBA"格式)
- 1个像素 = 4个字节
- 1个字节 = 红色
- 1个字节= 绿色
- 1个字节 = 蓝色
- 1个字节 = 透明值
- rgba 4项 可以表示所有的颜色
- byte: n. 字节;8位元组
- 1个字节 = 8位 = 0~255 (Byte 数据类型): 颜色值 = 0 ~255
- 每个颜色值表示: 用 0至255 来代表。
- 1个像素 = 4个字节
-
③ 颜色值的分配位置: = 数组索引. 每个颜色部份 被分配到 一个在数组内 (连续的)索引.r/g/b,1个颜色,一个索引位置.
- 左上角像素的红色部份位置: 在数组的索引 0位置。(一个颜色,1个索引)
-
④ 像素处理顺序: 像素 从左到右被处理,然后往下,遍历整个数组。
-
⑤ 数组包含的数据大小:
Uint8ClampedArray
包含 高度 × 宽度 × 4 bytes 数据- 宽度*高度 = 总像素数,1个像素 = 4 Bytes = 4字节
- 所以,数据 = 宽度 * 高度 * 4Bytes = 总像素数 * 4 字节
-
⑥ 索引值范围: 从 0到(高度×宽度×4)-1
- 因为是从0开始,所以索引 = 总数据大小 - 1
- 1个像素 = rgba = 4个字节 = 4个索引位置
- 1个颜色 = 1个索引
-
- 1个像素 = 4个字节 = 4个索引,1个颜色值= 1个字节 = 0~255 (非负整型)
- ⑦ 根据行、列 读取 某像素点的 R/G/B/A值的公式:
imageData.data[((行数-1)*imageData.width + (列数-1))*4 - 1 + 1/2/3/4];
- 通过定位前一个像素的索引,来表示当前的索引
- 示例: 6*8的矩形 = 6行,8列,读取3行4列的像素点的值
- 指定元素前面,有多少像素: a=(行数-1)*imageData.width+(列数-1)
- 按照从左到右,从上到下的顺序,前面有19个像素: 3行4列 = a=2*8+3=19,第19个像素
- 前一个像素的最后一个索引: =第19 像素最后的一个字节的索引= 19*4-1=75 (索引从 0 开始,所以减1)
- 本身像素的索引: b=a*4-1 +1/2/3/4 =前一个像素的最后一个索引+1/2/3/4 (1个像素,4个字节 = 4 个索引)
- r/g/b/a = 1/2/3/4,找红色,就+1,找绿色,就+2,以此类推.
- ⑧ 读取像素数组的大小: =宽高4 = 使用
Uint8ClampedArray.length
属性来 读取像素 数组的大小(以bytes为单位)- 总字节数 = 总像素数*4 (1个像素 =4 个字节) = 索引个数 (0~ 索引个数-1)
var numBytes = imageData.data.length;
- 知识拓展: 点击查看 更多字节介绍
- 数据存储和传输:
- 数据存储: 是以“字节”(Byte)为单位
- 数据传输: 大多是以“位”(bit,又名“比特”)为单位,一个位 就代表一个0或1(即二进制)
- 最小的信息单位: 位 bit,每8个位(bit,简写为b)组成一个字节(Byte,简写为B),是 最小一级的信息单位。
- 字节和位: 1字节 = 8位 = 8位二进制数 = 一个数字单元 = 所有的数据 在存储和运算时 都要 使用二进制数表示(因为 计算机用高电平和低电平 分别表示1和0)
- 哪些二进制数字 表示哪个符号 = 编码
- 计算机高低电平 : = 1,0 = 二进制数 = 8位 二进制数 = 可表示1个字节
1.10.2 创建一个 ImageData 图像数据 对象
- 创建 ImageData 对象: 创建一个新的,空白的
ImageData
图片数据 对象,使用createImageData()
创建图片数据 方法。 createImageData()
创建图片数据 的 2 种方法- 指定宽高的图片数据 对象:
var myImageData = ctx.createImageData(width, height);
- 创建了一个 具体尺寸的
ImageData
对象。 - 所有像素被预设为 透明黑。
- 创建了一个 具体尺寸的
- 其他图片数据对象指定的:
var myImageData = ctx.createImageData(anotherImageData);
- 创建一个被
anotherImageData
对象指定的 相同像素的ImageData
对象。 - 这个新的对象像素 全部被预设为 透明黑。这个 并非复制了 图片数据。
- 创建一个被
- 指定宽高的图片数据 对象:
1.10.3 得到场景像素数据: 获得 ImageData 对象
- 获得 ImageData 对象: 为了获得 一个包含画布场景像素数据的
ImageData
对象,用getImageData()
方法: var myImageData = ctx.getImageData(left, top, width, height);
- 返回值: = ImageData 对象.
- 这个方法 会返回一个 ImageData 对象
- 画布四角的坐标点: 它代表了画布区域的对象数据,此画布的四个角落(的4个坐标点)分别表示为:
- 左上角 = (left, top),
- 右上角 = (left + width, top),
- 左下角 = (left, top + height),
- 右下角 = (left + width, top + height) 四个点。
- 画布坐标空间元素: 这些坐标点 被设定为 画布坐标空间元素。
- 画布以外的返回值: 任何在画布以外的元素 都会被返回成: 一个透明黑的 ImageData 对象。
- 返回值: = ImageData 对象.
1.10.4 在画布中 写入像素数据
-
像素数据的写入: 用
putImageData()
方法去 对画布进行 像素数据的写入。-
ctx.putImageData(myImageData, dx, dy);
- dx和dy参数: 坐标 = 写入像素数据 左上角的坐标。
-
-
在场景内 左上角绘制
myImageData
代表的图片,代码:ctx.putImageData(myImageData, 0, 0);
1.11 canvas 画布的优化: 改善性能
<canvas>
画布元素: 网络 2D图像 渲染标准之一。<canvas>
画布 广泛用于: 游戏 及复杂的图像可视化中。
- 改善性能的方法
- ⑴ 在离屏
canvas
画布上 预渲染 相似的图形或重复的对象- 有了离屏
canvas
画布,可以不用在主线程中 绘制图像 - 使用离屏
canvas
画布的原因: 防止卡顿.- 一般情况下,
canvas
计算 和渲染 和用户操作响应都发生在 同一个线程中,在动画中(有时候很耗时)的计算操作 将会导致App卡顿,降低用户体验. - 离屏后,canvas 将可以在Web Worker 中使用,Worker 是一个Web版的线程——它允许 在幕后 运行代码。将一部分代码 放到 Worker 中, 可以给主线程 更多的空闲时间.
- 一般情况下,
- 参考文档:
- 如果 在每一帧里 有很多复杂的画图运算,可以创建一个离屏
canvas
画布- 将图像在这个 离屏画布上画一次(或者每当图像改变的时候画一次),然后在每帧上 画出视线以外的这个画布。
- 有了离屏
myEntity.offscreenCanvas = document.createElement("canvas");
myEntity.offscreenCanvas.width = myEntity.width;
myEntity.offscreenCanvas.height = myEntity.height;
myEntity.offscreenContext = myEntity.offscreenCanvas.getContext("2d");
myEntity.render(myEntity.offscreenContext);
- ⑵ 坐标点取整数: 避免使用 浮点数的坐标点,用整数 坐标点
- 子像素渲染: 当你画一个没有整数坐标点的对象时,会发生 子像素渲染。
ctx.drawImage(myImage, 0.3, 0.5);
- 抗锯齿的额外运算: 浏览器为了达到抗锯齿的效果 会做额外的运算。
- 坐标点取整: 为了避免这种情况,要保证 在调用
drawImage()
函数时,用Math.floor()
函数对所有的坐标点取整。
- 子像素渲染: 当你画一个没有整数坐标点的对象时,会发生 子像素渲染。
- ⑶ 不要在用
drawImage()
时 缩放图像- 在离屏
canvas
画布中 缓存图片的不同尺寸,不要用drawImage()
去缩放它们。 - 大图片当小图片,图片的大小和下载速度,会有影响.
- 在离屏
- ⑷ 使用多层画布: 画一个复杂的图形 = 对画布的内容,进行 分类分层
- 多个画布,多个层次:
- 有些元素不断地改变或者移动,而其它的元素,例如外观,永远不变。
- 这种情况的一种优化: 用多个画布元素 去创建不同层次。
- 外观层: 可以在最顶层 创建一个外观层,而且仅仅在用户输入的时候被画出。
- 一个游戏层: 在上面 会有 不断更新的元素
- 一个背景层: 那些较少更新的元素。
- 分层次序: 使用 css 的
z-index
属性
- 有些元素不断地改变或者移动,而其它的元素,例如外观,永远不变。
- 多个画布,多个层次:
<div id="stage">
<canvas id="ui-layer" width="480" height="320"></canvas>
<canvas id="game-layer" width="480" height="320"></canvas>
<canvas id="background-layer" width="480" height="320"></canvas>
</div>
<style>
#stage {
width: 480px;
height: 320px;
position: relative;
border: 2px solid black
}
canvas { position: absolute; }
#ui-layer { z-index: 3 }
#game-layer { z-index: 2 }
#background-layer { z-index: 1 }
</style>
- ⑸ 用 CSS 设置大的背景图
- 如果像 大多数游戏那样,有一张静态的背景图,用一个静态的
<div>
元素,结合background
属性,将它置于 画布元素之后。 - 可以避免 每一帧 都要在画布上 绘制大图。(减少js ,也能提高运行性能)
- 如果像 大多数游戏那样,有一张静态的背景图,用一个静态的
- ⑹ 用CSS
transforms
属性缩放画布- CSS
transforms
属性 由于 调用GPU,因此更快捷。 - 大画布缩小: 不要 将小画布放大,而是将 大画布缩小。
- GPU:abbr. 图形处理器 (graphics processing unit );
- CSS
var scaleX = canvas.width / window.innerWidth;
var scaleY = canvas.height / window.innerHeight;
var scaleToFit = Math.min(scaleX, scaleY);
var scaleToCover = Math.max(scaleX, scaleY);
stage.style.transformOrigin = '0 0'; //scale from top left
stage.style.transform = 'scale(' + scaleToFit + ')';
- ⑺ 关闭透明度: 浏览器内部优化
-
不需要透明时关闭: 游戏使用画布 而且不需要透明,当使用
HTMLCanvasElement.getContext()
创建一个绘图上下文时把alpha
选项设置为false
。 -
浏览器内部优化: 这个选项 可以帮助浏览器 进行内部优化。
-
var ctx = canvas.getContext('2d', { alpha: false });
-
- ⑻ 更多的贴士
- 将画布的函数调用 集合到一起(例如,画一条折线,而不要画多条分开的直线)
- 减少路径 ?
- 少改变画布状态: 避免不必要的 画布状态改变 (变多了,容易混淆)
- 渲染画布中的不同点,而非整个新状态
- 尽可能避免
shadowBlur
阴影模糊 属性 - 尽可能避免 text rendering 文本渲染
- 使用 不同的办法 去清除画布 (
clearRect()
vs.fillRect()
vs. 调整canvas
大小) - 有动画,使用
window.requestAnimationFrame()
而非window.setInterval()
- 更新动画,优先使用 请求动画帧 方法
- 谨慎使用大型物理库
- 将画布的函数调用 集合到一起(例如,画一条折线,而不要画多条分开的直线)
-
参考文档
-
感谢:♥♥♥ 如果这篇文章对您有帮助的话,可以点赞、评论、关注,鼓励下作者哦,感谢阅读 ~
-
转载 请注明出处 ,Thanks♪(・ω・)ノ
- 作者:Hey_Coder
- 来源:CSDN
- 原文:https://blog.csdn.net/VickyTsai/article/details/90170058
- 版权声明:本文为博主原创文章,转载请附上博文链接!