Canvas—2D 绘图

目录

前言

一、canvas 的基本知识

1、创建画布

2、创建画笔

二、绘制 2D 图形

1、绘制和清除矩形

(1)、绘制矩形

(2)、清除矩形

(3)、实例

2、绘制路径

(1)、开启绘制模式

(2)、绘制路径的属性和方法

(3)、实例

3、绘制文本

4、变换

(1)、变换的语法

(2)、变换的步骤

(3)、变换后的状态管理

5、绘制图像

(1)、原图绘制

(2)、按指定大小绘制

(3)、绘制图像的某一部分

6、阴影

7、渐变

(1)、线性渐变

(2)、径向渐变

8、模式

9、使用图像数据

10、合成

(1)、globalAlpha 属性的应用

(2)、globalCompositionOperation 属性的应用

三、绘制 3D 图形

四、导出在 HTML 的 canvas 元素上绘制的图像

五、canvas 报错汇总

1、 报错:canvas.getContext is not a function.


前言

Canvas API中文文档

无论是使用 HTML/CSS 还是 SVG,它们都属于声明式绘图系统,也就是我们根据数据创建各种不同的图形元素(或者 CSS 规则),然后利用浏览器渲染引擎解析它们并渲染出来。

但是 Canvas2D 不同,它是浏览器提供的一种可以直接用代码在一块平面的“画布”上绘制图形的 API,使用它来绘图更像是传统的“编写代码”,简单来说就是调用绘图指令,然后引擎直接在页面上绘制图形。这是一种指令式的绘图系统。

Canvas 能够直接操作绘图上下文,不需要经过 HTML、CSS 解析、构建渲染树、布局等一系列操作。因此单纯绘图的话,Canvas 比 HTML/CSS 和 SVG 要快得多。

Canvas 和 SVG 的使用也不是非此即彼的,它们可以结合使用。因为 SVG 作为一种图形格式,也可以作为 image 元素绘制到 Canvas 中。举个例子,我们可以先使用 SVG 生成某些图形,然后用 Canvas 来渲染。这样,我们就既可以享受 SVG 的便利性,又可以享受 Canvas 的高性能了。

一、canvas 的基本知识

HTML5 中 新增了 <canvas> 元素。<canvas> 元素负责在页面中设定一个区域,然后就可以通过 JavaScript 动态的在这个区域内绘制图形。

1、创建画布

使用 canvas 绘图必须先设置其 width 和 height 属性,来指定一个可以绘图的区域大小。

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

<canvas> 元素与其他 HTML 元素一样,可以通过 CSS 为其添加样式,也可以用 JavaScript 修改其属性的值。

2、创建画笔

要在画布上绘图,需要通过调用 getContext()取得绘图上下文对象。

var drawing = document.getElementById("draw");
if(drawing.getContext){
	var context = drawing.getContext("2d");
}

在使用Canvas前,用 canvas.getContext 来测试浏览器是否支持Canvas。

二、绘制 2D 图形

1、绘制和清除矩形

(1)、绘制矩形

  • fillStyle 属性:设置填充的背景颜色。
  • strokeStyle 属性:设置边框颜色。
  • fillRect(x, y, width, height) 方法:从点(x, y)开始绘制一个矩形,宽度和高度分别是 width 和 height。可以填充它的背景颜色。
  • strokeRect(x, y, width, height) 方法:从点(x, y)开始绘制一个矩形框,宽度和高度分别是 width 和 height。可以指定其边框的颜色,但不能填充背景色。

(2)、清除矩形

clearRect(x, y, width, height) 方法:从点(x, y)开始清除一个宽度和高度分别为 width 和 height 的矩形区域。清除后的区域是透明的。

(3)、实例

