mdn 文档
canvas 能够用于动画、游戏画面、数据可视化、图片编辑以及实时视频解决等内容。
canvas api 次要聚焦于 2D 图形。
WebGL api 次要聚焦于硬件加速的 2D 和 3D 图形。
先来看一个根底事例:
<canvas id="canvas"> Your browser not support canvas, please update browser.</canvas><script> function draw() { // 获取 canvas 元素 const canvas = document.getElementById('canvas'); // 创立画板 const context = canvas.getContext('2d'); // 填充形态 context.fillRect(10, 10, 55, 50); // 填充色彩 context.fillStyle = 'black'; } draw();</script>
这时,浏览器会呈现一个彩色的方块:
绘制图形
首先学习如何绘制矩形。
绘制矩形
canvas 提供了 3 种办法来绘制矩形:
- fillRect(x, y, width, height): 绘制了一个填充的矩形;
- strokeRect(x, y, width, height): 绘制了一个矩形的边框;
- clearRect(x, y, width, height): 分明指定矩形区域,让分明局部齐全通明;
- rect(x, y, width, height): 矩形门路
应用以上三个办法构建一个空心矩形:
function draw() { const canvas = document.getElementById('canvas'); const context = canvas.getContext('2d'); context.fillRect(25, 25, 100, 100); context.clearRect(50, 50, 50, 50);}draw();
如图下所示:
绘制门路
绘图的根本元素是门路。
门路:通过不同色彩和宽度的线段或曲线相连造成的不同形态的点的汇合
一下是所须要的函数:
- beginPath(): 新建一条门路,生成之后,图形绘制命令被指向到门路上生成门路;
- closePath(): 闭合门路之后图形绘制命令又从新指向到上下文中;
- stroke(): 通过线条来绘制图形轮廓;
- fill(): 通过填充门路的内容区域生成实心的图形了;
用以上的办法来构建一个矩形:
function draw() { const canvas = document.getElementById('canvas'); const context = canvas.getContext('2d'); context.beginPath(); context.moveTo(100, 100); context.lineTo(100, 0); context.lineTo(0, 0); context.lineTo(0, 100); context.fill();}draw();
生成门路:
- beginPath(): 实质上,门路是由很多子门路形成,这些子门路都是在一个列表中,所有的子门路来形成图形。每次调用这个办法,列表都会清空,而后就能够从新绘制新的图形了;
- moveTo(): 设置门路之后,指定起始地位;
- lineTo(): 调用函数绘制门路;
- closePath(): 闭合门路(非必须),在调用 fill() 函数时,所有没有闭合的形态都会主动闭合。
挪动笔
moveTo(): 从画布上的一个点挪动到另外一个点。
绘制直线
应用 lineTo(x, y) 办法:从以后地位到 (x, y) 的领导地位的线段。
绘制圆弧
- arc(x, y, radius, startAngle, endAngle, anticlockwise): 画一个以 (x, y) 为圆心的以 radius 为半径的圆弧/园。从 startAngle 开始,到 endAngle 完结,依照 anticlockwise 给定的方向。
- arcTo(x1, y1, x2, y2, radius): 依据给定的控制点和半径画一段圆弧,再以直线链接两个控制点。
arc()
函数中,示意角度的单位是弧度,不是角度。角度转弧度的公式为:弧度 = 角度 * (/ 180)
应用以上办法,画一个实心圆:
function draw() { const canvas = document.getElementById('canvas'); const context = canvas.getContext('2d'); context.beginPath(); context.moveTo(50, 50); context.arc(50, 50, 25, 0, 2 * Math.PI, true); context.fill();}draw();
如图下所示:
Path2D
为了简化代码以及进步性能,咱们能够应用 Path2D 用来缓存或者记录绘画命令。
来试验一下,来画一个内切圆:
function draw() { const canvas = document.getElementById('canvas'); const context = canvas.getContext('2d'); const rectangle = new Path2D(); rectangle.rect(0, 0, 100, 100); const circle = new Path2D(); circle.moveTo(50, 50); circle.arc(50, 50, 50, 2 * Math.PI, false); context.stroke(rectangle); context.stroke(circle);}draw();
SVG Path
能够应用 svg path data 来初始化 canvas 下面的门路
到当初为止,咱们能够应用一系列门路来将咱们所须要的图形,绘画在画布下面了。大体上的步骤如下:
- 创立画布
- 开始门路
- 设置初始点
- 绘画门路
- 闭合门路
示例
接下来,就通过下面学习到的常识来画一个简略的柱状图:
轴线
生成轴线:
// 坐标轴生成createAxle(type = 'x') { const {point, axleLength} = this.defaults; const axle = new Path2D(); axle.moveTo(...point); if (type === 'x') { axle.lineTo(point[0] + axleLength, point[1]); } else { axle.lineTo(point[0], point[1] - axleLength); } this.context.stroke(axle);}
刻度
生成刻度:
// 生成刻度createScale(type = 'x') { const {point, count, axleLength, scaleLength} = this.defaults; const ruleWidth = parseInt(axleLength / count, 10); const scale = new Path2D(); for (let i = 0; i <= axleLength; i += ruleWidth) { if (type === 'x') { scale.moveTo(point[0] + i, point[1]); scale.lineTo(point[0] + i, point[1] + scaleLength); } else { scale.moveTo(point[0], point[1] - i); scale.lineTo(point[0] - scaleLength, point[1] - i); } } this.context.stroke(scale);}
画出柱状图
最初一步,咱们就要画出柱状图的要害,柱子了。
// 画柱状图createRect() { const {point, axleLength, count} = this.defaults; const rectLength = parseInt(axleLength / count, 10); const rect = new Path2D(); for (let i = 0; i <= count; i += 1) { rect.rect( point[0] + (rectLength * i), point[1], rectLength, -rectLength * i ); } this.context.fill(rect);}
残缺代码
class LineChart { constructor(config) { const canvas = document.getElementById('canvas'); this.context = canvas.getContext('2d'); this.defaults = { point: [50, 120], axleLength: 100, count: 5, scaleLength: 5 }; Object.assign(this.defaults, config); } // 坐标轴生成 createAxle(type = 'x') { const {point, axleLength} = this.defaults; const axle = new Path2D(); axle.moveTo(...point); if (type === 'x') { axle.lineTo(point[0] + axleLength, point[1]); } else { axle.lineTo(point[0], point[1] - axleLength); } this.context.stroke(axle); } // 坐标标尺 createScale(type = 'x') { const {point, count, axleLength, scaleLength} = this.defaults; const ruleWidth = parseInt(axleLength / count, 10); const scale = new Path2D(); for (let i = 0; i <= axleLength; i += ruleWidth) { if (type === 'x') { scale.moveTo(point[0] + i, point[1]); scale.lineTo(point[0] + i, point[1] + scaleLength); } else { scale.moveTo(point[0], point[1] - i); scale.lineTo(point[0] - scaleLength, point[1] - i); } } this.context.stroke(scale); } // 画柱状图 createRect() { const {point, axleLength, count} = this.defaults; const rectLength = parseInt(axleLength / count, 10); const rect = new Path2D(); for (let i = 0; i <= count; i += 1) { rect.rect( point[0] + (rectLength * i), point[1], rectLength, -rectLength * i ); } this.context.fill(rect); } draw() { this.createAxle('x'); this.createAxle('y'); this.createScale('x'); this.createScale('y'); this.createRect(); }}const lineChart = new LineChart();lineChart.draw();
后果如下所示:
剩下的工作还有一些,之后在更新吧!
- [ ] 丑化这个柱状图啊,当初这个太黑了
- [ ] 标上刻度
- [ ] 依据数据来对柱子进行调节