D3Charts-自定义

Demo

可以查看项目下的http://localhost:8080/demo/bar/barchart.html 这个示例。

设计思路

自定义也是继承自series,然后将数据模型(model)和作图功能(view)完全开放给用户。因此需要用户自己实现作图逻辑,有不低的使用门槛。

文件位置

自定义功能是大约在2020.03.09开始使用的,实际上程序在2018年2月份就已经由WJW添加上去了。

程序位于src/chart/custom目录下。

代码逻辑

CustomModel

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
30
31
32
33
34
35
36
/**
* 自定义Model模板
* by wjw
*/
import SeriesModel from '../../model/Series';
import { each } from '../../util';

export const TYPE = 'custom';

export default class CustomModel extends SeriesModel {
static type = TYPE; // 静态变量

type = TYPE; // 实例变量

static defaultOption = {
zlevel: 0, // canvas层级
z: 1, // 同一层canvas内层级

view: {
},

model: {
}
}

constructor() {
super(...arguments);

let modelFunc = this.get('model');

each(modelFunc, (func, name) => {
this[name] = func;
});
}

}

CustomView

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
import ChartView from '../../view/Chart';
import { TYPE } from './CustomModel';
import { each } from '../../util';


export default class CustomView extends ChartView {

static type = TYPE;

type = TYPE;

constructor(model) {
super(...arguments);

let viewFunc = model.get('view');

each(viewFunc, (func, name) => {
this[name] = func;
});
}
//render(model, globalModel, global) {
//let viewFunc = model.get('view');
//viewFunc.render.call(this, ...arguments)
//}
}

流程详解

自定义类型使用方法和常规 series 一致,绘图逻辑需写在 view 对象里面的 render 方法中,如:

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
30
31
32
33
34
35
36
37
38
39
40
41
series: [{
type: 'custom',
zlevel: 10,
$dataIndex: 1,
view: {
render: function(model, globalModel, global) {
let data = model.getData()
let axisModel = globalModel.getComponent('axis')
let xScale = axisModel[0].getScale()
let yScale = axisModel[1].getScale()
let bandWidth = xScale.bandwidth()

let attr = [], attr2 = []

data.forEach(d => {
attr2.push({
shape: {
cx: xScale(d[0]) + bandWidth / 2,
cy: yScale(d[1]),
r: 10
},
style: {
fill: 'red',
stroke: 'green',
lineWidth: 5
}
})
})


this.setShapeGroup('circle', D3Charts.graphic.Circle, attr2)
this.setShape('text', D3Charts.graphic.Text, {
style: {
x: 200,
y: 100,
text: '随便写点字'
}
})
}
}
}]

完整例子可前往:http://datav.iwencai.com/platform/chartconfig.html#chartId=282

render 方法提供三个参数,model, globalModel, global

  • model: custom 模块的 model 信息,获取 model 配置信息等
  • globalModel: 保存着全局的 model 信息,比如在这里获取 axis, grid 等组件的 model 等
  • global: 用于获取全局信息的一些方法,比如获取视图宽高、DOM 等

如上面的例子中,在柱状图中添加自定义的 circle 形状,有如下步骤

获取数据

首先需要确定绘图数据,获取数据

1
let data = model.getData()

要想正确获取数据,前提是配置正确的 $dataIndex,这将用于确定 data 数据源中的数据索引

【核心】获取柱子坐标

这一步是非常关键的,要想用好自定义,必须掌握获取各种坐标数据的API。

1
2
3
4
let axisModel = globalModel.getComponent('axis')
let xScale = axisModel[0].getScale()
let yScale = axisModel[1].getScale()
let bandWidth = xScale.bandwidth()

通过坐标轴的 scale 获取柱子处的坐标

在 globalModel 上获取到 axis 组件,根据配置情况分别获取到 x、y 轴,在其之上调用 getScale 则获取到了相应的坐标轴,
由于是 band 类型的 scale,要想获得柱子中间的 x 值,则需要加上柱子的一半宽度,柱子宽度通过 scale.bandwidth() 获得。

处理数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
data.forEach(d => {
attr2.push({
shape: {
cx: xScale(d[0]) + bandWidth / 2,
cy: yScale(d[1]),
r: 10
},
style: {
fill: 'red',
stroke: 'green',
lineWidth: 5
}
})
})

结合 scale 和数据,生成需要绘图的数据和配置,不同的 shape 对应不同的配置,可通过 https://ecomfe.github.io/zrender-doc/public/api.html 查看各 shape 的配置方法

绘制图形

主要有 setShape 和 setShapeGroup 两个方法进行绘制

  • setShape(name, Shape, shapeAttr, animateOption, callback): 设置单个 shape
  • setShapeGroup(name, Shape, shapeAttrArray, animateOption, callback): 设置一组 shape

其中参数

  • name: 形状的名字
  • Shape: 要绘制的形状构造器,如 Line、Circle 等
  • shapeAttr(Array): 形状的配置
  • animateOption: 动画相关的配置
  • callback: 绘制完成的回调

给绘制的图形设置动画(可选)

设置 setShape(Group) 的 animateOption 参数,即可以决定是否开启动画和动画相关配置,完整配置参数如下:

1
2
3
4
5
6
{
animation: Boolean // 是否开启动画
animateFrom: Object // 动画起始状态属性
duration: Number // 动画时长
easing: String // 动画类型
}

给绘制的图形绑定事件(可选)

在 setShape(Group) 的事件回调 callback 中,参数即为绘制好的图形,可直接通过 .on 方法对其进行事件绑定,更多方法见:https://ecomfe.github.io/zrender-doc/public/api.html#oneventname-eventhandler-context 如:

1
2
3
4
5
6
7
8
9
10
11
this.setShapeGroup(
'circle',
D3Charts.graphic.Circle,
attr2,
{},
function(circle) {
circle.on('click', () => {
console.log('我被点击了!')
})
}
)