canvas 图形画布标签

  • 1. 如何定义一个 图形画布 ?
    • 1.1 canvas 图形画布 标签的属性 有哪些 ?
    • 1.2 canvas 图形画布的 基本用法 有哪些 ?
    • 1.3 如何使用 canvas 图形画布 标签 来绘制图形 ?
      • 1.3.1 画布栅格和坐标空间
      • 1.3.2 绘制矩形
      • 1.3.3 绘制路径
      • 1.3.4 移动笔触
      • 1.3.5 绘制 线段
      • 1.3.6 绘制 圆弧和圆弧
      • 1.3.7 二次 贝塞尔曲线 和三次 贝塞尔曲线
      • 1.3.8 绘制 矩形路径
      • 1.3.9 Path2D 对象
      • 1.3.10 使用 SVG paths
    • 1.4 给图形 添加样式和颜色
      • 1.4.1 给图形添加颜色
      • 1.4.2 透明度: 设置图形透明度
      • 1.4.3 线型:线的样式
      • 1.4.4 设置 图形渐变色 Gradients
      • 1.4.5 图案样式 Patterns
      • 1.4.6 阴影 Shadows
      • 1.4.7 Canvas 图形填充规则
    • 1.5 在 canvas 图形中 绘制文本
      • 1.5.1 绘制文本的 2 种方法
      • 1.5.2 设置文本的样式
      • 1.5.3 预测量文本
    • 1.6 canvas 图形画布的 图像操作能力: 使用图像
      • 1.6.1 获得 需要绘制的图片
      • 1.6.2 在图形画布中 绘制图片
    • 1.7 变形: 对图形画布的网格 旋转和缩放
      • 1.7.1 图形画布 状态的 保存和恢复
      • 1.7.2 移动: 移动 图形画布原点
      • 1.7.3 旋转: 对图形旋转
      • 1.7.4 图形的缩放
      • 1.7.5 变形 : 一次性设置 (缩放 + 倾斜 + 移动)
    • 1.8 图形的 合成与裁剪
      • 1.8.1 图形的合成
      • 1.8.2 裁剪: 隐藏 裁剪区外的图形
    • 1.9 动画: 会动的图形
      • 1.9.1 基本动画
    • 1.10 像素操作
      • 1.10.1 ImageData 对象
      • 1.10.2 创建一个 ImageData 图像数据 对象
      • 1.10.3 得到场景像素数据: 获得 ImageData 对象
      • 1.10.4 在画布中 写入像素数据
    • 1.11 canvas 画布的优化: 改善性能

