echarts源码解读《一》:zrender源码概要分析

2019年首次发博,不知道大家有没有出去过520呢??我反正是脚伤了!哭

今天7月份我将入职腾讯,我的工作导师于3月份让我学习可视化,前段时间一直在忙毕设,也就耽搁下来了。

在echarts源码解读系列文章中,我将会记录我学习echarts源码时的过程,并对echarts的基本实现进行大致的解读,写博客是为了能够让自己对echarts有更好的了解,也希望能够通过博客帮助到需要学习解读echarts源码的小伙伴们,一起加油吧!!!

前言

由于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解释

全局的文件:

  • config.js 配置文件
  • Element.js 元素文件作为zrender最基本的元素
  • Handle.js C层,控制层
  • Layer.js 图层管理
  • Painter.js V层,视图层
  • Storage.js M层,数据管理层
  • zrender.js 入口

zrender整体架构

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.jsPath、Text等的基类,继承自Element,定义了setStyle、useStyle等方法,

Text

文本,继承自Displayable,调用了helper/text.js中的renderText方法进行绘制。主要代码如下:

1
2
3
4
5
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进行圆形的绘制,主要实现代码如下:

1
2
3
4
5
6
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进行弧形的绘制,主要实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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进行弧形的绘制,主要实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 为曲线的必经点

具体绘制图如下:

贝塞尔1

同理,我们可以绘制出左半边心形:

贝塞尔2

Droplet

水滴形状💧,通过调用Path.extend方法进行扩展,重写了buildPath方法,buildPath调用了canvas api:ctx.bezierCurveTo进行弧形的绘制,贝塞尔曲线的绘制过程同心形绘制原理,主要实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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进行弧形的绘制,贝塞尔曲线的绘制过程同心形绘制原理,主要实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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进行圆形的绘制,主要实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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进行开发的。

希望这篇博文能够帮助大家对zrender及其实现的图形有大致的了解~

本文标题:echarts源码解读《一》:zrender源码概要分析

文章作者:萌萌哒的邱邱邱邱

发布时间:2019年05月20日 - 10:05

最后更新:2019年05月21日 - 10:05

原始链接:https://qiuruolin.github.io/2019/05/20/echarts-1/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

-------------本文结束感谢您的阅读-------------
感谢您的支持