可视化图形基础-用Canvas绘制基本几何图形

664 阅读6分钟

什么是 Canvas

canvas标签是一个可以使用脚本(通常是JavaScript)来绘制图形的HTML元素。它可以用于:

  • 绘制图表
  • 制作图片构图
  • 制作简单的(及不那么简单的)动画

最早由Apple引入WebKit, 用于Mac OS X 的Dashboard, 随后被各个浏览器实现。如今,所有主流的浏览器都支持它。

基本用法

canvas标签只有两个属性:width 和 height

<canvas id='tutorial' width='150' height='150'></canvas>

width、height这两个属性是可选的。没有设置宽、高时,canvas会初始化宽度为300像素、高度为150像素。

id 属性并非元素所特有的,而是每一个HTML元素都默认具有的属性(比如class属性)。给标签加上id属性是个好主意,这样就能很容易地找到它。

元素可以有margin, border, background 等属性。然而,这些样式不会影响在canvas中的实际图像。如果没有为canvas规定样式,它将会完全透明。

写在两个标签之间的内容通常不会被渲染

<canvas id="stockGraph" width="150" height="150">
	current stock price: $3.15 +0.15 //后备内容
</canvas>

<canvas id="clock" width="150" height="150">
  <img src="images/clock.png" width="150" height="150" alt=""/> //后备内容
</canvas>

不支持的浏览器会忽略容器并渲染后备内容;

支持的浏览器会忽略后备内容,仅正常渲染canvas。

标签不可省略

元素需要结束标签。

如果结束标签不存在,则剩余部分将会被认为“后备内容”。

渲染上下文

什么是“渲染上下文(The rendering context)”?

  • 元素创造了一个固定大小的画布,它公开了一个或多个渲染上下文,它可以用来绘制和处理要展示的内容。
  • 起初是空白的。为了在其上绘图,首先javascript需要找到渲染上下文。

获取渲染上下文

const canvas = document.getElementById('tutorial') //获取Dom对象
const ctx = canvas.getContext('2d') //获取渲染上下文和canvas的绘画功能
  • 元素有一个叫做 getContext() 的方法,调用这个方法可以获得渲染上下文和它的绘画功能。
  • getContext() 接收一个参数,即上下文的类型。对于2d图像而言,可以使用CanvasRenderingContext2D

上下文类型(contextType):2d 、webgl、webgl2、bitmaprenderer

栅格与坐标空间

栅格(canvas grid)

canvas 元素默认被网格所覆盖。网格中的一个单元相当于canvas元素中的一像素。

坐标空间

栅格的起点为左上角(坐标为(0,0)),所有元素都相对于原点定位。

图中蓝色方形左上角的坐标为距离左边x像素,距离上边y像素,坐标为(x,y)。

使用Canvas绘制图形

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

绘制矩形

canvas提供了三种方法绘制矩形:

  • fillRect(x, y, width, height) 绘制一个填充的矩形。
  • strokeRect(x, y, width, height) 绘制一个矩形的边框。
  • clearRect(x, y, width, height) 清楚指定矩形区域,让清除部分完全透明。

x 与 y 指定了在 canvas 画布上所绘制的矩形的左上角的坐标,width 和 height 设置矩形的尺寸。

function draw(){
  var canvas = document.getElementById('canvas')
  if(canvas.getContext){
    var ctx = canvas.getContext('2d')
    
    ctx.fillRect(25, 25, 100, 100)
    ctx.clearRect(45, 45, 60, 60)
    ctx.strokeRect(50, 50, 50, 50)
  }
}

绘制路径

图形的基本元素是路径,路径是通过不同颜色和宽度的线段或曲线相连形成的不同形状的点的集合。

使用路径绘制图形的步骤

  1. beginPath():创建路径起始点 。路径是由很多子路径构成的,这些子路径都在一个列表中,所有的子路径(线、弧形...)构成图形。每次调用beginPath()后,列表清空重置。然后我们就可以绘制新的图形。
  2. 使用画图命令去画出路径。
  3. closePath():把路径封闭 。closePath()通过绘制一条从当前点到起始点的直线来闭合图形。如果图形已经是闭合了的(即当前点为起始点),则closePath()什么也不做。
  4. 描边 stroke() 或填充 fill() 。一旦路径生成,你就能通过描边或填充路径区域来渲染图形。

描边 stroke() :通过线条来绘制图形轮廓。

填充 fill():通过填充路径的内容区域生成实心的图形。

当调用fill()函数时,所有没有闭合的形状都会自动闭合,所以不需要调用closePath()函数。

但是,调用stroke()时不会自动闭合。

移动笔触 - moveTo()

moveTo()函数不能绘制出任何东西,但却非常有用。moveTo(x, y) 能将笔触移动到指定的坐标(x,y)上。