1. 如何定义一个 图形画布 ?

  • 用于

    • 如何定义一个 图形画布 ? (在上面 绘制图形)

      • 使用 <canvas>图形画布 标签
      • ['kænvəs]
  • 使用
  • <canvas>图形画布 标签 可以 直接绘制图形吗 ?
    • 不可以,<canvas>图形画布 标签 只是 图形容器,要使用脚本 来绘制图形。
  • <canvas>图形画布 标签的浏览器支持
    • IE 8 以及更早的版本不支持 <canvas>图形画布 标签。
  • 如何通过 <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 图形画布的默认大小 为多少 ?
      • 300像素×150像素(宽×高,像素的单位是 px)矩形画布
  • 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 文档 来描述绘图。
    • 相同点:
      • 功能等同: 这两种方式在功能上是等同的,任何一种 都可以用另一种来模拟。
    • 移除元素:
      • 移除元素: 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 画布中的实际图像。
    • 画布默认样式 - 透明:如果没有为 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 id="foo" ...></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="data:images/clock.png" width="150" height="150" alt=""/>
</canvas>

  • 画笔下笔地方: 渲染上下文 (The rendering context)

    • 画布起始空白: canvas 图形画布 起初是空白的,一张白纸一样,什么都没画。
    • 找一块下笔处:
      • 在什么上面 绘制图形 ?

        • 为了展示,首先脚本需要找到 渲染上下文( 就是图形上下笔的地方),然后在它的上面 绘制图形。
        • 这个下笔的地方,不是 canvas 图形画布标签,相当于 canvas 画布上,找一个更具体的位置了.(个人理解的)
    • 怎么找到这个下笔处 ?
      • canvas-getContext()获取上下文 方法

        • <canvas>图形画布 元素 有一个叫做 getContext()的方法 , 用来获得 渲染上下文(下笔处)和它的绘画功能。
    • 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 (垂直距离同理)

  • 示例3: 在清除矩形 区域中,生成一个 宽高 20像素 的矩形边框

    • ctx.strokeRect(65,65,20,20);画矩形边框 方法

  • 关联关系

    • document>canvas>ctx>strokeRect()画矩形边框 方法
  • 三个矩形绘制方法

    • 关联关系: document>canvas>ctx>

      • fillRect()
      • clearRect()
      • strokeRect)
   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()
    • 图形的显示:
      • 在没有 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.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 为顺时针方向, 不设置这个属性值 = 默认顺时针.
    • 使用arcTo()圆弧到点 方法
      • 这个方法的实现还不好,暂时不使用.
      • arcTo(x1, y1, x2, y2, radius)圆弧到点 方法
      • 根据给定的 控制点和半径 画一段圆弧,再 以直线 连接两个控制点。

  • arc()圆弧 方法的 开始角和结束角的单位:

    • startAngleendAngle

      • arc()圆弧 函数中 表示角的单位是 弧度,不是角度。
      • 角度与弧度的 js 表达式: 弧度=(Math.PI/180)*角度。
    • 角的单位 = 弧度 = (π/180°)x角度 (rad), (弧度 = 角度除以180,乘以 π)
      • 弧度单位 rad, 常省略. 角度单位 °,不省略.
    • 角度 = 弧度/π x 180 ° (弧度除以π,乘以 180)
      • 弧度 π = > π/π x 180° = 180°

        • (π = 180°,2π = 360°)
        • 弧度和角度值的形式: 弧度 = …π, 角度 = …°

  • 开始角结束角 在坐标系上的弧度

    • 注意,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()闭合路径 方法 设置闭合路径,因为 结束角和半径结合 指明了结束位置,圆弧 也不需要 闭合路径.(圆弧 不是封闭图形)

  • 示例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 设置: = 矩形的宽高尺寸.

  • 示例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, arcquadraticCurveTo
  • 路径结合: 用什么将 path 路径 结合起来 ?
    • addPath() 添加路径 方法

      • void path.addPath(path [, transform]);
    • addPath()添加路径 方法的参数
      • path : 需要添加的 Path2D 路径。
      • transform: 可选 SVGMatrix 作为新增路径的 变换矩阵。
  • 参考文档:
    • Path2D() WebAPI 接口参考
    • Path2D.addPath() WebAPI 接口参考

  • 示例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()

1.3.10 使用 SVG paths

  • SVG

    • abbr. 可伸缩向量图形(Scalable Vector Graphics);
  • 路径重用: canvas 路径 = SVG path data
    • 可以使用 SVG path data 来初始化 canvas上的路径。

      • 因此,获取路径时 可以 (以SVG或canvas的方式) 来重用它们。
    • 使用路径: 使用 SVG创建的路径对象 = 绘画命名的参数变量.
  • 示例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 填充样式的值

      • 这个颜色 = 后面 所有 新绘制的图形 的默认颜色。
    • 如果要给之后的每个图形 设置不同的颜色,该怎样 ?
      • 重新设置 颜色样式属性: 需要重新设置fillStylestrokeStyle填充样式和轮廓线条样式 的值。
    • 总结:不同的颜色,要用 fillStylestrokeStyle填充样式和轮廓线条样式 属性 重新设置.
  • 示例: 一个 红色描边正方形,一个 绿色填充圆形