var drawing = document.getElementById("draw");
if(drawing.getContext){
	var context = drawing.getContext("2d");

	// 绘制一个填充矩形(红色)
	context.fillStyle = "#f00";
	context.fillRect(10, 10, 50, 50);

	// 绘制一个填充透明矩形(蓝色半透明)
	context.fillStyle = "rgba(0, 0, 255, 0.5)";
	context.fillRect(30, 30, 50, 50);

	// 绘制一个矩形框(绿色边框)
	context.fillStyle = "#f00";// 红色(设置了也不会填充)
	context.strokeStyle = "#0f0";// 设置边框颜色(绿色)
	context.strokeRect(60, 60, 50, 50);

	// 清除一个指定的矩形区域(白色的小块)
	context.clearRect(40, 40, 10, 10);// 清除后形成一个白色的区域
}

 

2、绘制路径

(1)、开启绘制模式

beginPath() 方法:表示要开始绘制新路径了。

var drawing = document.getElementById("draw");
if(drawing.getContext){
	var context = drawing.getContext("2d");
	// 开始绘制路径
	context.beginPath();
}

(2)、绘制路径的属性和方法

  • moveTo(x, y) 方法:将绘图游标移动到(x, y)处,不画线。
  • lineTo(x, y) 方法:从上一点开始绘制一条直线,到(x, y)为止。
  • rect(x, y width, height) 方法:从点(x, y)开始绘制一个矩形,宽度和高度分别是 width 和 height。
  • arc(x, y, radius, startAngle, endAngle, counterclockwise) 方法:以(x, y)为圆心,绘制一条弧线。弧线半径为 radius。起始和结束的角度(用弧度表示)分别为 startAngle 和 endAngle。counterclockwise 用来指定绘制的方向,默认值是 false,表示按顺时针方向绘制路径。
  • arcTo(x1, y1, x2, y2, radius) 方法:从上一点开始绘制一条路径,到(x2, y2)为止,并且以给定的半径 radius 穿过(x1, y1)。
  • bezierCurveTo(c1x, c1y, c2x, c2y, x, y) 方法:从上一点开始绘制一条路径,到(x, y)为止,并且以(c1x, c1y)和(c2x, c2y)为控制点。
  • quadraticCurveTo(cx, cy, x, y) 方法:从上一点开始绘制一条二次曲线,到(x, y)为止,并且以(cx, cy)作为控制点。
  • closePath() 方法:将路径与起点相连,形成封闭图形。
  • fillStyle 属性:用来设置填充封闭图形的背景颜色。
  • strokeStyle 属性:用来设置路径的颜色。
  • stroke() 方法:用来给路径描边。

(3)、实例

var drawing = document.getElementById("draw");
if(drawing.getContext){
	var ctx = drawing.getContext("2d");
	// 开始绘制路径
	ctx.beginPath();
	// 画外圆
	ctx.arc(100, 100, 99, 0, 2*Math.PI, false);
	// 画内圆
	ctx.moveTo(194, 100);// 创建路径的起始点
	ctx.arc(100, 100, 94, 0, 2*Math.PI, false);
	// 画分针
	ctx.moveTo(100, 100);
	ctx.lineTo(100, 15);
	// 时针
	ctx.moveTo(100, 100);
	ctx.lineTo(35, 100);
	// 描边
	ctx.stroke();
}

3、绘制文本

  • font 属性:表示文本的样式、大小及字体。用 CSS 的规则来指定,比如:“10px Arial”。
  • textAlign 属性:表示文本的对齐方式。可选值有:"start"、"end"、"left"、"right" 和 "center"。建议使用 "start"、"end",不建议使用 "left"、"right"。
  • textBaseline 属性:表示文本基线。可选值有:"top"、"haging"、"middle"、"alphabatic"、"ideographic" 和 "bottom"。
  • strokeStyle 属性: 用来指定路径的颜色。
  • fillText() 方法:填充文本。可以接收四个参数:文本字符串、x 坐标、y 坐标 和(可选的)最大像素宽度。
  • strokeText() 方法:通过路径描边实现文本。可以接收四个参数:文本字符串、x 坐标、y 坐标 和(可选的)最大像素宽度。
ctx.font = "bold 14px Arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText("12", 100, 20);

4、变换