function draw(){
	var canvas = document.getElementById('canvas')
  if(canvas.getContext){
  	var ctx = canvas.getContext('2d')
    
    ctx.beginPath()
    ctx.arc(75, 75, 50, 0, Math.PI * 2, true)
    ctx.moveTo(110, 75)
    ctx.arc(75, 75, 35, 0, Math.PI, false) //口(顺时针)
    ctx.moveTo(65, 65)
    ctx.arc(60, 65, 5, 0, Math.PI * 2, true) //左眼
    ctx.moveTo(95, 65)
    ctx.arc(90, 65, 5, 9, Math.PI * 2, true) //右眼
    ctx.stroke()
  }
}

绘制直线 - lineTo()

lineTo(x, y) 绘制一条从当前位置到指定位置(x, y)的直线。x 以及 y 代表直线结束的坐标。

开始坐标和之前的绘制路径有关,之前路径的结束点就是接下来的开始点。开始点页可以通过moveTo函数改变。

function draw(){
	var canvas = document.getElementById('canvas')
  if(canvas.getContext){
  	var ctx = canvas.getContext('2d')
    
    //填充三角形
    ctx.beginPath()
    ctx.moveTo(25, 25)
    ctx.lineTo(105, 25)
    ctx.lineTo(25, 105)
    ctx.fill()
    
    //描边三角形
    ctx.beginPath()
    ctx.moveTo(125, 125)
    ctx.lineTo(125, 45)
    ctx.lineTo(45, 125)
    ctx.closePath()
    ctx.stroke()
  }
}

绘制圆弧 - arc(x, y, redius, startAngle, enAngle, anticlockwise)

画一个以 (x,y) 为圆心、以radius为半径的圆弧(圆),从startAngle开始到endAngle结束,按照anticlockwise给定的方向(默认为顺时针,true为逆时针,false为顺时针)来生成。

function draw(){
	var canvas = document.getElementById('canvas')
  if(canvas.getContext){
    const ctx = canvas.getContext('2d')
    
    ctx.beginPath()
    ctx.arc(50, 50, 20, 30, 50, true)
    ctx.closePath()
    ctx.stroke()
    
    ctx.beginPath()
    ctx.arc(100, 100, 20, 30, 50, true)
    ctx.fill()
  }
}

用二次贝塞尔曲线绘图 - quadraticCurveTo(cp1x, cp1y, x, y)

  • 什么是二次贝塞尔曲线?

一个开始点(蓝色)

一个结束点(蓝色)

一个控制点(红色)

  • 参数的含义

cp1x, cp1y 为一个控制点的坐标;x, y 为结束点的坐标。

function draw(){
	var canvas = document.getElement('canvas')
  if(canvas.getContext){
  	var ctx = canvas.getContext('2d')
    
    //二次贝塞尔曲线
    ctx.beginPath()
    ctx.moveTo(75, 25)
    ctx.quadraticCurveTo(25, 25, 25, 62.5)
    ctx.quadraticCurveTo(25, 100, 50, 100)
    ctx.quadraticCurveTo(50, 120, 30, 125)
    ctx.quadraticCurveTo(60, 120, 65, 100)
    ctx.quadraticCurveTo(125, 100, 125, 62.5)
    ctx.quadraticCurveTo(125, 25, 75, 25)
    ctx.stroke()
 	}
}

用三次贝塞尔曲线绘图 - bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)

  • 什么是二次贝塞尔曲线?

一个开始点(蓝色)

一个结束点(蓝色)

两个控制点(红色)

  • 参数的含义

cpx1, cpy1 为第一个控制点的坐标

cpx2, cpy2 为第二个控制点的坐标

x, y 为结束点的坐标

function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext){
    var ctx = canvas.getContext('2d');

     //三次贝塞尔曲线
    ctx.beginPath();
    ctx.moveTo(75, 40);
    ctx.bezierCurveTo(75, 37, 70, 25, 50, 25);
    ctx.bezierCurveTo(20, 25, 20, 62.5, 20, 62.5);
    ctx.bezierCurveTo(20, 80, 40, 102, 75, 120);
    ctx.bezierCurveTo(110, 102, 130, 80, 130, 62.5);
    ctx.bezierCurveTo(130, 62.5, 130, 25, 100, 25);
    ctx.bezierCurveTo(85, 25, 75, 37, 75, 40);
    ctx.fill();
  }
}

绘制矩形 - rect(x, y, width, height)

除了直接在画布上绘制矩形的三个方法外,还可以通过rect()方法,将一个矩形路径添加到当前路径上。

rect(x, y, width, height)绘制一个左上角坐标为(x,y),宽为width,高为height的矩形。

ctx.beginPath()
ctx.rect(50, 50, 20, 30)
ctx.closePath()
ctx.stroke

ctx.beginPath()
ctx.rect(100, 100, 20, 30)
ctx.fill()

参考资料:developer.mozilla.org/zh-CN/docs/…