<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 ,数字越大,越不透明了

      • (想要相反效果,可以把图形颜色设置为白色,再用其他图形 设置其他的背景色,白色越重叠,越不透明,越接近白色,就看上去像是越来越透明了)
    • 透明度和填充色的 语句位置: 只设置了一次透明度和填充颜色,下方三个图形 都有相同填充色和透明度了

  • 示例3: 使用透明颜色,作为填充颜色,不设置专门的透明属性

    • ctx.fillStyle= "rgba(0,150,0,0.8)"

  • 知识拓展
  • RGB颜色
    • 什么是 RGB颜色 ?

      • 颜色标准: RGB 色彩模式是工业界的一种颜色标准
    • 产色原理: 颜色发光+ 颜色通道的 (变化+叠加)
      • 通过对红( R)、绿(G)、蓝(B) 三个颜色通道的变化 以及它们相互之间的叠加 来得到各式各样的颜色的.
      • RGB 是从颜色发光的原理来设计定的,
    • 色彩和亮度: = 色彩相混合+亮度相叠加
      • 像有红、绿、蓝三盏灯,当它们的光 相互叠合的时候,色彩相混,而亮度 却等于 三者亮度之总和,越混合亮度越高。
      • 亮度: 每种色各分为256 阶亮度
        • 在0时“灯”最弱——是关掉的,而在 255 时“灯”最亮
      • 灰色: 当三色 亮度数值相同时,产生 不同灰度值的灰色调
        • 三色亮度都为 0 时,是最暗的黑色调—
        • 三色亮 度都为 255 时,是最亮的白色调
  • 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) 的矩形,整个区域的边界 刚好落在像素边缘上,这样得到的矩形 有着清晰的边缘.
  • 线宽的近似渲染问题 (准确的线条)

    • 绘制一条从 (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();//线段1ctx.moveTo(10,10);//起始点ctx.lineTo(140,10);//终点//线段2ctx.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纵坐标,每次向下 平移40ctx.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个间隙

  • 示例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] ,奇数,自动重复成偶数

    • 数组只有一个值 = 自重复 = (虚线线段长度 = 空白间隙)
  • 设置 虚线偏移量: line​Dash​Offset虚线偏移量 属性
    • 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()的参数


