【实现自己的可视化引擎01】认识Canvas

1,791 阅读5分钟

工欲善其事,必先利其器。要想打造自己的可视化引擎,那就必须要先熟悉Cavas API。Canvas具体教程可以参考Canvas MDN。下面我们将对Cavas API进行简单的说明。

基本用法

<canvas>是HTML 5 新增的元素,在浏览器创建一个固定大小的画布。通过Document.getElementById() 方法获取HTML <canvas> 元素的引用。HTMLCanvasElement.getContext() 方法获取该画布上的上下文context,我们可以称之为画笔。通过画笔,我们就可以在画布上绘制图形了。下面时一个简单的官方绘制绿色矩形的示例:

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'green';        // 设置画笔颜色——绿色
ctx.fillRect(10, 10, 150, 100); // 在坐标(10, 10)处绘制宽为150,高为100的长方形

绘图API

Cavas为我们提供了许多绘图API,我们将简单的介绍本系列文章中常用的API,其他具体API可以参见Canvas MDN

绘制函数

  • 绘制矩形
      清除矩形区域 clearRect(x, y, width, height);
      填充矩形区域 fillRect(x, y, width, height);
      绘制矩形边框 strokeRect(x, y, width, height);
  • 路径
       新建路劲 beginPath();
       移动画笔至指定坐标 moveTo(x, y);
       从当前位置到指定坐标的直线 lineTo(x, y);
       在指定坐标绘制半径的圆弧 arc(x, y, radius, startAngle, endAngle, anticlockwise);
       绘制二次贝塞尔曲线 quadraticCurveTo(cp1x, cp1y, x, y);
       绘制三次贝塞尔曲线 bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
       闭合路径 closePath();
       绘制图形轮廓 stroke();
       填充图形路径 stroke();
  • 文本
       指定位置填充文本 fillText(text, x, y [, maxWidth]);    指定位置绘制文本边框 strokeText(text, x, y [, maxWidth]);
  • 图像
       绘制指定图像 drawImage(image, sx, sy, [sWidth, sHeight, dx, dy, dWidth, dHeight]);

样式

  • 颜色:fillStyle(填充颜色)/strokeStyle(轮廓颜色);
  • 透明度: globalAlpha;
  • 线性: lineWidth(线宽)/lineCap(末端样式)/lineJoin(线条接合样式)/miterLimit(交接处最大长度);
  • 渐变: createRadialGradient(x1, y1, r1, x2, [y2, r2])/addColorStop(position, color)
  • 阴影: shadowOffsetX(X轴阴影)/shadowOffsetY(Y轴阴影)/shadowBlur(模糊程度)/shadowColor(阴影颜色)

其他

  • save() 保存画笔状态
  • restore() 回复画笔保存时状态快照
  • translate(x, y) 画布位置偏移
  • rotate(angle) 画布旋转
  • scale(x, y) 画布缩放

下面一个例子,我们将对上面API做一个简单的应用

<canvas id="canvas" width="500" height="500"></canvas>
<button onclick="draw('rect')">长方形</button>
<button onclick="draw('rect_str')">长方形框</button>
<button onclick="draw('angle3')">三角形</button>
<button onclick="draw('arc')">圆弧</button>
<button onclick="draw('bezier')">贝塞尔绘制爱心</button>
<script type="text/javascript">
    let canvas = document.getElementById('canvas');
    let ctx = canvas.getContext('2d');
    function draw(type) {
    	let posX = Math.round(Math.random() * 500);
    	let posY = Math.round(Math.random() * 500);
    	if (type === 'rect') {
    		ctx.fillRect(posX, posY, 100, 150);
    	} else if(type === 'rect_str') {
    		ctx.strokeRect(posX, posY, 100, 100);
    	} else if(type === 'angle3') {
    		ctx.beginPath();
    		ctx.moveTo(posX, posY);
    		ctx.lineTo(posX + 50, posY - 50);
    		ctx.lineTo(posX, posY - 100);
    		ctx.closePath();
    		ctx.fill();
    	} else if(type === 'arc') {
    		ctx.beginPath();
    		ctx.arc(posX, posY, 50, 0, Math.PI * 2, true);
    		ctx.closePath();
    		ctx.stroke();
    	} else if(type === 'bezier') {
    		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();
    	} else if(type === 'text') {
    		ctx.font="20px Georgia";
    		ctx.fillText("Hello World!", posX, posY);
    	}
    }