(1)、变换的语法

  • rotate(angle) 方法:围绕原点旋转图像 angle 弧度。
  • scale(scaleX,scaleY) 方法:缩放图像。在 x 方向乘以 scaleX,在 y 方向上乘以 scaleX。scaleX 和 scaleY 的默认值都是 1。
  • translate(x, y) 方法:将坐标原点移动到(x, y)处。
  • transform(m1_1, m1_2, m2_1, m2_2, dx, dy) 方法:直接将修改变换矩阵,方式是乘以如下矩阵:
    • m1_1    m1_2    dx
      m2_1    m2_2    dy
      0           0           1
  • setTransform(m1_1, m1_2, m2_1, m2_2, dx, dy) 方法:将变换矩阵重置为默认状态,然后重新调用 transform() 方法。

(2)、变换的步骤

  • 第一步是“变换原点”;
  • 指定“变换弧度”;
  • 绘制要变换的元素。
var drawing = document.getElementById("draw");
if(drawing.getContext){
	var ctx = drawing.getContext("2d");
	// 开始绘制路径
	ctx.beginPath();
	// 画外圆
	ctx.arc(100, 100, 99, 0, 2*Math.PI, false);
	// 画内圆
	ctx.moveTo(194, 100);// 创建路径的起始点
	ctx.arc(100, 100, 94, 0, 2*Math.PI, false);
	// 绘制文本
	ctx.font = "bold 14px Arial";
	ctx.textAlign = "center";
	ctx.textBaseline = "middle";
	ctx.fillText("12", 100, 20);

	// 变换原点
	ctx.translate(100, 100);

	// 旋转表针
	ctx.rotate(1);

	// 画分针
	ctx.moveTo(0, 0);
	ctx.lineTo(0, -85);
	// 时针
	ctx.moveTo(0, 0);
	ctx.lineTo(-65, 0);
	// 描边
	ctx.stroke();
}

顺序不能差,否则出不来下面的效果:

(3)、变换后的状态管理

如何保存当前状态?并在对上下文修改后,如何恢复到之前的状态呢?

有 2 个方法可以跟踪上下文的变化:

  • save() 方法:将当前状态保存在栈中。只保存对绘图的设置和变换,不保存绘图的内容。
  • restore() 方法:从栈中调用前一个状态。用来恢复到前一个状态。只恢复绘图的设置和变换,不恢复绘图的内容。
if(drawing.getContext){
	var ctx = drawing.getContext("2d");
	
	ctx.fillStyle = "#f00";
	ctx.save();
	
	ctx.fillStyle = "#0f0";
	ctx.translate(50, 50);
	ctx.save();
	
	ctx.fillStyle = "#00f";
	ctx.fillRect(0, 0, 100, 50);// 从(50, 50)开始绘制蓝色矩形
	
	ctx.restore();
	ctx.fillRect(20, 20, 100, 50);// 从(70, 70)开始绘制绿色矩形
	
	ctx.restore();
	ctx.fillRect(0, 0, 100, 50);// 从 (0, 0) 开始绘制红色矩形
}

 

5、绘制图像

drawImage() 方法,用来把一个图像绘制到画布上。根据传入参数个数的不同,表现出来的效果也不相同。

drawImage(image, sx, sy, sWidth, sHeight, x, y, width, height) 方法的参数分析:要绘制的图像、原图像的 x 坐标、原图像的 y 坐标、原图像的宽度、原图像的高度、绘制 canvas 对象的 x 坐标、绘制 canvas 对象的 y 坐标、绘制 canvas 对象的宽度 和 绘制 canvas 对象的高度。

(1)、原图绘制

drawImage(image, x, y) 时:要绘制的图像、绘制 canvas 对象的 x 坐标、绘制 canvas 对象的 y 坐标。

<body>
	<img src="./image/666.png">
	<canvas id="draw" width="200" height="200">您的浏览器不支持 HTML5 canvas 标签。</canvas>
</body>
<script type="text/javascript">
	var drawing = document.getElementById("draw");
	var image = document.images[0];
	var ctx = drawing.getContext("2d");
	ctx.drawImage(image, 10, 10);
