前言
由于echarts是基于zrender进行实现的,所以要解读echarts源码,首先要对zrender有大致的了解,在这篇文章中,我将对zrender的整体结构进行大致的解读。
介绍zrender是canvas的一个类库,也就是说zrender是基于canvas实现的,canvas API可以参考:W3CSchool Canvas 参考手册
在后续的解读中,我们会介绍zrender使用canvas api 的位置及用途。
目录介绍文件夹:
- animation 动画有关;
- contain 包含判断;
- container Group.js 元素组的概念;
- core 核心代码,包含一些工具(util.js)、事件(event.js)、唯一ID(guid.js)、矩阵运算有关(matrix.js)等;
- dom HandleProxy.js dom事件有关;
- graphic 图形有关,shape文件夹下就是各个图形的js文件;
- mixin 混入模式要混入的函数;
- tool 工具函数,包括颜色工具(color.js),path工具(path.js)和转换工具(transformPath.js);
- vml IE中的画笔,[vml解释](http://www.g168.net/txt/vml/])
全局的文件:
- config.js 配置文件
- Element.js 元素文件作为zrender最基本的元素
- Handle.js C层,控制层
- Layer.js 图层管理
- Painter.js V层,视图层
- Storage.js M层,数据管理层
- zrender.js 入口
zrender采用了MVC封装,M为Model数据层,V为View视图层,C为Controller控制层:
- Storage(M):Storage为zrender中的Model层,它主要进行图形数据的增删改查(CRUD)操作;
- Painter(V):Painter为zrender中的View层,它主要对canvas元素的生命周期进行管理,渲染视图以及控制更新等。定义了addHover、setBackgroundColor等方法;
- Handler(C):Handler为zrender中的Controller层,它主要实现事件交互处理,实现完整dom事件的模拟封装。
处于graphic文件夹下,zrender定义了一系列图形,供外界调用,包括圆形、矩形、心形、扇形、多边形、折线等,echarts通过调用zrender定义好的图形,可以很便捷地完成图表渲染。
Displayable
Displayable.js为Path、Text等的基类,继承自Element,定义了setStyle、useStyle等方法,
Text
文本,继承自Displayable,调用了helper/text.js中的renderText方法进行绘制。主要代码如下:
export function renderText(hostEl, ctx, text, style, rect, prevEl) { style.rich ? renderRichText(hostEl, ctx, text, style, rect, prevEl) : renderPlainText(hostEl, ctx, text, style, rect, prevEl); }
文本分为富文本以及普通文本进行分别渲染,它们均通过调用canvas的ctx.fillText或ctx.strokeText 等api进行文本渲染,具体实现可以查看helper/text.js文件。
Style
图形的样式文件,定义了基本的样式属性,以及样式set、clone以及判断等方法。
Path
path为图形的基类,继承自Displayable,定义了buildPath、getBoundingRect、setShape等方法,其包含this._style属性即为Style对象实例。
Shape
shape文件下存放着zrender定义的图形文件,扩展自Path。
Circle
圆形,通过调用Path.extend方法进行扩展,重写了buildPath方法,buildPath调用了canvas api:ctx.arc进行圆形的绘制,主要实现代码如下:
buildPath: function (ctx, shape, inBundle) { if (inBundle) { ctx.moveTo(shape.cx + shape.r, shape.cy); } ctx.arc(shape.cx, shape.cy, shape.r, 0, Math.PI * 2, true); }
Arc
弧形,通过调用Path.extend方法进行扩展,重写了buildPath方法,buildPath调用了canvas api:ctx.arc进行弧形的绘制,主要实现代码如下:
buildPath: function (ctx, shape) { var x = shape.cx; var y = shape.cy; var r = Math.max(shape.r, 0); var startAngle = shape.startAngle; var endAngle = shape.endAngle; var clockwise = shape.clockwise; var unitX = Math.cos(startAngle); var unitY = Math.sin(startAngle); ctx.moveTo(unitX * r + x, unitY * r + y); ctx.arc(x, y, r, startAngle, endAngle, !clockwise); }
Heart
心形❤️,通过调用Path.extend方法进行扩展,重写了buildPath方法,buildPath调用了canvas api:ctx.bezierCurveTo进行弧形的绘制,主要实现代码如下:
buildPath: function (ctx, shape) { var x = shape.cx; var y = shape.cy; var a = shape.width; var b = shape.height; ctx.moveTo(x, y); ctx.bezierCurveTo( x + a / 2, y - b * 2 / 3, x + a * 2, y + b / 3, x, y + b ); ctx.bezierCurveTo( x - a * 2, y + b / 3, x - a / 2, y - b * 2 / 3, x, y ); }
bezierCurveTo(控制点1x坐标, 控制点1y坐标, 控制点2x坐标, 控制点2y坐标, 结束点x坐标, 结束点y坐标)
三次被塞尔曲线绘制:
- P0为开始点,P1为控制点1,P2为控制点2,P3为结束点
- 取 P0P1 中点M
- 取 P1P2 中点M’
- 取 MM’ 中点P
- 则 P 为曲线的必经点
具体绘制图如下:
同理,我们可以绘制出左半边心形:
Droplet
水滴形状????,通过调用Path.extend方法进行扩展,重写了buildPath方法,buildPath调用了canvas api:ctx.bezierCurveTo进行弧形的绘制,贝塞尔曲线的绘制过程同心形绘制原理,主要实现代码如下:
buildPath: function (ctx, shape) { var x = shape.cx; var y = shape.cy; var a = shape.width; var b = shape.height; ctx.moveTo(x, y + a); ctx.bezierCurveTo( x + a, y + a, x + a * 3 / 2, y - a / 3, x, y - b ); ctx.bezierCurveTo( x - a * 3 / 2, y - a / 3, x - a, y + a, x, y + a ); ctx.closePath(); }
Ellipse
椭圆,通过调用Path.extend方法进行扩展,重写了buildPath方法,buildPath调用了canvas api:ctx.bezierCurveTo进行弧形的绘制,贝塞尔曲线的绘制过程同心形绘制原理,主要实现代码如下:
buildPath: function (ctx, shape) { var k = 0.5522848; var x = shape.cx; var y = shape.cy; var a = shape.rx; var b = shape.ry; var ox = a * k; // 水平控制点偏移量 var oy = b * k; // 垂直控制点偏移量 // 从椭圆的左端点开始顺时针绘制四条三次贝塞尔曲线 ctx.moveTo(x - a, y); ctx.bezierCurveTo(x - a, y - oy, x - ox, y - b, x, y - b); ctx.bezierCurveTo(x + ox, y - b, x + a, y - oy, x + a, y); ctx.bezierCurveTo(x + a, y + oy, x + ox, y + b, x, y + b); ctx.bezierCurveTo(x - ox, y + b, x - a, y + oy, x - a, y); ctx.closePath(); }
Sector
扇形,通过调用Path.extend方法进行扩展,重写了buildPath方法,buildPath调用了canvas api:ctx.arc以及arc.lineTo进行圆形的绘制,主要实现代码如下:
buildPath: function (ctx, shape) { var x = shape.cx; var y = shape.cy; var r0 = Math.max(shape.r0 || 0, 0); var r = Math.max(shape.r, 0); var startAngle = shape.startAngle; var endAngle = shape.endAngle; var clockwise = shape.clockwise; var unitX = Math.cos(startAngle); var unitY = Math.sin(startAngle); ctx.moveTo(unitX * r0 + x, unitY * r0 + y); ctx.lineTo(unitX * r + x, unitY * r + y); ctx.arc(x, y, r, startAngle, endAngle, !clockwise); ctx.lineTo( Math.cos(endAngle) * r0 + x, Math.sin(endAngle) * r0 + y ); if (r0 !== 0) { ctx.arc(x, y, r0, endAngle, startAngle, clockwise); } ctx.closePath(); }
zrender还定义了Rect矩形、Isogon正多边形、Line直线、Polygon多边形、Polyline折线、Ring圆环、Rose玫瑰线、Star n角星等图形,其实现过程基本与上述图形上类似,都是基于canvas的lineTo、arc、bezierCurveTo等api进行绘制的,这边就不做一一介绍了。
总结zrender中封装了很多工具,在后续echarts源码解读中,我们可以看到echarts是基于zrender进行开发的。