1.4.4 设置 图形渐变色 Gradients

  • 渐变色 Gradients 的应用: 填充+描边 ( gradient ['ɡredɪənt] )

    • 填充+描边: (用线性 或者 径向的渐变色) 来填充或描边。

      • 用在 fillStyle 描边样式 或 strokeStyle 填充样式 ,作为颜色值.
    • 渐变色分类: 线性渐变色 + 径向渐变色
  • 新建 图形渐变色的方法:
    • 新建一个 canvasGradient 图形渐变色 对象,并且 赋给图形的 fillStylestrokeStyle 属性。
    • 创建 线性渐变: 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 的圆
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
    • 增加渐变色部分: 要把渐变的部分加大一点,可以把第一个色变的位置,向前移动些

  • 示例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.
      • 重复类型: Type 必须是下面的字符串值之一:repeat,repeat-x,repeat-y 和 no-repeat。
        • 水平和垂直: “repeat” (both directions),
        • 水平: “repeat-x” (horizontal only),
        • 垂直: “repeat-y” (vertical only),
        • 不重复: “no-repeat” (neither).
  • 图案的应用: 创建图案 + 赋给 fillStyle , strokeStyle
    • 跟渐变类似的,创建出一个 pattern 之后,赋给 fillStyle 填充样式 属性 或 strokeStyle描边样式 属性即可。
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

1.4.6 阴影 Shadows

  • 设置阴影的属性

    • 在x,y轴的阴影延伸距离

      • 在 x 轴的延伸距离: shadowOffsetX = float 阴影偏移 x 属性
      • 在 y 轴的延伸距离: shadowOffsetY = float阴影偏移 y 属性
        • 设定阴影 在 x 轴和 y 轴的延伸距离,不受 变换矩阵所影响。
        • 负值: 表示阴影 会往上或左延伸,左上方.
        • 正值: 表示阴影 会往下或右延伸,右下方.
      • 默认值,都为 0。默认不偏移延伸阴影.
        • 值为 Infinity 无穷大 或者NaN(Not a number) 非数字 都会被忽略。
    • 阴影的模糊程度: shadowBlur = float
      • 设定 阴影的模糊程度,模糊程度数值 不跟像素数量挂钩,也不受变换矩阵的影响
      • 阴影模糊程度,默认为 0。默认不模糊.
        • 负数、 Infinity 无穷大 或者 NaN 非数字 都会被忽略。
    • 阴影的颜色: shadowColor = color
      • 设定 阴影颜色
      • 阴影默认颜色: 全透明的黑色。默认黑色.

  • 示例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 非零规则:
  • 奇偶规则: evenodd,根据 路径数目

    • 奇偶规则判断: 射线和路径交点个数,结果是奇数 = 点在内部

      • 作射线: 判断一个点 是否在图形内部,从该点作任意方向的一条射线
      • 射线和路径的交点数量: 检测 射线与图形路径的交点的数量。
      • 计数结果
        • 结果是奇数 = 点在内部
        • (奇数 = 填充部分 = 只穿过了 1个路径,最外部的路径,最外部路径和相邻路径 之间的部分) 跟路径方向无关,只跟路径数目 有关
        • 结果是偶数 = 点在外部
    • 下图 演示了evenodd 奇偶规则:
  • nonzero | 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")

  • 示例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()

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
    • 文本对齐的属性值

      • 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 的下端沿,比基线(字母基线)偏低一些,高于内容区底线
    • 内容区: = 底线和顶线 包裹的区域 = 中文字符上边沿和下边沿之间的区域 ,英文字符上边沿,没有到达顶线的位置
    • 字体高度< 内容区高度 < 盒子高度
      • 字体 没有撑满整个内容区,内容区,也没有撑满整个盒子
    • 悬挂基线: 低于顶线,低于汉字上边沿,高于中间线

  • 参考博文: 深入理解 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 元素生成的图片 作为图片源)
  • 引入图像到 canvas图形画布里 的 2个步骤

    • 获得图片: 3个方法

      • 获得一个指向 HTMLImageElement 网页图片元素 的对象
      • 获得 另一个 canvas图形画布 元素的引用 作为源
      • 提供一个URL的方式 来使用图片.
    • 绘制图片 到画布上: 使用drawImage()画图片 函数 将图片 绘制到画布上 (draw [drɔ])

1.6.1 获得 需要绘制的图片

  • 图片的源: canvas图形画布 的API 可以使用 下面这些类型中的一种 作为图片的源

    • API: = 接口, Application Programming Interface 应用程序接口

    • 图片元素: HTMLImageElement =Image()图片 函数的图片+<img>图片元素

      • 这些图片是由Image()图片 函数构造出来的,或者任何的<img>元素
    • 视频的帧: HTMLVideoElement

      • 用一个HTML的 <video>元素作为图片源,可以从视频中 抓取当前帧 作为一个图像
    • 图形画布: HTMLCanvasElement

      • 可以使用另一个 <canvas> 图形画布 元素 作为的图片源。
    • 位图: ImageBitmap

      • 这是一个高性能的位图,可以低延迟地 绘制,可以从 上述的所有源 以及其它几种源中生成。
      • 总结: canvas 图片源 = ❶Image()图片 函数的图片+❷<img>图片元素 + ❸<video>元素的帧 + ❹<canvas> 图形画布 +❺ 位图
  • 图片源的引用: 这些源 统一由 CanvasImageSource类型 来引用。

  • 获取 在canvas上使用的图片 的方式
  • 使用 相同页面内的图片
    • 获得与 canvas 图形画布 相同页面内的 图片的引用 的方法:
    • document.images集合
    • document.getElementsByTagName()方法
    • document.getElementById()通过指定图片的ID,获得这个图片
  • 使用 其它域名下的图片
    • 跨源属性: 在 HTMLImageElement上使用crossOrigin跨源 属性,可以请求加载 其它域名上的图片。

      • 如果图片的服务器 允许跨域访问 这个图片,可以使用这个图片 而不污染canvas图形画布,否则,使用这个图片将会污染canvas
  • 使用其它 canvas图形画布 元素
    • 和引用页面内的图片类似地,用 document.getElementsByTagNamedocument.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 >根指定元素的 宽度和高度
    • 绘制带宽高的图片: drawImage(image, x, y, width, height):

      • 5个参数 = 图片对象+ 起始坐标+宽高
      • 多了2个参数:widthheight,设置图片的宽高 (缩放图片的效果)
    • 绘制切片图片: drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)

      • 9个参数 = 图片对象+ 切片位置和宽高 + 显示位置和宽高

        • 第一个参数:和另两个方法相同 = 一个图像或者另一个 canvas 的引用。
        • 其它8个参数
          • 前4个: 切片 = 把图片切下来 = 图像源上 切片的位置和宽高
          • 后4个: 显示 = 显示在图形画布中 = 切片 显示位置和宽

  • 示例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://img-blog.csdnimg.cn/20190517163253798.png" style="display:none" /><img id="animal" src="https://img-blog.csdnimg.cn/20190517161833899.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1ZpY2t5VHNhaQ==,size_16,color_FFFFFF,t_70" 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 ,弹出栈3ctx.restore();//恢复栈中最后一次保存的状态 = 栈中第三个颜色 = 绿色,然后弹出这个状态颜色,栈中最后一个颜色 = 栈中第二个颜色 = 蓝色ctx.fillRect(65,65,40,40);//第 7个填充矩形,恢复状态,蓝色 = 栈2 ,弹出栈2ctx.restore();//恢复栈中最后一次保存的状态 = 栈中第二个颜色 = 蓝色,然后弹出这个状态颜色,栈中最后一个颜色 = 栈中第一个颜色 = 黑色ctx.fillRect(75,75,20,20);//第 8个填充矩形,恢复状态,黑色 = 栈1ctx.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,2for (var j = 0; j < 3; j++) {// j=0,1,2ctx.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++,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) = 原点的横坐标向右移,形成了第二行 第二种颜色的方块
    • 执行次数:
      • 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 变量 数值 = 初始值 (不是上次循环多次,自增多次的数值)
  • i++++i的区别:

    • i++ :是先运行,再加1
    • ++i :是先加1,再运行
    • 总结: + 在前,先加1,+ 在后,先运行

  • 参考博文:

    • 双层 for 循环解释
    • 双层 for 循环嵌套的执行流程

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坐标也变小了一半
    • 放大和这个同理.如下 ↓

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()还能指定新的变形

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(套图)
        • multiplyscreen的结合,原本暗的地方更暗,原本亮的地方更亮。
      • 保留最暗: 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 canvasctx.fillStyle = 'rgba(0,0,0,0.3)';//指定填充颜色ctx.strokeStyle = 'rgba(0,153,255,0.4)';//指定绘线颜色ctx.save();//保存状态 栈1 = 最初状态,原点在(0,0),没有移动过// 画地球 Earthctx.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个矩形,作为地球图形上的另一半遮盖住,当作阴影 Shadowctx.drawImage(earth,-12,-12);//把地球图形,添加到画布上//画月球 Moonctx.save();//保存当前的状态 栈2ctx.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();//恢复状态,当前状态=栈2ctx.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() ;
  • 注释: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。
          • 引用: 一旦(数组)被创建,你可以使用 对象的方法 引用数组里的元素,或使用标准的 数组索引语法(即使用方括号标记)。

  • 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 来代表。
    • 颜色值的分配位置: = 数组索引. 每个颜色部份 被分配到 一个在数组内 (连续的)索引.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 对象。

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 — 使用Web Worker提高你的Canvas运行速度
      • 耦合和解耦
    • 如果 在每一帧里 有很多复杂的画图运算,可以创建一个离屏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 );
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()
      • 更新动画,优先使用 请求动画帧 方法
    • 谨慎使用大型物理库

  • 参考文档

    • W3School 教程
    • MDN Canvas教程
  • 感谢:♥♥♥ 如果这篇文章对您有帮助的话,可以点赞、评论、关注,鼓励下作者哦,感谢阅读 ~

  • 转载 请注明出处 ,Thanks♪(・ω・)ノ

    • 作者:Hey_Coder
    • 来源:CSDN
    • 原文:https://blog.csdn.net/VickyTsai/article/details/90170058
    • 版权声明:本文为博主原创文章,转载请附上博文链接!

