adaptor.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. import { __assign } from "tslib";
  2. import { each, every, find, findIndex, get, isBoolean, isEqual, isObject } from '@antv/util';
  3. import { animation as commonAnimation, annotation as commonAnnotation, interaction as commonInteraction, limitInPlot as commonLimitInPlot, scale, theme as commonTheme, } from '../../adaptor/common';
  4. import { deepAssign, flow } from '../../utils';
  5. import { percent } from '../../utils/transform/percent';
  6. import { findViewById } from '../../utils/view';
  7. import { LEFT_AXES_VIEW, RIGHT_AXES_VIEW } from './constant';
  8. import { AxisType, DualAxesGeometry } from './types';
  9. import { drawSingleGeometry } from './util/geometry';
  10. import { getViewLegendItems } from './util/legend';
  11. import { getGeometryOption, getYAxisWithDefault, isColumn, transformObjectToArray } from './util/option';
  12. import { doSliderFilter } from './util/render-sider';
  13. /**
  14. * transformOptions,双轴图整体的取参逻辑如下
  15. * 1. get index getOptions: 对应的是默认的图表参数,如 appendPadding,syncView 等
  16. * 2. get adpator transformOption: 对应的是双轴图的默认参数,deepAssign 优先级从低到高如下
  17. * 2.1 defaultoption,如 tooltip,legend
  18. * 2.2 用户填写 options
  19. * 2.3 根据用户填写的 options 补充的数组型 options,如 yaxis,GeometryOption,因为 deepAssign 无法 assign 数组
  20. *
  21. * @param params
  22. */
  23. export function transformOptions(params) {
  24. var _a;
  25. var options = params.options;
  26. var _b = options.geometryOptions, geometryOptions = _b === void 0 ? [] : _b, xField = options.xField, yField = options.yField;
  27. var allLine = every(geometryOptions, function (_a) {
  28. var geometry = _a.geometry;
  29. return geometry === DualAxesGeometry.Line || geometry === undefined;
  30. });
  31. return deepAssign({}, {
  32. options: {
  33. geometryOptions: [],
  34. meta: (_a = {},
  35. _a[xField] = {
  36. // 默认为 cat 类型
  37. type: 'cat',
  38. // x 轴一定是同步 scale 的
  39. sync: true,
  40. // 如果有没有柱子,则
  41. range: allLine ? [0, 1] : undefined,
  42. },
  43. _a),
  44. tooltip: {
  45. showMarkers: allLine,
  46. // 存在柱状图,不显示 crosshairs
  47. showCrosshairs: allLine,
  48. shared: true,
  49. crosshairs: {
  50. type: 'x',
  51. },
  52. },
  53. interactions: !allLine
  54. ? [{ type: 'legend-visible-filter' }, { type: 'active-region' }]
  55. : [{ type: 'legend-visible-filter' }],
  56. legend: {
  57. position: 'top-left',
  58. },
  59. },
  60. }, params, {
  61. options: {
  62. // yAxis
  63. yAxis: transformObjectToArray(yField, options.yAxis),
  64. // geometryOptions
  65. geometryOptions: [
  66. getGeometryOption(xField, yField[0], geometryOptions[0]),
  67. getGeometryOption(xField, yField[1], geometryOptions[1]),
  68. ],
  69. // annotations
  70. annotations: transformObjectToArray(yField, options.annotations),
  71. },
  72. });
  73. }
  74. /**
  75. * 创建 双轴图 中绘制图形的 view,提前创建是因为 theme 适配器的需要
  76. * @param params
  77. */
  78. function createViews(params) {
  79. var _a, _b;
  80. var chart = params.chart, options = params.options;
  81. var geometryOptions = options.geometryOptions;
  82. var SORT_MAP = { line: 0, column: 1 };
  83. // 包含配置,id,数据的结构
  84. var geometries = [
  85. { type: (_a = geometryOptions[0]) === null || _a === void 0 ? void 0 : _a.geometry, id: LEFT_AXES_VIEW },
  86. { type: (_b = geometryOptions[1]) === null || _b === void 0 ? void 0 : _b.geometry, id: RIGHT_AXES_VIEW },
  87. ];
  88. // 将线的 view 放置在更上一层,防止线柱遮挡。先柱后先
  89. geometries.sort(function (a, b) { return -SORT_MAP[a.type] + SORT_MAP[b.type]; }).forEach(function (g) { return chart.createView({ id: g.id }); });
  90. return params;
  91. }
  92. /**
  93. * 绘制图形
  94. * @param params
  95. */
  96. function geometry(params) {
  97. var chart = params.chart, options = params.options;
  98. var xField = options.xField, yField = options.yField, geometryOptions = options.geometryOptions, data = options.data, tooltip = options.tooltip;
  99. // 包含配置,id,数据的结构
  100. var geometries = [
  101. __assign(__assign({}, geometryOptions[0]), { id: LEFT_AXES_VIEW, data: data[0], yField: yField[0] }),
  102. __assign(__assign({}, geometryOptions[1]), { id: RIGHT_AXES_VIEW, data: data[1], yField: yField[1] }),
  103. ];
  104. geometries.forEach(function (geometry) {
  105. var id = geometry.id, data = geometry.data, yField = geometry.yField;
  106. // 百分比柱状图需要额外处理一次数据
  107. var isPercent = isColumn(geometry) && geometry.isPercent;
  108. var formatData = isPercent ? percent(data, yField, xField, yField) : data;
  109. var view = findViewById(chart, id).data(formatData);
  110. var tooltipOptions = isPercent
  111. ? __assign({ formatter: function (datum) { return ({
  112. name: datum[geometry.seriesField] || yField,
  113. value: (Number(datum[yField]) * 100).toFixed(2) + '%',
  114. }); } }, tooltip) : tooltip;
  115. // 绘制图形
  116. drawSingleGeometry({
  117. chart: view,
  118. options: {
  119. xField: xField,
  120. yField: yField,
  121. tooltip: tooltipOptions,
  122. geometryOption: geometry,
  123. },
  124. });
  125. });
  126. return params;
  127. }
  128. export function color(params) {
  129. var _a;
  130. var chart = params.chart, options = params.options;
  131. var geometryOptions = options.geometryOptions;
  132. var themeColor = ((_a = chart.getTheme()) === null || _a === void 0 ? void 0 : _a.colors10) || [];
  133. var start = 0;
  134. /* 为 geometry 添加默认 color。
  135. * 1. 若 geometryOptions 存在 color,则在 drawGeometry 时已处理
  136. * 2. 若 不存在 color,获取 Geometry group scales个数,在 theme color 10 中提取
  137. * 3. 为防止 group 过多导致右色板无值或值很少,右 view 面板在依次提取剩下的 N 个 后再 concat 一次 themeColor
  138. * 4. 为简便获取 Geometry group scales个数,在绘制完后再执行 color
  139. * 5. 考虑之后将不同 view 使用同一个色板的需求沉淀到 g2
  140. */
  141. chart.once('beforepaint', function () {
  142. each(geometryOptions, function (geometryOption, index) {
  143. var view = findViewById(chart, index === 0 ? LEFT_AXES_VIEW : RIGHT_AXES_VIEW);
  144. if (geometryOption.color)
  145. return;
  146. var groupScale = view.getGroupScales();
  147. var count = get(groupScale, [0, 'values', 'length'], 1);
  148. var color = themeColor.slice(start, start + count).concat(index === 0 ? [] : themeColor);
  149. view.geometries.forEach(function (geometry) {
  150. if (geometryOption.seriesField) {
  151. geometry.color(geometryOption.seriesField, color);
  152. }
  153. else {
  154. geometry.color(color[0]);
  155. }
  156. });
  157. start += count;
  158. });
  159. chart.render(true);
  160. });
  161. return params;
  162. }
  163. /**
  164. * meta 配置
  165. * @param params
  166. */
  167. export function meta(params) {
  168. var _a, _b;
  169. var chart = params.chart, options = params.options;
  170. var xAxis = options.xAxis, yAxis = options.yAxis, xField = options.xField, yField = options.yField;
  171. scale((_a = {},
  172. _a[xField] = xAxis,
  173. _a[yField[0]] = yAxis[0],
  174. _a))(deepAssign({}, params, { chart: findViewById(chart, LEFT_AXES_VIEW) }));
  175. scale((_b = {},
  176. _b[xField] = xAxis,
  177. _b[yField[1]] = yAxis[1],
  178. _b))(deepAssign({}, params, { chart: findViewById(chart, RIGHT_AXES_VIEW) }));
  179. return params;
  180. }
  181. /**
  182. * axis 配置
  183. * @param params
  184. */
  185. export function axis(params) {
  186. var chart = params.chart, options = params.options;
  187. var leftView = findViewById(chart, LEFT_AXES_VIEW);
  188. var rightView = findViewById(chart, RIGHT_AXES_VIEW);
  189. var xField = options.xField, yField = options.yField, xAxis = options.xAxis, yAxis = options.yAxis;
  190. chart.axis(xField, false);
  191. chart.axis(yField[0], false);
  192. chart.axis(yField[1], false);
  193. // 左 View
  194. leftView.axis(xField, xAxis);
  195. leftView.axis(yField[0], getYAxisWithDefault(yAxis[0], AxisType.Left));
  196. // 右 Y 轴
  197. rightView.axis(xField, false);
  198. rightView.axis(yField[1], getYAxisWithDefault(yAxis[1], AxisType.Right));
  199. return params;
  200. }
  201. /**
  202. * tooltip 配置
  203. * @param params
  204. */
  205. export function tooltip(params) {
  206. var chart = params.chart, options = params.options;
  207. var tooltip = options.tooltip;
  208. var leftView = findViewById(chart, LEFT_AXES_VIEW);
  209. var rightView = findViewById(chart, RIGHT_AXES_VIEW);
  210. // tooltip 经过 getDefaultOption 处理后,一定不为 undefined
  211. chart.tooltip(tooltip);
  212. // 在 view 上添加 tooltip,使得 shared 和 interaction active-region 起作用
  213. // view 应该继承 chart 里的 shared,但是从表现看来,继承有点问题
  214. leftView.tooltip({
  215. shared: true,
  216. });
  217. rightView.tooltip({
  218. shared: true,
  219. });
  220. return params;
  221. }
  222. /**
  223. * interaction 配置
  224. * @param params
  225. */
  226. export function interaction(params) {
  227. var chart = params.chart;
  228. commonInteraction(deepAssign({}, params, { chart: findViewById(chart, LEFT_AXES_VIEW) }));
  229. commonInteraction(deepAssign({}, params, { chart: findViewById(chart, RIGHT_AXES_VIEW) }));
  230. return params;
  231. }
  232. /**
  233. * annotation 配置
  234. * @param params
  235. */
  236. export function annotation(params) {
  237. var chart = params.chart, options = params.options;
  238. var annotations = options.annotations;
  239. var a1 = get(annotations, [0]);
  240. var a2 = get(annotations, [1]);
  241. commonAnnotation(a1)(deepAssign({}, params, {
  242. chart: findViewById(chart, LEFT_AXES_VIEW),
  243. options: {
  244. annotations: a1,
  245. },
  246. }));
  247. commonAnnotation(a2)(deepAssign({}, params, {
  248. chart: findViewById(chart, RIGHT_AXES_VIEW),
  249. options: {
  250. annotations: a2,
  251. },
  252. }));
  253. return params;
  254. }
  255. export function theme(params) {
  256. var chart = params.chart;
  257. /*
  258. * 双轴图中,部分组件是绘制在子 view 层(例如 axis,line),部分组件是绘制在 chart (例如 legend)
  259. * 为 chart 和 子 view 均注册 theme,使其自行遵循 G2 theme geometry > view > chart 进行渲染。
  260. */
  261. commonTheme(deepAssign({}, params, { chart: findViewById(chart, LEFT_AXES_VIEW) }));
  262. commonTheme(deepAssign({}, params, { chart: findViewById(chart, RIGHT_AXES_VIEW) }));
  263. commonTheme(params);
  264. return params;
  265. }
  266. export function animation(params) {
  267. var chart = params.chart;
  268. commonAnimation(deepAssign({}, params, { chart: findViewById(chart, LEFT_AXES_VIEW) }));
  269. commonAnimation(deepAssign({}, params, { chart: findViewById(chart, RIGHT_AXES_VIEW) }));
  270. return params;
  271. }
  272. /**
  273. * 双轴图 limitInPlot
  274. * @param params
  275. */
  276. export function limitInPlot(params) {
  277. var chart = params.chart, options = params.options;
  278. var yAxis = options.yAxis;
  279. commonLimitInPlot(deepAssign({}, params, {
  280. chart: findViewById(chart, LEFT_AXES_VIEW),
  281. options: {
  282. yAxis: yAxis[0],
  283. },
  284. }));
  285. commonLimitInPlot(deepAssign({}, params, {
  286. chart: findViewById(chart, RIGHT_AXES_VIEW),
  287. options: {
  288. yAxis: yAxis[1],
  289. },
  290. }));
  291. return params;
  292. }
  293. /**
  294. * legend 配置
  295. * 使用 custom,便于和类似于分组柱状图-单折线图的逻辑统一
  296. * @param params
  297. */
  298. export function legend(params) {
  299. var chart = params.chart, options = params.options;
  300. var legend = options.legend, geometryOptions = options.geometryOptions, yField = options.yField, data = options.data;
  301. var leftView = findViewById(chart, LEFT_AXES_VIEW);
  302. var rightView = findViewById(chart, RIGHT_AXES_VIEW);
  303. if (legend === false) {
  304. chart.legend(false);
  305. }
  306. else if (isObject(legend) && legend.custom === true) {
  307. chart.legend(legend);
  308. }
  309. else {
  310. var leftLegend_1 = get(geometryOptions, [0, 'legend'], legend);
  311. var rightLegend_1 = get(geometryOptions, [1, 'legend'], legend);
  312. // 均使用自定义图例
  313. chart.once('beforepaint', function () {
  314. var leftItems = data[0].length
  315. ? getViewLegendItems({
  316. view: leftView,
  317. geometryOption: geometryOptions[0],
  318. yField: yField[0],
  319. legend: leftLegend_1,
  320. })
  321. : [];
  322. var rightItems = data[1].length
  323. ? getViewLegendItems({
  324. view: rightView,
  325. geometryOption: geometryOptions[1],
  326. yField: yField[1],
  327. legend: rightLegend_1,
  328. })
  329. : [];
  330. chart.legend(deepAssign({}, legend, {
  331. custom: true,
  332. // todo 修改类型定义
  333. // @ts-ignore
  334. items: leftItems.concat(rightItems),
  335. }));
  336. });
  337. if (geometryOptions[0].seriesField) {
  338. leftView.legend(geometryOptions[0].seriesField, leftLegend_1);
  339. }
  340. if (geometryOptions[1].seriesField) {
  341. rightView.legend(geometryOptions[1].seriesField, rightLegend_1);
  342. }
  343. // 自定义图例交互
  344. chart.on('legend-item:click', function (evt) {
  345. var delegateObject = get(evt, 'gEvent.delegateObject', {});
  346. if (delegateObject && delegateObject.item) {
  347. var _a = delegateObject.item, field_1 = _a.value, isGeometry = _a.isGeometry, viewId = _a.viewId;
  348. // geometry 的时候,直接使用 view.changeVisible
  349. if (isGeometry) {
  350. var idx = findIndex(yField, function (yF) { return yF === field_1; });
  351. if (idx > -1) {
  352. var geometries = get(findViewById(chart, viewId), 'geometries');
  353. each(geometries, function (g) {
  354. g.changeVisible(!delegateObject.item.unchecked);
  355. });
  356. }
  357. }
  358. else {
  359. var legendItem_1 = get(chart.getController('legend'), 'option.items', []);
  360. // 分组柱线图
  361. each(chart.views, function (view) {
  362. // 单折柱图
  363. var groupScale = view.getGroupScales();
  364. each(groupScale, function (scale) {
  365. if (scale.values && scale.values.indexOf(field_1) > -1) {
  366. view.filter(scale.field, function (value) {
  367. var curLegendItem = find(legendItem_1, function (item) { return item.value === value; });
  368. // 使用 legend 中的 unchecked 来判断,使得支持关闭多个图例
  369. return !curLegendItem.unchecked;
  370. });
  371. }
  372. });
  373. chart.render(true);
  374. });
  375. }
  376. }
  377. });
  378. }
  379. return params;
  380. }
  381. /**
  382. * 双轴图 slider 适配器
  383. * @param params
  384. */
  385. export function slider(params) {
  386. var chart = params.chart, options = params.options;
  387. var slider = options.slider;
  388. var leftView = findViewById(chart, LEFT_AXES_VIEW);
  389. var rightView = findViewById(chart, RIGHT_AXES_VIEW);
  390. if (slider) {
  391. // 左 View
  392. leftView.option('slider', slider);
  393. // 监听左侧 slider 改变事件, 同步右侧 View 视图
  394. leftView.on('slider:valuechanged', function (evt) {
  395. var _a = evt.event, value = _a.value, originValue = _a.originValue;
  396. if (isEqual(value, originValue)) {
  397. return;
  398. }
  399. doSliderFilter(rightView, value);
  400. });
  401. chart.once('afterpaint', function () {
  402. // 初始化数据,配置默认值时需要同步
  403. if (!isBoolean(slider)) {
  404. var start = slider.start, end = slider.end;
  405. if (start || end) {
  406. doSliderFilter(rightView, [start, end]);
  407. }
  408. }
  409. });
  410. }
  411. return params;
  412. }
  413. /**
  414. * 双折线图适配器
  415. * @param chart
  416. * @param options
  417. */
  418. export function adaptor(params) {
  419. // transformOptions 一定在最前面处理;color legend 使用了 beforepaint,为便于理解放在最后面
  420. return flow(transformOptions, createViews,
  421. // 主题靠前设置,作为最低优先级
  422. theme, geometry, meta, axis, limitInPlot, tooltip, interaction, annotation, animation, color, legend, slider)(params);
  423. }
  424. //# sourceMappingURL=adaptor.js.map