</script>

封装Canvas

Canvas类

由上一节例子可以看出,对于每次绘制图形时必须创建<canvas>标签,同时保存画笔对象context,并通过context绘制图形,为了后续更好的管理画布,我们这里对Canvas进行了封装。本引擎以面向对象的方式管理图元。

认识CanvasWidth属性和样式Width的区别

在封装Canvas类之前,我们先来了解一下canvas width 属性与canvas 样式width的区别。width属性是Canvas画布的宽度,样式width是canvas文档流中的宽度。样式width是canvas在文档流中实际的像素大小,width属性是Canvas画布的宽度,相对于画笔而言的虚拟宽度,可以理解为画笔所能精确到的细度。假设canvas的样式width为300px, canvas的width属性为600px,这意味着画笔能够绘制相对于文档页面0.5px的宽度。默认情况下Canvas的Canvas Width属性和样式属性为1:1。

代码

class Canvas {
	constructor(config) {
		if (config.ele === undefined) {
		  throw new Error('Not found config of canvas element');
		}
		// canvas 标签的容器标签
		this.container = config.ele;
		// 设置canvas width属性与样式width 的比率
		this.ratio = config.ratio || 2;
		// 创建 canvas 标签
		this.canvas = document.createElement('canvas');
		this.childs = [];
		this.init();
	}
	
	/**
	* 重新定义Canvas的大小
	*/
	repaint() {
		this.container.innerHTML = '';
		this.canvas = document.createElement('canvas');
		this.init();
	}
	
	/**
	* 初始化Canvas系数
	*/
	init() {
		// 获取容器的样式
		const styles = getComputedStyle(this.container, null);
		// 容器的宽
		const width = parseInt(styles.width);
		// 容器的高
		const height = parseInt(styles.height);
		// 设置canvas的样式宽
		this.canvas.style.width = `${width}px`;
		// 设置canvas的样式高
		this.canvas.style.height = `${height}px`;
		// 根据比率设置相应的属性宽高
		this.canvas.width = this.ratio * width; //设置缩放比
		this.canvas.height = this.ratio * height;
		// 去除点击选中样式
		this.canvas.style.outline = 'none';
		this.canvas.onclick = (e) => { this.canvas.focus(); };
		this.container.appendChild(this.canvas);
		// 设置画笔属性
		this.painter = this.canvas.getContext('2d');
	}
}

由上述代码,我们只需要对新建一个Canvas类,就能获取到画笔工具,使用例子如下:

<style>
.bigger{
	width: 200px;
	height:200px;
}
</style>
<div class="bigger" id="canvas" />
let dv = document.getElementById('canvas');
let canvas = new Canvas({
	ele: dv
});
canvas.painter.fillRect(0, 0, 100, 150);

目录

【实现自己的可视化引擎01】认识Canvas
【实现自己的可视化框架引擎02】抽象图像元素
【实现自己的可视化引擎03】构建基础图元库
【实现自己的可视化引擎04】图像元素动画
【实现自己的可视化引擎05】交互与事件
【实现自己的可视化引擎06】折线图
【实现自己的可视化引擎07】柱状图
【实现自己的可视化引擎08】条形图
【实现自己的可视化引擎09】饼图
【实现自己的可视化引擎10】散点图
【实现自己的可视化引擎11】雷达图
【实现自己的可视化引擎12】K线图
【实现自己的可视化引擎13】仪表盘
【实现自己的可视化引擎14】地图
【实现自己的可视化引擎15】关系图