在part-2中我们重点讨论了G6的数据灌入与Node/Edge实例的创建。第三章,我们来讨论,这些数据挂载在group中是如何绘制的。

这里的核心函数便是canavs.draw()

  1. render() {
  2. Util.each(data.nodes, node => { self.add(NODE, node)});
  3. Util.each(data.edges, edge => {self.add(EDGE, edge)});
  4. self.paint();
  5. }
  6. paint() {
  7. this.emit('beforepaint');
  8. this.get('canvas').draw();
  9. this.emit('afterpaint');
  10. }

image.png

这块的逻辑是这样的。

  • G支持canvas和svg两种绘图方式,因此this._cfg.painter就是通过用户配置的render后获取不同painter实例
  • 查看下图的调用栈,可知,draw —> _drawGroup—>递归执行_drawGroup得到最后一层children 即用户添加的shapes —> 执行drawShape

image.png

shape是谁?

是用户自定义shape,它由group.addShape()方法创建,实例化的过程如下 /g/src/core/group.js
Shape在 /g/src/shapes/*.js

  1. addShape(type, cfg) {
  2. const canvas = this.get('canvas');
  3. cfg.canvas = canvas;
  4. cfg.type = type;
  5. const rst = new Shape[shapeType](cfg);
  6. this.add(rst);
  7. return rst;
  8. },

Shape.drawInner在哪?

很显然,Circle实例的drawInner方法继承与Shape,/g/src/core/shape.js ,在父类Shape上,没有createPath等方法,需要子类自己去实现。

  1. Util.augment(Shape, isPointInPath, {
  2. isShape: true,
  3. drawInner(context) {
  4. const self = this;
  5. self.createPath(context);
  6. self.afterPath(context);
  7. },
  8. })

核心!createPath

我们以Circle为例,createPath完成核心的绘制工作 /g/src/shapes/circle.js
这里就只有canvas API,详见MDN

  1. const Util = require('../util/index');
  2. const Shape = require('../core/shape');
  3. const Circle = function(cfg) {
  4. Circle.superclass.constructor.call(this, cfg);
  5. };
  6. Util.extend(Circle, Shape);
  7. Util.augment(Circle, {
  8. type: 'circle',
  9. createPath(context) {
  10. const attrs = this._attrs;
  11. const cx = attrs.x;
  12. const cy = attrs.y;
  13. const r = attrs.r;
  14. context.beginPath();
  15. context.arc(cx, cy, r, 0, Math.PI * 2, false);
  16. context.closePath();
  17. }
  18. });
  19. module.exports = Circle;

G6源码阅读-Part3-绘制Paint - 图3