bar.js 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953
  1. /**
  2. * echarts图表类:柱形图
  3. *
  4. * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
  5. * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
  6. *
  7. */
  8. define(function (require) {
  9. var ChartBase = require('./base');
  10. // 图形依赖
  11. var RectangleShape = require('zrender/shape/Rectangle');
  12. // 组件依赖
  13. require('../component/axis');
  14. require('../component/grid');
  15. require('../component/dataZoom');
  16. var ecConfig = require('../config');
  17. // 柱形图默认参数
  18. ecConfig.bar = {
  19. zlevel: 0, // 一级层叠
  20. z: 2, // 二级层叠
  21. clickable: true,
  22. legendHoverLink: true,
  23. // stack: null
  24. xAxisIndex: 0,
  25. yAxisIndex: 0,
  26. barMinHeight: 0, // 最小高度改为0
  27. // barWidth: null, // 默认自适应
  28. barGap: '30%', // 柱间距离,默认为柱形宽度的30%,可设固定值
  29. barCategoryGap: '20%', // 类目间柱形距离,默认为类目间距的20%,可设固定值
  30. itemStyle: {
  31. normal: {
  32. // color: '各异',
  33. barBorderColor: '#fff', // 柱条边线
  34. barBorderRadius: 0, // 柱条边线圆角,单位px,默认为0
  35. barBorderWidth: 0, // 柱条边线线宽,单位px,默认为1
  36. label: {
  37. show: false
  38. // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调
  39. // position: 默认自适应,水平布局为'top',垂直布局为'right',可选为
  40. // 'inside'|'left'|'right'|'top'|'bottom'
  41. // textStyle: null // 默认使用全局文本样式,详见TEXTSTYLE
  42. }
  43. },
  44. emphasis: {
  45. // color: '各异',
  46. barBorderColor: '#fff', // 柱条边线
  47. barBorderRadius: 0, // 柱条边线圆角,单位px,默认为0
  48. barBorderWidth: 0, // 柱条边线线宽,单位px,默认为1
  49. label: {
  50. show: false
  51. // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调
  52. // position: 默认自适应,水平布局为'top',垂直布局为'right',可选为
  53. // 'inside'|'left'|'right'|'top'|'bottom'
  54. // textStyle: null // 默认使用全局文本样式,详见TEXTSTYLE
  55. }
  56. }
  57. }
  58. };
  59. var ecData = require('../util/ecData');
  60. var zrUtil = require('zrender/tool/util');
  61. var zrColor = require('zrender/tool/color');
  62. /**
  63. * 构造函数
  64. * @param {Object} messageCenter echart消息中心
  65. * @param {ZRender} zr zrender实例
  66. * @param {Object} series 数据
  67. * @param {Object} component 组件
  68. */
  69. function Bar(ecTheme, messageCenter, zr, option, myChart){
  70. // 图表基类
  71. ChartBase.call(this, ecTheme, messageCenter, zr, option, myChart);
  72. this.refresh(option);
  73. }
  74. Bar.prototype = {
  75. type: ecConfig.CHART_TYPE_BAR,
  76. /**
  77. * 绘制图形
  78. */
  79. _buildShape: function () {
  80. this._buildPosition();
  81. },
  82. _buildNormal: function(seriesArray, maxDataLength, locationMap, xMarkMap, orient) {
  83. var series = this.series;
  84. // 确定类目轴和数值轴,同一方向随便找一个即可
  85. var seriesIndex = locationMap[0][0];
  86. var serie = series[seriesIndex];
  87. var isHorizontal = orient == 'horizontal';
  88. var xAxis = this.component.xAxis;
  89. var yAxis = this.component.yAxis;
  90. var categoryAxis = isHorizontal
  91. ? xAxis.getAxis(serie.xAxisIndex)
  92. : yAxis.getAxis(serie.yAxisIndex);
  93. var valueAxis; // 数值轴各异
  94. var size = this._mapSize(categoryAxis, locationMap);
  95. var gap = size.gap;
  96. var barGap = size.barGap;
  97. var barWidthMap = size.barWidthMap;
  98. var barMaxWidthMap = size.barMaxWidthMap;
  99. var barWidth = size.barWidth; // 自适应宽度
  100. var barMinHeightMap = size.barMinHeightMap;
  101. var barHeight;
  102. var curBarWidth;
  103. var interval = size.interval;
  104. var x;
  105. var y;
  106. var lastP; // 正向堆积处理
  107. var baseP;
  108. var lastN; // 负向堆积处理
  109. var baseN;
  110. var barShape;
  111. var data;
  112. var value;
  113. var islandR = this.deepQuery([this.ecTheme, ecConfig], 'island.r');
  114. for (var i = 0, l = maxDataLength; i < l; i++) {
  115. if (categoryAxis.getNameByIndex(i) == null) {
  116. // 系列数据超出类目轴长度
  117. break;
  118. }
  119. isHorizontal
  120. ? (x = categoryAxis.getCoordByIndex(i) - gap / 2)
  121. : (y = categoryAxis.getCoordByIndex(i) + gap / 2);
  122. for (var j = 0, k = locationMap.length; j < k; j++) {
  123. // 堆积数据用第一条valueAxis
  124. var yAxisIndex = series[locationMap[j][0]].yAxisIndex || 0;
  125. var xAxisIndex = series[locationMap[j][0]].xAxisIndex || 0;
  126. valueAxis = isHorizontal
  127. ? yAxis.getAxis(yAxisIndex)
  128. : xAxis.getAxis(xAxisIndex);
  129. baseP = lastP = baseN = lastN = valueAxis.getCoord(0);
  130. for (var m = 0, n = locationMap[j].length; m < n; m++) {
  131. seriesIndex = locationMap[j][m];
  132. serie = series[seriesIndex];
  133. data = serie.data[i];
  134. value = this.getDataFromOption(data, '-');
  135. xMarkMap[seriesIndex] = xMarkMap[seriesIndex]
  136. || {
  137. min: Number.POSITIVE_INFINITY,
  138. max: Number.NEGATIVE_INFINITY,
  139. sum: 0,
  140. counter: 0,
  141. average: 0
  142. };
  143. curBarWidth = Math.min(
  144. barMaxWidthMap[seriesIndex] || Number.MAX_VALUE,
  145. barWidthMap[seriesIndex] || barWidth
  146. );
  147. if (value === '-') {
  148. // 空数据在做完后补充拖拽提示框
  149. continue;
  150. }
  151. if (value > 0) {
  152. // 正向堆积
  153. barHeight = m > 0
  154. ? valueAxis.getCoordSize(value)
  155. : (
  156. isHorizontal
  157. ? (baseP - valueAxis.getCoord(value))
  158. : (valueAxis.getCoord(value) - baseP)
  159. );
  160. // 非堆积数据最小高度有效
  161. if (n === 1 && barMinHeightMap[seriesIndex] > barHeight) {
  162. barHeight = barMinHeightMap[seriesIndex];
  163. }
  164. if (isHorizontal) {
  165. lastP -= barHeight;
  166. y = lastP;
  167. }
  168. else {
  169. x = lastP;
  170. lastP += barHeight;
  171. }
  172. }
  173. else if (value < 0){
  174. // 负向堆积
  175. barHeight = m > 0
  176. ? valueAxis.getCoordSize(value)
  177. : (
  178. isHorizontal
  179. ? (valueAxis.getCoord(value) - baseN)
  180. : (baseN - valueAxis.getCoord(value))
  181. );
  182. // 非堆积数据最小高度有效
  183. if (n === 1 && barMinHeightMap[seriesIndex] > barHeight) {
  184. barHeight = barMinHeightMap[seriesIndex];
  185. }
  186. if (isHorizontal) {
  187. y = lastN;
  188. lastN += barHeight;
  189. }
  190. else {
  191. lastN -= barHeight;
  192. x = lastN;
  193. }
  194. }
  195. else {
  196. // 0值
  197. barHeight = 0;
  198. // 最小高度无效
  199. if (isHorizontal) {
  200. lastP -= barHeight;
  201. y = lastP;
  202. }
  203. else {
  204. x = lastP;
  205. lastP += barHeight;
  206. }
  207. }
  208. xMarkMap[seriesIndex][i] = isHorizontal
  209. ? (x + curBarWidth / 2)
  210. : (y - curBarWidth / 2);
  211. if (xMarkMap[seriesIndex].min > value) {
  212. xMarkMap[seriesIndex].min = value;
  213. if (isHorizontal) {
  214. xMarkMap[seriesIndex].minY = y;
  215. xMarkMap[seriesIndex].minX = xMarkMap[seriesIndex][i];
  216. }
  217. else {
  218. xMarkMap[seriesIndex].minX = x + barHeight;
  219. xMarkMap[seriesIndex].minY = xMarkMap[seriesIndex][i];
  220. }
  221. }
  222. if (xMarkMap[seriesIndex].max < value) {
  223. xMarkMap[seriesIndex].max = value;
  224. if (isHorizontal) {
  225. xMarkMap[seriesIndex].maxY = y;
  226. xMarkMap[seriesIndex].maxX = xMarkMap[seriesIndex][i];
  227. }
  228. else {
  229. xMarkMap[seriesIndex].maxX = x + barHeight;
  230. xMarkMap[seriesIndex].maxY = xMarkMap[seriesIndex][i];
  231. }
  232. }
  233. xMarkMap[seriesIndex].sum += value;
  234. xMarkMap[seriesIndex].counter++;
  235. if (i % interval === 0) {
  236. barShape = this._getBarItem(
  237. seriesIndex, i,
  238. categoryAxis.getNameByIndex(i),
  239. x,
  240. y - (isHorizontal ? 0 : curBarWidth),
  241. isHorizontal ? curBarWidth : barHeight,
  242. isHorizontal ? barHeight : curBarWidth,
  243. isHorizontal ? 'vertical' : 'horizontal'
  244. );
  245. this.shapeList.push(new RectangleShape(barShape));
  246. }
  247. }
  248. // 补充空数据的拖拽提示框
  249. for (var m = 0, n = locationMap[j].length; m < n; m++) {
  250. seriesIndex = locationMap[j][m];
  251. serie = series[seriesIndex];
  252. data = serie.data[i];
  253. value = this.getDataFromOption(data, '-');
  254. curBarWidth = Math.min(
  255. barMaxWidthMap[seriesIndex] || Number.MAX_VALUE,
  256. barWidthMap[seriesIndex] || barWidth
  257. );
  258. if (value != '-') {
  259. // 只关心空数据
  260. continue;
  261. }
  262. if (this.deepQuery([data, serie, this.option], 'calculable')) {
  263. if (isHorizontal) {
  264. lastP -= islandR;
  265. y = lastP;
  266. }
  267. else {
  268. x = lastP;
  269. lastP += islandR;
  270. }
  271. barShape = this._getBarItem(
  272. seriesIndex, i,
  273. categoryAxis.getNameByIndex(i),
  274. x,
  275. y - (isHorizontal ? 0 : curBarWidth),
  276. isHorizontal ? curBarWidth : islandR,
  277. isHorizontal ? islandR : curBarWidth,
  278. isHorizontal ? 'vertical' : 'horizontal'
  279. );
  280. barShape.hoverable = false;
  281. barShape.draggable = false;
  282. barShape.style.lineWidth = 1;
  283. barShape.style.brushType = 'stroke';
  284. barShape.style.strokeColor = serie.calculableHolderColor
  285. || this.ecTheme.calculableHolderColor
  286. || ecConfig.calculableHolderColor;
  287. this.shapeList.push(new RectangleShape(barShape));
  288. }
  289. }
  290. isHorizontal
  291. ? (x += (curBarWidth + barGap))
  292. : (y -= (curBarWidth + barGap));
  293. }
  294. }
  295. this._calculMarkMapXY(xMarkMap, locationMap, isHorizontal ? 'y' : 'x');
  296. },
  297. /**
  298. * 构建类目轴为水平方向的柱形图系列
  299. */
  300. _buildHorizontal: function (seriesArray, maxDataLength, locationMap, xMarkMap) {
  301. return this._buildNormal(
  302. seriesArray, maxDataLength, locationMap, xMarkMap, 'horizontal'
  303. );
  304. },
  305. /**
  306. * 构建类目轴为垂直方向的柱形图系列
  307. */
  308. _buildVertical: function (seriesArray, maxDataLength, locationMap, xMarkMap) {
  309. return this._buildNormal(
  310. seriesArray, maxDataLength, locationMap, xMarkMap, 'vertical'
  311. );
  312. },
  313. /**
  314. * 构建双数值轴柱形图
  315. */
  316. _buildOther: function (seriesArray, maxDataLength, locationMap, xMarkMap) {
  317. var series = this.series;
  318. for (var j = 0, k = locationMap.length; j < k; j++) {
  319. for (var m = 0, n = locationMap[j].length; m < n; m++) {
  320. var seriesIndex = locationMap[j][m];
  321. var serie = series[seriesIndex];
  322. var xAxisIndex = serie.xAxisIndex || 0;
  323. var xAxis = this.component.xAxis.getAxis(xAxisIndex);
  324. var baseX = xAxis.getCoord(0);
  325. var yAxisIndex = serie.yAxisIndex || 0;
  326. var yAxis = this.component.yAxis.getAxis(yAxisIndex);
  327. var baseY = yAxis.getCoord(0);
  328. xMarkMap[seriesIndex] = xMarkMap[seriesIndex]
  329. || {
  330. min0: Number.POSITIVE_INFINITY,
  331. min1: Number.POSITIVE_INFINITY,
  332. max0: Number.NEGATIVE_INFINITY,
  333. max1: Number.NEGATIVE_INFINITY,
  334. sum0: 0,
  335. sum1: 0,
  336. counter0: 0,
  337. counter1: 0,
  338. average0: 0,
  339. average1: 0
  340. };
  341. for (var i = 0, l = serie.data.length; i < l; i++) {
  342. var data = serie.data[i];
  343. var value = this.getDataFromOption(data, '-');
  344. if (!(value instanceof Array)) {
  345. continue;
  346. }
  347. var x = xAxis.getCoord(value[0]);
  348. var y = yAxis.getCoord(value[1]);
  349. var queryTarget = [data, serie];
  350. var barWidth = this.deepQuery(queryTarget, 'barWidth') || 10; // 默认柱形
  351. var barHeight = this.deepQuery(queryTarget, 'barHeight');
  352. var orient;
  353. var barShape;
  354. if (barHeight != null) {
  355. // 条形图
  356. orient = 'horizontal';
  357. if (value[0] > 0) {
  358. // 正向
  359. barWidth = x - baseX;
  360. x -= barWidth;
  361. }
  362. else if (value[0] < 0){
  363. // 负向
  364. barWidth = baseX - x;
  365. }
  366. else {
  367. // 0值
  368. barWidth = 0;
  369. }
  370. barShape = this._getBarItem(
  371. seriesIndex, i,
  372. value[0],
  373. x,
  374. y - barHeight / 2,
  375. barWidth,
  376. barHeight,
  377. orient
  378. );
  379. }
  380. else {
  381. // 柱形
  382. orient = 'vertical';
  383. if (value[1] > 0) {
  384. // 正向
  385. barHeight = baseY - y;
  386. }
  387. else if (value[1] < 0){
  388. // 负向
  389. barHeight = y - baseY;
  390. y -= barHeight;
  391. }
  392. else {
  393. // 0值
  394. barHeight = 0;
  395. }
  396. barShape = this._getBarItem(
  397. seriesIndex, i,
  398. value[0],
  399. x - barWidth / 2,
  400. y,
  401. barWidth,
  402. barHeight,
  403. orient
  404. );
  405. }
  406. this.shapeList.push(new RectangleShape(barShape));
  407. x = xAxis.getCoord(value[0]);
  408. y = yAxis.getCoord(value[1]);
  409. if (xMarkMap[seriesIndex].min0 > value[0]) {
  410. xMarkMap[seriesIndex].min0 = value[0];
  411. xMarkMap[seriesIndex].minY0 = y;
  412. xMarkMap[seriesIndex].minX0 = x;
  413. }
  414. if (xMarkMap[seriesIndex].max0 < value[0]) {
  415. xMarkMap[seriesIndex].max0 = value[0];
  416. xMarkMap[seriesIndex].maxY0 = y;
  417. xMarkMap[seriesIndex].maxX0 = x;
  418. }
  419. xMarkMap[seriesIndex].sum0 += value[0];
  420. xMarkMap[seriesIndex].counter0++;
  421. if (xMarkMap[seriesIndex].min1 > value[1]) {
  422. xMarkMap[seriesIndex].min1 = value[1];
  423. xMarkMap[seriesIndex].minY1 = y;
  424. xMarkMap[seriesIndex].minX1 = x;
  425. }
  426. if (xMarkMap[seriesIndex].max1 < value[1]) {
  427. xMarkMap[seriesIndex].max1 = value[1];
  428. xMarkMap[seriesIndex].maxY1 = y;
  429. xMarkMap[seriesIndex].maxX1 = x;
  430. }
  431. xMarkMap[seriesIndex].sum1 += value[1];
  432. xMarkMap[seriesIndex].counter1++;
  433. }
  434. }
  435. }
  436. this._calculMarkMapXY(xMarkMap, locationMap, 'xy');
  437. },
  438. /**
  439. * 我真是自找麻烦啊,为啥要允许系列级个性化最小宽度和高度啊!!!
  440. * @param {CategoryAxis} categoryAxis 类目坐标轴,需要知道类目间隔大小
  441. * @param {Array} locationMap 整形数据的系列索引
  442. */
  443. _mapSize: function (categoryAxis, locationMap, ignoreUserDefined) {
  444. var res = this._findSpecialBarSzie(locationMap, ignoreUserDefined);
  445. var barWidthMap = res.barWidthMap;
  446. var barMaxWidthMap = res.barMaxWidthMap;
  447. var barMinHeightMap = res.barMinHeightMap;
  448. var sBarWidthCounter = res.sBarWidthCounter; // 用户指定
  449. var sBarWidthTotal = res.sBarWidthTotal; // 用户指定
  450. var barGap = res.barGap;
  451. var barCategoryGap = res.barCategoryGap;
  452. var gap;
  453. var barWidth;
  454. var interval = 1;
  455. if (locationMap.length != sBarWidthCounter) {
  456. // 至少存在一个自适应宽度的柱形图
  457. if (!ignoreUserDefined) {
  458. gap = typeof barCategoryGap === 'string' && barCategoryGap.match(/%$/)
  459. // 百分比
  460. ? ((categoryAxis.getGap() * (100 - parseFloat(barCategoryGap)) / 100).toFixed(2) - 0)
  461. // 数值
  462. : (categoryAxis.getGap() - barCategoryGap);
  463. if (typeof barGap === 'string' && barGap.match(/%$/)) {
  464. barGap = parseFloat(barGap) / 100;
  465. barWidth = +(
  466. (gap - sBarWidthTotal) / (
  467. (locationMap.length - 1) * barGap + locationMap.length - sBarWidthCounter
  468. )
  469. ).toFixed(2);
  470. barGap = barWidth * barGap;
  471. }
  472. else {
  473. barGap = parseFloat(barGap);
  474. barWidth = +(
  475. (gap - sBarWidthTotal - barGap * (locationMap.length - 1)) / (
  476. locationMap.length - sBarWidthCounter
  477. )
  478. ).toFixed(2);
  479. }
  480. // 无法满足用户定义的宽度设计,忽略用户宽度,打回重做
  481. if (barWidth <= 0) {
  482. return this._mapSize(categoryAxis, locationMap, true);
  483. }
  484. }
  485. else {
  486. // 忽略用户定义的宽度设定
  487. gap = categoryAxis.getGap();
  488. barGap = 0;
  489. barWidth = +(gap / locationMap.length).toFixed(2);
  490. // 已经忽略用户定义的宽度设定依然还无法满足显示,只能硬来了;
  491. if (barWidth <= 0) {
  492. interval = Math.floor(locationMap.length / gap);
  493. barWidth = 1;
  494. }
  495. }
  496. }
  497. else {
  498. // 全是自定义宽度,barGap无效,系列间隔决定barGap
  499. gap = sBarWidthCounter > 1
  500. ? (typeof barCategoryGap === 'string' && barCategoryGap.match(/%$/))
  501. // 百分比
  502. ? +(categoryAxis.getGap() * (100 - parseFloat(barCategoryGap)) / 100).toFixed(2)
  503. // 数值
  504. : (categoryAxis.getGap() - barCategoryGap)
  505. // 只有一个
  506. : sBarWidthTotal;
  507. barWidth = 0;
  508. barGap = sBarWidthCounter > 1
  509. ? +((gap - sBarWidthTotal) / (sBarWidthCounter - 1)).toFixed(2)
  510. : 0;
  511. if (barGap < 0) {
  512. // 无法满足用户定义的宽度设计,忽略用户宽度,打回重做
  513. return this._mapSize(categoryAxis, locationMap, true);
  514. }
  515. }
  516. // 检查是否满足barMaxWidthMap
  517. return this._recheckBarMaxWidth(
  518. locationMap,
  519. barWidthMap, barMaxWidthMap, barMinHeightMap,
  520. gap, // 总宽度
  521. barWidth, barGap, interval
  522. );
  523. },
  524. /**
  525. * 计算堆积下用户特殊指定的各种size
  526. */
  527. _findSpecialBarSzie: function(locationMap, ignoreUserDefined) {
  528. var series = this.series;
  529. var barWidthMap = {};
  530. var barMaxWidthMap = {};
  531. var barMinHeightMap = {};
  532. var sBarWidth; // 用户指定
  533. var sBarMaxWidth; // 用户指定
  534. var sBarWidthCounter = 0; // 用户指定
  535. var sBarWidthTotal = 0; // 用户指定
  536. var barGap;
  537. var barCategoryGap;
  538. for (var j = 0, k = locationMap.length; j < k; j++) {
  539. var hasFound = {
  540. barWidth: false,
  541. barMaxWidth: false
  542. };
  543. for (var m = 0, n = locationMap[j].length; m < n; m++) {
  544. var seriesIndex = locationMap[j][m];
  545. var queryTarget = series[seriesIndex];
  546. if (!ignoreUserDefined) {
  547. if (!hasFound.barWidth) {
  548. sBarWidth = this.query(queryTarget, 'barWidth');
  549. if (sBarWidth != null) {
  550. // 同一堆积第一个生效barWidth
  551. barWidthMap[seriesIndex] = sBarWidth;
  552. sBarWidthTotal += sBarWidth;
  553. sBarWidthCounter++;
  554. hasFound.barWidth = true;
  555. // 复位前面同一堆积但没被定义的
  556. for (var ii = 0, ll = m; ii < ll; ii++) {
  557. var pSeriesIndex = locationMap[j][ii];
  558. barWidthMap[pSeriesIndex] = sBarWidth;
  559. }
  560. }
  561. }
  562. else {
  563. barWidthMap[seriesIndex] = sBarWidth; // 用找到的一个
  564. }
  565. if (!hasFound.barMaxWidth) {
  566. sBarMaxWidth = this.query(queryTarget, 'barMaxWidth');
  567. if (sBarMaxWidth != null) {
  568. // 同一堆积第一个生效barMaxWidth
  569. barMaxWidthMap[seriesIndex] = sBarMaxWidth;
  570. hasFound.barMaxWidth = true;
  571. // 复位前面同一堆积但没被定义的
  572. for (var ii = 0, ll = m; ii < ll; ii++) {
  573. var pSeriesIndex = locationMap[j][ii];
  574. barMaxWidthMap[pSeriesIndex] = sBarMaxWidth;
  575. }
  576. }
  577. }
  578. else {
  579. barMaxWidthMap[seriesIndex] = sBarMaxWidth; // 用找到的一个
  580. }
  581. }
  582. barMinHeightMap[seriesIndex] = this.query(queryTarget, 'barMinHeight');
  583. barGap = barGap != null ? barGap : this.query(queryTarget, 'barGap');
  584. barCategoryGap = barCategoryGap != null
  585. ? barCategoryGap : this.query(queryTarget, 'barCategoryGap');
  586. }
  587. }
  588. return {
  589. barWidthMap: barWidthMap,
  590. barMaxWidthMap: barMaxWidthMap,
  591. barMinHeightMap: barMinHeightMap,
  592. sBarWidth: sBarWidth,
  593. sBarMaxWidth: sBarMaxWidth,
  594. sBarWidthCounter: sBarWidthCounter,
  595. sBarWidthTotal: sBarWidthTotal,
  596. barGap: barGap,
  597. barCategoryGap: barCategoryGap
  598. };
  599. },
  600. /**
  601. * 检查是否满足barMaxWidthMap
  602. */
  603. _recheckBarMaxWidth: function(
  604. locationMap,
  605. barWidthMap, barMaxWidthMap, barMinHeightMap,
  606. gap, // 总宽度
  607. barWidth, barGap, interval
  608. ) {
  609. for (var j = 0, k = locationMap.length; j < k; j++) {
  610. var seriesIndex = locationMap[j][0];
  611. if (barMaxWidthMap[seriesIndex] && barMaxWidthMap[seriesIndex] < barWidth) {
  612. // 不满足最大宽度
  613. gap -= barWidth - barMaxWidthMap[seriesIndex]; // 总宽度减少
  614. }
  615. }
  616. return {
  617. barWidthMap: barWidthMap,
  618. barMaxWidthMap: barMaxWidthMap,
  619. barMinHeightMap: barMinHeightMap ,
  620. gap: gap, // 总宽度
  621. barWidth: barWidth,
  622. barGap: barGap,
  623. interval: interval
  624. };
  625. },
  626. /**
  627. * 生成最终图形数据
  628. */
  629. _getBarItem: function (seriesIndex, dataIndex, name, x, y, width, height, orient) {
  630. var series = this.series;
  631. var barShape;
  632. var serie = series[seriesIndex];
  633. var data = serie.data[dataIndex];
  634. // 多级控制
  635. var defaultColor = this._sIndex2ColorMap[seriesIndex];
  636. var queryTarget = [data, serie];
  637. var normal = this.deepMerge(queryTarget, 'itemStyle.normal');
  638. var emphasis = this.deepMerge(queryTarget, 'itemStyle.emphasis');
  639. var normalBorderWidth = normal.barBorderWidth;
  640. barShape = {
  641. zlevel: serie.zlevel,
  642. z: serie.z,
  643. clickable: this.deepQuery(queryTarget, 'clickable'),
  644. style: {
  645. x: x,
  646. y: y,
  647. width: width,
  648. height: height,
  649. brushType: 'both',
  650. color: this.getItemStyleColor(
  651. this.deepQuery(queryTarget, 'itemStyle.normal.color') || defaultColor,
  652. seriesIndex, dataIndex, data
  653. ),
  654. radius: normal.barBorderRadius,
  655. lineWidth: normalBorderWidth,
  656. strokeColor: normal.barBorderColor
  657. },
  658. highlightStyle: {
  659. color: this.getItemStyleColor(
  660. this.deepQuery(queryTarget, 'itemStyle.emphasis.color'),
  661. seriesIndex, dataIndex, data
  662. ),
  663. radius: emphasis.barBorderRadius,
  664. lineWidth: emphasis.barBorderWidth,
  665. strokeColor: emphasis.barBorderColor
  666. },
  667. _orient: orient
  668. };
  669. var barShapeStyle = barShape.style;
  670. barShape.highlightStyle.color = barShape.highlightStyle.color
  671. || (typeof barShapeStyle.color === 'string'
  672. ? zrColor.lift(barShapeStyle.color, -0.3)
  673. : barShapeStyle.color
  674. );
  675. //亚像素优化
  676. barShapeStyle.x = Math.floor(barShapeStyle.x);
  677. barShapeStyle.y = Math.floor(barShapeStyle.y);
  678. barShapeStyle.height = Math.ceil(barShapeStyle.height);
  679. barShapeStyle.width = Math.ceil(barShapeStyle.width);
  680. // 考虑线宽的显示优化
  681. if (normalBorderWidth > 0
  682. && barShapeStyle.height > normalBorderWidth
  683. && barShapeStyle.width > normalBorderWidth
  684. ) {
  685. barShapeStyle.y += normalBorderWidth / 2;
  686. barShapeStyle.height -= normalBorderWidth;
  687. barShapeStyle.x += normalBorderWidth / 2;
  688. barShapeStyle.width -= normalBorderWidth;
  689. }
  690. else {
  691. // 太小了或者线宽小于0,废了边线
  692. barShapeStyle.brushType = 'fill';
  693. }
  694. barShape.highlightStyle.textColor = barShape.highlightStyle.color;
  695. barShape = this.addLabel(barShape, serie, data, name, orient);
  696. var barShapeStyleList = [ // normal emphasis都需要检查
  697. barShapeStyle,
  698. barShape.highlightStyle
  699. ];
  700. for (var i = 0, l = barShapeStyleList.length; i < l; i++) {
  701. var textPosition = barShapeStyleList[i].textPosition;
  702. if (textPosition === 'insideLeft'
  703. || textPosition === 'insideRight'
  704. || textPosition === 'insideTop'
  705. || textPosition === 'insideBottom'
  706. ) {
  707. var gap = 5;
  708. switch (textPosition) {
  709. case 'insideLeft':
  710. barShapeStyleList[i].textX = barShapeStyle.x + gap;
  711. barShapeStyleList[i].textY = barShapeStyle.y + barShapeStyle.height / 2;
  712. barShapeStyleList[i].textAlign = 'left';
  713. barShapeStyleList[i].textBaseline = 'middle';
  714. break;
  715. case 'insideRight':
  716. barShapeStyleList[i].textX = barShapeStyle.x + barShapeStyle.width - gap;
  717. barShapeStyleList[i].textY = barShapeStyle.y + barShapeStyle.height / 2;
  718. barShapeStyleList[i].textAlign = 'right';
  719. barShapeStyleList[i].textBaseline = 'middle';
  720. break;
  721. case 'insideTop':
  722. barShapeStyleList[i].textX = barShapeStyle.x + barShapeStyle.width / 2;
  723. barShapeStyleList[i].textY = barShapeStyle.y + gap / 2;
  724. barShapeStyleList[i].textAlign = 'center';
  725. barShapeStyleList[i].textBaseline = 'top';
  726. break;
  727. case 'insideBottom':
  728. barShapeStyleList[i].textX = barShapeStyle.x + barShapeStyle.width / 2;
  729. barShapeStyleList[i].textY = barShapeStyle.y + barShapeStyle.height - gap / 2;
  730. barShapeStyleList[i].textAlign = 'center';
  731. barShapeStyleList[i].textBaseline = 'bottom';
  732. break;
  733. }
  734. barShapeStyleList[i].textPosition = 'specific';
  735. barShapeStyleList[i].textColor = barShapeStyleList[i].textColor || '#fff';
  736. }
  737. }
  738. if (this.deepQuery([data, serie, this.option],'calculable')) {
  739. this.setCalculable(barShape);
  740. barShape.draggable = true;
  741. }
  742. ecData.pack(
  743. barShape,
  744. series[seriesIndex], seriesIndex,
  745. series[seriesIndex].data[dataIndex], dataIndex,
  746. name
  747. );
  748. return barShape;
  749. },
  750. // 位置转换
  751. getMarkCoord: function (seriesIndex, mpData) {
  752. var serie = this.series[seriesIndex];
  753. var xMarkMap = this.xMarkMap[seriesIndex];
  754. var xAxis = this.component.xAxis.getAxis(serie.xAxisIndex);
  755. var yAxis = this.component.yAxis.getAxis(serie.yAxisIndex);
  756. var dataIndex;
  757. var pos;
  758. if (mpData.type
  759. && (mpData.type === 'max' || mpData.type === 'min' || mpData.type === 'average')
  760. ) {
  761. // 特殊值内置支持
  762. var valueIndex = mpData.valueIndex != null
  763. ? mpData.valueIndex
  764. : xMarkMap.maxX0 != null
  765. ? '1' : '';
  766. pos = [
  767. xMarkMap[mpData.type + 'X' + valueIndex],
  768. xMarkMap[mpData.type + 'Y' + valueIndex],
  769. xMarkMap[mpData.type + 'Line' + valueIndex],
  770. xMarkMap[mpData.type + valueIndex]
  771. ];
  772. }
  773. else if (xMarkMap.isHorizontal) {
  774. // 横向
  775. dataIndex = typeof mpData.xAxis === 'string' && xAxis.getIndexByName
  776. ? xAxis.getIndexByName(mpData.xAxis)
  777. : (mpData.xAxis || 0);
  778. var x = xMarkMap[dataIndex];
  779. x = x != null
  780. ? x
  781. : typeof mpData.xAxis != 'string' && xAxis.getCoordByIndex
  782. ? xAxis.getCoordByIndex(mpData.xAxis || 0)
  783. : xAxis.getCoord(mpData.xAxis || 0);
  784. pos = [x, yAxis.getCoord(mpData.yAxis || 0)];
  785. }
  786. else {
  787. // 纵向
  788. dataIndex = typeof mpData.yAxis === 'string' && yAxis.getIndexByName
  789. ? yAxis.getIndexByName(mpData.yAxis)
  790. : (mpData.yAxis || 0);
  791. var y = xMarkMap[dataIndex];
  792. y = y != null
  793. ? y
  794. : typeof mpData.yAxis != 'string' && yAxis.getCoordByIndex
  795. ? yAxis.getCoordByIndex(mpData.yAxis || 0)
  796. : yAxis.getCoord(mpData.yAxis || 0);
  797. pos = [xAxis.getCoord(mpData.xAxis || 0), y];
  798. }
  799. return pos;
  800. },
  801. /**
  802. * 刷新
  803. */
  804. refresh: function (newOption) {
  805. if (newOption) {
  806. this.option = newOption;
  807. this.series = newOption.series;
  808. }
  809. this.backupShapeList();
  810. this._buildShape();
  811. },
  812. /**
  813. * 动态数据增加动画
  814. */
  815. addDataAnimation: function (params, done) {
  816. var series = this.series;
  817. var aniMap = {}; // seriesIndex索引参数
  818. for (var i = 0, l = params.length; i < l; i++) {
  819. aniMap[params[i][0]] = params[i];
  820. }
  821. var x;
  822. var dx;
  823. var y;
  824. var dy;
  825. var serie;
  826. var seriesIndex;
  827. var dataIndex;
  828. var aniCount = 0;
  829. function animationDone() {
  830. aniCount--;
  831. if (aniCount === 0) {
  832. done && done();
  833. }
  834. }
  835. for (var i = this.shapeList.length - 1; i >= 0; i--) {
  836. seriesIndex = ecData.get(this.shapeList[i], 'seriesIndex');
  837. if (aniMap[seriesIndex] && !aniMap[seriesIndex][3]) {
  838. // 有数据删除才有移动的动画
  839. if (this.shapeList[i].type === 'rectangle') {
  840. // 主动画
  841. dataIndex = ecData.get(this.shapeList[i], 'dataIndex');
  842. serie = series[seriesIndex];
  843. if (aniMap[seriesIndex][2] && dataIndex === serie.data.length - 1) {
  844. // 队头加入删除末尾
  845. this.zr.delShape(this.shapeList[i].id);
  846. continue;
  847. }
  848. else if (!aniMap[seriesIndex][2] && dataIndex === 0) {
  849. // 队尾加入删除头部
  850. this.zr.delShape(this.shapeList[i].id);
  851. continue;
  852. }
  853. if (this.shapeList[i]._orient === 'horizontal') {
  854. // 条形图
  855. dy = this.component.yAxis.getAxis(serie.yAxisIndex || 0).getGap();
  856. y = aniMap[seriesIndex][2] ? -dy : dy;
  857. x = 0;
  858. }
  859. else {
  860. // 柱形图
  861. dx = this.component.xAxis.getAxis(serie.xAxisIndex || 0).getGap();
  862. x = aniMap[seriesIndex][2] ? dx : -dx;
  863. y = 0;
  864. }
  865. this.shapeList[i].position = [0, 0];
  866. aniCount++;
  867. this.zr.animate(this.shapeList[i].id, '')
  868. .when(
  869. this.query(this.option, 'animationDurationUpdate'),
  870. { position: [x, y] }
  871. )
  872. .done(animationDone)
  873. .start();
  874. }
  875. }
  876. }
  877. // 没有动画
  878. if (!aniCount) {
  879. done && done();
  880. }
  881. }
  882. };
  883. zrUtil.inherits(Bar, ChartBase);
  884. // 图表注册
  885. require('../chart').define('bar', Bar);
  886. return Bar;
  887. });