</script>

 

(2)、按指定大小绘制

drawImage(image, x, y, width, height) 时:要绘制的图像、绘制 canvas 对象的 x 坐标、绘制 canvas 对象的 y 坐标、绘制 canvas 对象的宽度 和 绘制 canvas 对象的高度。

<body>
	<img src="./image/666.png">
	<canvas id="draw" width="200" height="200">您的浏览器不支持 HTML5 canvas 标签。</canvas>
</body>
<script type="text/javascript">
	var drawing = document.getElementById("draw");
	var image = document.images[0];
	var ctx = drawing.getContext("2d");
	ctx.drawImage(image, 10, 10, 90, 100);
</script>

(3)、绘制图像的某一部分

drawImage(image, sx, sy, sWidth, sHeight, x, y, width, height) 时:要绘制的图像、原图像的 x 坐标、原图像的 y 坐标、原图像的宽度、原图像的高度、绘制 canvas 对象的 x 坐标、绘制 canvas 对象的 y 坐标、绘制 canvas 对象的宽度 和 绘制 canvas 对象的高度。

<body>
	<img src="./image/666.png">
	<canvas id="draw" width="200" height="200">您的浏览器不支持 HTML5 canvas 标签。</canvas>
</body>
<script type="text/javascript">
	var drawing = document.getElementById("draw");
	var image = document.images[0];
	var ctx = drawing.getContext("2d");
	ctx.drawImage(image, 30, 30, 90, 100, 0, 0, 40, 60);
</script>

6、阴影

  • shadowColor:用 CSS 颜色格式表示的阴影颜色,默认为黑色。
  • shadowOffsetX:形状或路径 x 轴方向的阴影偏移量,默认值为 0。
  • shadowOffsetY:形状或路径 y 轴方向的阴影偏移量,默认值为 0。
  • shadowBlur:模糊的像素数,默认值为 0,即不模糊。
var drawing = document.getElementById("draw");
if(drawing.getContext){
	var ctx = drawing.getContext("2d");
	
	ctx.shadowOffsetX = 5;
	ctx.shadowOffsetY = 5;
	ctx.shadowBlur = 3;
	ctx.shadowColor = "rgba(0, 0, 0, 0.5)";
	
	ctx.fillStyle = "#00f";
	ctx.fillRect(20, 20, 100, 50);
}

 

7、渐变

渐变分为 线性渐变 和 径向渐变(也叫放射渐变)。

canvas 中的渐变由 CanvasGradient 实例表示:

  • createLinerGradient() 方法:创建“线性渐变”。接收 4 个参数:起点的 x 坐标、起点的 y 坐标、终点的 x 坐标 和 终点的 y 坐标。
  • createRadialGradient() 方法:创建“径向渐变”。接收 6 个参数:起点的 x 坐标、起点的 y 坐标、起点的半径、终点的 x 坐标、终点的 y 坐标 和 终点的半径。

无论是线性渐变还是径向渐变,创建渐变后,都需要使用 addColorStop() 方法来指定色标。

  • addColorStop() 方法接收 2 个参数:色标位置(一个0~1之间的数值) 和 CSS 颜色值。

(1)、线性渐变

var drawing = document.getElementById("draw");
if(drawing.getContext){
	var ctx = drawing.getContext("2d");

	var gradient = ctx.createLinearGradient(30, 30, 70, 70);
	gradient.addColorStop(0, "white");
	gradient.addColorStop(1, "black");
	
	ctx.fillStyle = gradient;
	ctx.fillRect(30, 30, 50, 50);
}

上述代码中,需要注意的是:矩形和渐变对象的开始渐变的坐标必须匹配才行,即都是(30, 30),否则就会发生偏差,导致部分渐变。

确保渐变与形状对齐非常重要,可以考虑使用函数来确保坐标合适:

function createRectLinearGradient(context, x, y, width, height){
	return context.createLinearGradient(x, y, x+width, y+height);
}

(2)、径向渐变