【canvas 图形画布标签】(使用详解)相关推荐

  1. canvas rotate()画布的旋转详解

    刚才还是风和日丽的天,这没一会会就乌云密布了,雨呀,眼瞅就下来了呢,一场秋雨一场寒,感觉懂得要穿棉袄了的节奏 10月1号之前,还是T恤呢,10月放完国庆节和中秋节以后就开始毛衣.毛衫.毛外套了,这真的 ...

  2. html画布用函数旋转,canvas rotate()画布的旋转详解

    刚才仍是风和日丽的天,这没一会会就乌云密布了,雨呀,眼瞅就下来了呢,一场秋雨一场寒,感受懂得要穿棉袄了的节奏 10月1号以前,仍是T恤呢,10月放完国庆节和中秋节之后就开始毛衣.毛衫.毛外套了,这真的 ...

  3. html5 canvas基础与动画开发详解-吴华-专题视频课程

    html5 canvas基础与动画开发详解-533人已学习 课程介绍         一.本课程几乎包括所有canvas常用的api用法讲解 二.包括以下案例应用: 1.坐标系绘制 2.图片裁剪与填充 ...

  4. 【转】图形流水线中坐标变换详解:模型矩阵、视角矩阵、投影矩阵

    转自:图形流水线中坐标变换详解:模型矩阵.视角矩阵.投影矩阵_sherlockreal的博客-CSDN博客_视角矩阵 图形流水线中坐标变换详解:模型矩阵.视角矩阵.投影矩阵 图形流水线中坐标变换过程 ...

  5. 图形流水线中坐标变换详解:模型矩阵、视角矩阵、投影矩阵

    图形流水线中坐标变换详解:模型矩阵.视角矩阵.投影矩阵 图形流水线中坐标变换过程 模型矩阵:模型局部坐标系和世界坐标系之间的桥梁 1.模型局部坐标系存在的意义 2.根据模型局部坐标系中点求其在世界坐标 ...

  6. h5列表 php,H5的标签使用详解

    这次给大家带来H5的标签使用详解,使用H5标签的注意事项有哪些,下面就是实战案例,一起来看一下. 不允许写结束标记的标签:area(定义图像映射中的区域).base(为页面上的所有链接规定默认地址或默 ...

  7. html标签非成对,深入document.write()与HTML4.01的非成对标签的详解

    深入document.write()与HTML4.01的非成对标签的详解 (一)HTML4.01中的非成对标签: 注释标签: 严格来讲不算HTML标签的:文档声明标签 设置页面元信息的:标签 设置网页 ...

  8. HTML标签图文详解(三)

    前两篇博文我们学习了HTML的基础知识与标签 HTML标签图文详解(一) HTML标签图文详解(二) 现在我们开更加深入的学习 本文主要内容 列表标签:<ul>.<OL>.&l ...

  9. 04-HTML标签图文详解(一)

    一.排版标签 注释标签 <!-- 注释 --> 段落标签<p> <p>This is a paragraph</p> <p>This is ...

最新文章

  1. R把天数据按照不同时间粒度聚合数据(Aggregate)
  2. 利用机器学习进行DNS隐蔽通道检测——数据收集,利用iodine进行DNS隐蔽通道样本收集...
  3. boost::math模块实现图表显示使用 Lambert W 函数计算电流的测试程序
  4. HTTP 错误 500.19 - Internal Server Error 无法访问请求的页面,因为该页的相关配置数据无效。...
  5. 设为首页、加入收藏 兼容代码
  6. OpenCV开发团队开源计算机视觉标注工具CVAT
  7. Bootstrap 图片替换
  8. 一些iphone开发的资料
  9. 尚硅谷Linux视频学习建议贴及linux全套视频,运维版本180G高质量视频教程免费下,linux教程
  10. WinForm 快捷键设置
  11. mfc单文档中如何将view的基类由CView转为CScrollview
  12. MSRA-TD500数据集(MSRA Text Detection 500 Database)
  13. 雷林鹏分享:jQuery EasyUI 树形菜单 - 创建带复选框的树形菜单
  14. 第一次做socket的一些心得
  15. 计算机三级考点2:管理和运营宽带城域网的关键技术
  16. 海康威视相机 RTSP 传输延迟解决方案
  17. 2016中期中国云计算市场报告
  18. 趁年轻,我们干点什么吧
  19. 消除Permission is only granted to system apps报错
  20. BUUCTF pwn rootersctf_2019_xsh

热门文章

  1. 带后台的IM即时通讯App 全程MVP手把手打造总结
  2. 揭秘软件臃肿的真实原因!
  3. Java面试攻略——JVM
  4. 步之道︱适宜步行和不适宜步行的城市究竟什么样?
  5. DB2数据库常用查询语句
  6. 挂载(具名挂载、匿名挂载)
  7. 推荐Paypal的两个数据职位(实习/社招)
  8. 内网安全:初探隧道技术
  9. 什么是立即执行函数?有什么作用?
  10. 学习C语言类似百词斩的软件,十款很不错的英语学习软件大推荐