可以把径向渐变想象成一个长圆桶,而这 6 个参数定义的正是这个桶的两个圆形开口的位置。如果把一个圆形开口定义的比另一个小一些,那这个圆桶就变成了圆锥体,而通过移动每个圆形开口的位置,就可以达到像旋转这个圆锥体一样的效果。

如果想从某个形状的中心点开始,创建一个向外扩散的径向渐变效果,就要将两个圆心定义为同心圆。

var drawing = document.getElementById("draw");
if(drawing.getContext){
	var ctx = drawing.getContext("2d");
	
	var gradient = ctx.createRadialGradient(55, 55, 10, 55, 55, 30);
	gradient.addColorStop(0, "white");
	gradient.addColorStop(1, "black");
	
	ctx.fillStyle = gradient;
	ctx.fillRect(30, 30, 50, 50);
}

8、模式

模式就是重复的图像。可以用指定的图像来填充图形或描边图形。

createPattern() 方法:用来创建一个新的模式。该方法接收 2 个参数:一个 <img> 元素 和 一个表示如何重复图像的字符串。第二个参数的可选值与 CSS 的 background-repeat 的值相同:"repeat"、"repeat-x"、"repeat-y" 和 "no-repeat"。

创建好模式后,将填充样式(fillStyle)设置为模式对象,表示:从画布的原点开始,在某个特定区域内,显示重复的指定图像。

var drawing = document.getElementById("draw");
if(drawing.getContext){
	var ctx = drawing.getContext("2d");
	var imge = document.images[0];
	var pattern = ctx.createPattern(imge, "repeat");
	
	ctx.fillStyle = pattern;
	ctx.fillRect(10, 10,150, 150);
}

另外,createPattern() 方法的第一个参数,也可以是一个 <video> 元素,或者是另一个 <canvas> 元素。

9、使用图像数据

2D 上下文可以通过 getImageData() 方法获取原始图像的数据。该方法接收 4 个参数:要取得其数据的画面区域的 x 坐标、要取得其数据的画面区域的 y 坐标、要取得其数据的画面区域的像素宽度 和 要取得其数据的画面区域的像素高度。

getImageData() 方法,返回一个 ImageData 对象的实例。每个 ImageData 对象的实例都有 3 个属性:width、height 和 data。其中 data 属性是一个数组,保存着图像中每一个像素的数据。在 data 数组中,每一个像素 4 个元素来保存,分别是 “红、绿、蓝 和 透明度值”。因此,第一个像素的数据就保存在数组对的第 0 个到第 3 个元素中。数组中每个元素的值都介于 0 到 255 之间(包括 0 和 255)。

例如:要取得左上角坐标为(10, 5)、大小为 50x50 像素的区域的图像数据。

var drawing = document.getElementById("draw");
if(drawing.getContext){
	var ctx = drawing.getContext("2d");
	ctx.fillStyle = "#f00";
	ctx.fillRect(5, 5, 50, 50);
	
	var imgData = ctx.getImageData(10, 5, 50, 50);

	var width, height, red, green, blue, alpha;
	width = imgData.width;
	height = imgData.height;
	red = imgData.data[0];
	green = imgData.data[1];
	blue = imgData.data[2];
	alpha = imgData.data[3];

	console.log([width, height, red, green, blue, alpha]);// [50, 50, 255, 0, 0, 255]
}

获取到图像数据后,可以通过操作原始像素值实现灰阶过滤功能,还可以实现其他的功能,具体请戳此链接:Learn

10、合成

以下两个属性,会直接影响整个 2D 上下文中的所有绘制操作:

  • globalAlpha 属性:是一个介于 0 到 1 之间的值(包括 0 和 1),用于指定所有绘制的透明度。默认值为 0。
  • globalCompositionOperation 属性:表示后绘制的图形怎样与先绘制的图像相结合。这个属性的值是字符串,可选的值如下:
    • source-over:默认值。在目标图像上显示源图像。
    • source-atop:在目标图像顶部显示源图像。源图像位于目标图像之外的部分是不可见的。
    • source-in:在目标图像中显示源图像。只有目标图像内的源图像部分会显示,目标图像是透明的。
    • source-out:在目标图像之外显示源图像。只会显示目标图像之外源图像部分,目标图像是透明的。
    • destination-over:在源图像上方显示目标图像。
    • destination-atop:在源图像顶部显示目标图像。源图像之外的目标图像部分不会被显示。
    • destination-in:在源图像中显示目标图像。只有源图像内的目标图像部分会被显示,源图像是透明的。
    • destination-out:在源图像外显示目标图像。只有源图像外的目标图像部分会被显示,源图像是透明的。
    • lighter:显示源图像 + 目标图像。
    • copy:显示源图像。忽略目标图像。
    • xor:使用异或操作对源图像与目标图像进行组合。

(1)、globalAlpha 属性的应用

如果所有的后续操作都基于一个相同的透明度,就可以先把 globalAlpha  设置为适当的值,然后绘制,最后再把它设置回默认值 0。

var drawing = document.getElementById("draw");
if(drawing.getContext){
	var ctx = drawing.getContext("2d");
	
	ctx.fillStyle = "#f00";
	ctx.fillRect(10, 10, 50, 50);
	
	// 修改全局透明度
	ctx.globalAlpha = 0.5;
	
	ctx.fillStyle = "#00f";
	ctx.fillRect(30, 30, 50, 50);
	
	// 重置全局透明度
	ctx.globalAlpha = 0;
}

(2)、globalCompositionOperation 属性的应用

globalCompositionOperation 属性表示后绘制的图形怎样与先绘制的图像相结合。

globalCompositionOperation 属性的应用,请戳此链接:https://www.jianshu.com/p/ddb3fc0b66a1

三、绘制 3D 图形

绘制 3D 图形,使用的上下文是 WebGL,该上下文与 2D 上下文不同,它不是 W3C 制定的标准,而是由 Khronos Group 制定的。WebGL 是基于他们推出的 OpenGL 来制定的。

WebGL 原生的API是一种非常低级的接口,而且还需要一些数学和图形学的相关技术。对于没有相关基础的人来说,入门比较困难。

【推荐可视化实现库】

Three.js 的学习资源:Three.js中文网

e-charts 的学习资源:Apache ECharts

antdV 的学习资源:AntV | 蚂蚁数据可视化

D3.js 的学习资源:D3.js 官网 和 D3.js 中文教程

SVG 的学习资源:SVG 教程 MDN 和 SVG 教程菜鸟

四、导出在 HTML 的 canvas 元素上绘制的图像

通过 toDataURL() 方法导出在 <canvas> 元素上绘制的图像。

默认情况下,导出的图像是 png 格式的,当然也可以另行指定导出的格式。

var drawing = document.getElementById("draw");
if(drawing.getContext){
	var context = drawing.getContext("2d");

	// 绘制一个填充矩形
	context.fillStyle = "#f00";
	context.fillRect(10, 10, 50, 50);

	// 绘制一个填充透明矩形
	context.fillStyle = "rgba(0, 0, 255, 0.5)";
	context.fillRect(30, 30, 50, 50);

	// 绘制一个矩形框
	context.strokeRect(60, 60, 50, 50);

	// 清除一个指定的矩形区域
	context.clearRect(40, 40, 10, 10);

	// 导出 canvas 上绘制的图像
	var imgURI = drawing.toDataURL(context);
	var image = document.createElement("img");
	image.src = imgURI;
	document.body.appendChild(image);
}

五、canvas 报错汇总

1、 报错:canvas.getContext is not a function.

在使用 canvas 创建画笔时,必须是基于 <canvas></canvas> 标签的。

<!-- 错误 -->
<div id="myCanvas"></div>

你可以像下面这样使用 canvas:

<html>
    <!-- 正确 -->
    <canvas id="myCanvas" ></canvas>
</html>
<body>
<script>
    const canvas = document.getElementById("myCanvas");
    console.log('canvas: ', canvas);

    const context = canvas.getContext("2d");
    console.log('context: ', context);

</script>
</body>

 

  • 3
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值