You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

histogramUnit.vue 26 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855
  1. <!--
  2. Copyright 2020 Huawei Technologies Co., Ltd.All Rights Reserved.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. -->
  13. <template>
  14. <div class="cl-histogram-container">
  15. <div class="data-show-container">
  16. <div :id="itemId"
  17. v-show="!!fullData.length"
  18. class="data-item"></div>
  19. </div>
  20. </div>
  21. </template>
  22. <script>
  23. import echarts from 'echarts';
  24. import CommonProperty from '../common/common-property';
  25. import {format, precisionRound} from 'd3';
  26. const d3 = {format, precisionRound};
  27. export default {
  28. props: {
  29. // Histogram data
  30. fullData: {
  31. type: Array,
  32. default() {
  33. return [];
  34. },
  35. },
  36. // View name
  37. viewName: {
  38. type: Number,
  39. default: 1,
  40. },
  41. // Axis name
  42. axisName: {
  43. type: Number,
  44. default: 0,
  45. },
  46. // Display full screen
  47. fullScreen: {
  48. type: Boolean,
  49. default: false,
  50. },
  51. },
  52. data() {
  53. return {
  54. itemId: '', // Dom id
  55. oriData: {}, // Original data
  56. charOption: {}, // Chart configuration
  57. charObj: null, // Chart Object
  58. updated: false, // Updated
  59. zrDrawElement: {hoverDots: []},
  60. zr: null,
  61. chartTipFlag: false, // Wheather to display tips of the histogram
  62. };
  63. },
  64. computed: {},
  65. watch: {},
  66. mounted() {
  67. this.init();
  68. },
  69. methods: {
  70. /**
  71. * Initialize
  72. */
  73. init() {
  74. this.itemId =
  75. `${new Date().getTime()}` + `${this.$store.state.componentsCount}`;
  76. this.$store.commit('componentsNum');
  77. },
  78. /**
  79. * Update the view Size
  80. */
  81. resizeView() {
  82. if (this.charObj) {
  83. this.charObj.resize();
  84. }
  85. },
  86. /**
  87. * Convert to chart data
  88. */
  89. formatDataToChar() {
  90. const chartData = this.fullData;
  91. const seriesData = [];
  92. let maxX = -Infinity;
  93. let minX = Infinity;
  94. let maxZ = -Infinity;
  95. let minZ = Infinity;
  96. const gridData = [];
  97. if (chartData && chartData.length) {
  98. chartData.forEach((histogram) => {
  99. const seriesItem = [];
  100. gridData.push(histogram.step);
  101. histogram.items.forEach((bucket) => {
  102. if (this.viewName === 0) {
  103. seriesItem.push([bucket[2], bucket[3]]);
  104. } else if (this.viewName === 1) {
  105. seriesItem.push(bucket[2], histogram.step, bucket[3]);
  106. }
  107. maxX = Math.max(maxX, bucket[2]);
  108. minX = Math.min(minX, bucket[2]);
  109. minZ = Math.min(minZ, bucket[3]);
  110. maxZ = Math.max(maxZ, bucket[3]);
  111. });
  112. seriesData.push(seriesItem);
  113. });
  114. }
  115. this.oriData = {
  116. seriesData,
  117. maxX,
  118. minX,
  119. maxZ,
  120. minZ,
  121. gridData,
  122. };
  123. },
  124. /**
  125. * Update sample data
  126. */
  127. updateSampleData() {
  128. this.charOption = this.formatCharOption();
  129. if (!this.charObj) {
  130. const chartItem = document.getElementById(this.itemId);
  131. if (!chartItem) {
  132. return;
  133. }
  134. this.charObj = echarts.init(chartItem, null);
  135. }
  136. this.removeTooltip();
  137. this.charObj.setOption(this.charOption, true);
  138. },
  139. /**
  140. * Binding interaction event
  141. */
  142. sampleEventBind() {
  143. if (!this.zr) {
  144. this.zr = this.charObj.getZr();
  145. this.zr.off('mouseout', 'mousemove');
  146. this.zr.on('mouseout', (e) => {
  147. this.removeTooltip();
  148. this.chartTipFlag = false;
  149. this.$emit('chartTipFlagChange', this.chartTipFlag);
  150. });
  151. this.zr.on('mousemove', (e) => {
  152. this.removeTooltip();
  153. this.mousemoveEvent(e);
  154. });
  155. }
  156. },
  157. /**
  158. * Formate chart option
  159. * @return {Object} chatr option
  160. */
  161. formatCharOption() {
  162. const colorMin = '#346E69';
  163. const colorMax = '#EBFFFD';
  164. const oriData = this.oriData;
  165. const colorArr = this.getGrientColor(
  166. colorMin,
  167. colorMax,
  168. oriData.seriesData.length,
  169. );
  170. const fullScreenFun = this.toggleFullScreen;
  171. const axisName = this.axisName;
  172. const that = this;
  173. const option = {
  174. grid: {
  175. left: 40,
  176. top: 60,
  177. right: 80,
  178. bottom: 60,
  179. },
  180. xAxis: {
  181. max: oriData.maxX,
  182. min: oriData.minX,
  183. axisLine: {onZero: false},
  184. axisLabel: {
  185. fontSize: '11',
  186. formatter: function(value) {
  187. return that.formateNUmber(value);
  188. },
  189. },
  190. splitLine: {show: false},
  191. },
  192. yAxis: {
  193. position: 'right',
  194. axisLine: {onZero: false, show: false},
  195. splitLine: {show: true},
  196. axisTick: {show: false},
  197. boundaryGap: false,
  198. axisLabel: {
  199. fontSize: '11',
  200. formatter: function(value) {
  201. return that.formateNUmber(value);
  202. },
  203. },
  204. },
  205. toolbox: {
  206. top: 20,
  207. right: 20,
  208. emphasis: {
  209. iconStyle: {
  210. textPosition: 'top',
  211. borderColor: '#00A5A7',
  212. },
  213. },
  214. // toolbox
  215. feature: {
  216. // fullScreen
  217. myToolFullScreen: {
  218. show: true,
  219. title: this.$t('histogram.fullScreen'),
  220. iconStyle: {
  221. borderColor: this.fullScreen ? '#00A5A7' : '#6D7278',
  222. },
  223. icon: CommonProperty.fullScreenIcon,
  224. onclick() {
  225. fullScreenFun();
  226. },
  227. },
  228. },
  229. },
  230. };
  231. if (this.viewName === 1) {
  232. const seriesData = [];
  233. oriData.seriesData.forEach((item, dataIndex) => {
  234. const dataItem = {
  235. name: item[1],
  236. value: item,
  237. itemStyle: {
  238. color: colorArr[dataIndex],
  239. },
  240. };
  241. seriesData.push(dataItem);
  242. });
  243. option.series = [
  244. {
  245. type: 'custom',
  246. dimensions: ['x', 'y'],
  247. renderItem: (params, api) => {
  248. const points = this.makePolyPoints(
  249. params.dataIndex,
  250. api.coord,
  251. params.coordSys.y - 10,
  252. );
  253. return {
  254. type: 'polyline',
  255. z2: params.dataIndex,
  256. silent: true,
  257. shape: {
  258. points,
  259. },
  260. style: api.style({
  261. stroke: '#bbb',
  262. lineWidth: 1,
  263. }),
  264. };
  265. },
  266. data: seriesData,
  267. },
  268. ];
  269. option.yAxis.data = oriData.gridData;
  270. option.yAxis.type = 'category';
  271. option.grid.top = 126;
  272. if (axisName === 2 && this.fullScreen) {
  273. option.grid.right = 140;
  274. }
  275. option.yAxis.inverse = true;
  276. option.yAxis.axisLabel.formatter = function(value) {
  277. return that.yAxisFormatter(value);
  278. };
  279. } else if (this.viewName === 0) {
  280. option.color = colorArr;
  281. option.series = [];
  282. oriData.seriesData.forEach((k) => {
  283. option.series.push({
  284. type: 'line',
  285. symbol: 'none',
  286. lineStyle: {
  287. width: 1,
  288. },
  289. data: k.slice(1, -1),
  290. });
  291. });
  292. }
  293. return option;
  294. },
  295. /**
  296. * Expand/Collapse in full screen
  297. */
  298. toggleFullScreen() {
  299. this.removeTooltip();
  300. if (!this.fullScreen) {
  301. if (this.axisName === 2) {
  302. this.charOption.grid.right = 140;
  303. }
  304. this.charOption.toolbox.feature.myToolFullScreen.iconStyle.borderColor =
  305. '#00A5A7';
  306. } else {
  307. this.charOption.grid.right = 80;
  308. this.charOption.toolbox.feature.myToolFullScreen.iconStyle.borderColor =
  309. '#6D7278';
  310. }
  311. this.charObj.setOption(this.charOption);
  312. this.$emit('toggleFullScreen');
  313. },
  314. /**
  315. * Remove tooltip
  316. */
  317. removeTooltip() {
  318. if (this.zr) {
  319. if (this.zrDrawElement.hoverDots) {
  320. this.zrDrawElement.hoverDots.forEach((dot) => this.zr.remove(dot));
  321. }
  322. if (this.zrDrawElement.hoverLine) {
  323. this.zr.remove(this.zrDrawElement.hoverLine);
  324. }
  325. if (this.zrDrawElement.tooltip) {
  326. this.zr.remove(this.zrDrawElement.tooltip);
  327. }
  328. if (this.zrDrawElement.tooltipY) {
  329. this.zr.remove(this.zrDrawElement.tooltipY);
  330. }
  331. if (this.zrDrawElement.tooltipX) {
  332. this.zr.remove(this.zrDrawElement.tooltipX);
  333. }
  334. }
  335. },
  336. /**
  337. * Calculate polygon points
  338. * @param {Number} dataIndex
  339. * @param {Object} getCoord
  340. * @param {Number} yValueMapHeight
  341. * @return {Array} Array of ploygon points
  342. */
  343. makePolyPoints(dataIndex, getCoord, yValueMapHeight) {
  344. const points = [];
  345. const rawData = this.oriData.seriesData;
  346. const maxZ = this.oriData.maxZ;
  347. const minZ = this.oriData.minZ;
  348. for (let i = 0; i < rawData[dataIndex].length; ) {
  349. const x = this.getValue(rawData, dataIndex, i++);
  350. const y = this.getValue(rawData, dataIndex, i++);
  351. const z = this.getValue(rawData, dataIndex, i++);
  352. const pt = getCoord([x, y]);
  353. // Linear map in z axis
  354. if (maxZ !== minZ) {
  355. pt[1] -= ((z - minZ) / (maxZ - minZ)) * yValueMapHeight;
  356. }
  357. points.push(pt);
  358. }
  359. return points;
  360. },
  361. /**
  362. * Get convert point
  363. * @param {Array} pt value
  364. * @return {Array}
  365. */
  366. getCoord(pt) {
  367. return this.charObj.convertToPixel('grid', pt);
  368. },
  369. /**
  370. * Formate Y coordinate display
  371. * @param {Number} value
  372. * @return {Object}
  373. */
  374. yAxisFormatter(value) {
  375. let data = '';
  376. const filter = this.fullData.filter((k) => k.step === value);
  377. if (filter.length) {
  378. if (this.axisName === 2) {
  379. data = this.fullScreen
  380. ? this.dealrelativeTime(
  381. new Date(filter[0].wall_time * 1000).toString(),
  382. )
  383. : [];
  384. } else if (this.axisName === 1) {
  385. data = `${this.formateNUmber(
  386. (filter[0].relative_time).toFixed(0),
  387. )}s`;
  388. } else {
  389. data = this.formateNUmber(filter[0].step);
  390. }
  391. }
  392. return data;
  393. },
  394. /**
  395. * Formate time display
  396. * @param {Number} value
  397. * @return {Number} Formatted number
  398. */
  399. formateNUmber(value) {
  400. value = Number(value);
  401. if (value.toString().length > 6) {
  402. return value.toExponential(3);
  403. } else {
  404. return Math.round(value * 1000) / 1000;
  405. }
  406. },
  407. /**
  408. * Formate time display
  409. * @param {Object} time
  410. * @return {String} Formatted time
  411. */
  412. dealrelativeTime(time) {
  413. const arr = time.split(' ');
  414. const str = arr[0] + ' ' + arr[1] + ' ' + arr[2] + ',' + ' ' + arr[4];
  415. return str;
  416. },
  417. /**
  418. * Mouse move event
  419. * @param {Object} e Original event
  420. */
  421. mousemoveEvent(e) {
  422. const unit = 's';
  423. const nearestIndex = this.findNearestValue([e.offsetX, e.offsetY]);
  424. if (
  425. nearestIndex &&
  426. nearestIndex.yIndex !== null &&
  427. nearestIndex.binIndex !== null
  428. ) {
  429. const {binIndex, yIndex} = nearestIndex;
  430. const chartData = this.fullData;
  431. const hoveredItem = chartData[yIndex];
  432. const p = Math.max(0, d3.precisionRound(0.01, 1.01) - 1);
  433. const yValueFormat = d3.format(`.${p}e`);
  434. const gridRect = this.charObj
  435. .getModel()
  436. .getComponent('grid', 0)
  437. .coordinateSystem.getRect();
  438. const gridRectY = gridRect.y - 10;
  439. let linePoints = [];
  440. if (!hoveredItem || !hoveredItem.items[binIndex]) {
  441. return;
  442. }
  443. if (!this.chartTipFlag) {
  444. this.chartTipFlag = true;
  445. this.$emit('chartTipFlagChange', this.chartTipFlag);
  446. }
  447. if (this.viewName === 1 && yIndex !== null) {
  448. linePoints = this.makePolyPoints(yIndex, this.getCoord, gridRectY);
  449. } else if (this.viewName === 0 && hoveredItem.items) {
  450. hoveredItem.items.forEach((item) => {
  451. linePoints.push(this.getCoord([item[2], item[3]]));
  452. });
  453. }
  454. this.zrDrawElement.hoverLine = new echarts.graphic.Polyline({
  455. silent: true,
  456. shape: {
  457. points: linePoints.slice(1, -1),
  458. },
  459. z: 999,
  460. });
  461. this.zr.add(this.zrDrawElement.hoverLine);
  462. this.zrDrawElement.tooltip = new echarts.graphic.Text({});
  463. let itemX;
  464. const x = hoveredItem.items[binIndex][2];
  465. let z = 0;
  466. chartData.forEach((dataItem, index) => {
  467. const y = dataItem.step;
  468. const pt = this.getCoord([x, y]);
  469. if (index === yIndex) {
  470. z = hoveredItem.items[binIndex][3];
  471. } else {
  472. const items = dataItem.items;
  473. for (let k = 1; k < items.length - 1; k++) {
  474. const nextX = items[k + 1][2];
  475. const nextZ = items[k + 1][3];
  476. if (items[k][2] === x) {
  477. z = items[k][3];
  478. break;
  479. } else if (items[k][2] < x && nextX > x) {
  480. const proportionX = (x - items[k][2]) / (nextX - items[k][2]);
  481. z = (nextZ - items[k][3]) * proportionX + items[k][3];
  482. break;
  483. }
  484. }
  485. }
  486. itemX = pt[0];
  487. const circleOption = {
  488. z: 1000,
  489. };
  490. if (this.viewName === 1) {
  491. pt[1] -=
  492. ((z - this.oriData.minZ) /
  493. (this.oriData.maxZ - this.oriData.minZ)) *
  494. gridRectY;
  495. circleOption.shape = {
  496. cx: itemX,
  497. cy: pt[1],
  498. r: 1.5,
  499. };
  500. } else {
  501. circleOption.shape = {
  502. cx: 0,
  503. cy: 0,
  504. r: 1.5,
  505. };
  506. circleOption.position = this.charObj.convertToPixel('grid', [x, z]);
  507. }
  508. const dot = new echarts.graphic.Circle(circleOption);
  509. this.zr.add(dot);
  510. this.zrDrawElement.hoverDots.push(dot);
  511. });
  512. this.zrDrawElement.tooltip = new echarts.graphic.Text({});
  513. let htmlStr = '';
  514. const hoveredAxis = hoveredItem.items[binIndex][3];
  515. htmlStr = `<td>${
  516. hoveredAxis.toString().length >= 6
  517. ? yValueFormat(hoveredAxis)
  518. : hoveredAxis
  519. }</td><td style="text-align:center;">${this.formateNUmber(
  520. hoveredItem.step,
  521. )}</td><td>${this.formateNUmber(
  522. (hoveredItem.relative_time).toFixed(0),
  523. )}${unit}</td><td>${this.dealrelativeTime(
  524. new Date(hoveredItem.wall_time * 1000).toString(),
  525. )}</td>`;
  526. const dom = document.querySelector('#tipTr');
  527. dom.innerHTML = htmlStr;
  528. const chartElement = document.getElementById(this.itemId);
  529. if (chartElement) {
  530. if (!this.fullScreen) {
  531. const chartWidth =
  532. chartElement.parentNode.parentNode.parentNode.parentNode
  533. .clientWidth;
  534. const chartHeight =
  535. chartElement.parentNode.parentNode.parentNode.parentNode
  536. .clientHeight;
  537. const left =
  538. chartElement.parentNode.parentNode.parentNode.parentNode
  539. .offsetLeft;
  540. const top =
  541. chartElement.parentNode.parentNode.parentNode.parentNode
  542. .offsetTop;
  543. const echartTip = document.querySelector('#echartTip');
  544. echartTip.style.top = `${top + chartHeight - 60}px`;
  545. if (left > echartTip.clientWidth) {
  546. echartTip.style.left = `${left - echartTip.clientWidth}px`;
  547. } else {
  548. echartTip.style.left = `${left + chartWidth}px`;
  549. }
  550. } else {
  551. const width = document.querySelector('#echartTip').clientWidth;
  552. const height = document.querySelector('#echartTip').clientHeight;
  553. const screenWidth = document.body.scrollWidth;
  554. const screenHeight = document.body.scrollHeight;
  555. const scrollTop = document.querySelector('.cl-show-data-content')
  556. .scrollTop;
  557. const offsetTop = document.querySelector('.cl-show-data-content')
  558. .offsetTop;
  559. if (
  560. height + e.event.y + 20 > screenHeight &&
  561. screenHeight > height
  562. ) {
  563. document.querySelector('#echartTip').style.top = `${e.event.y +
  564. scrollTop -
  565. height -
  566. 20 -
  567. offsetTop}px`;
  568. } else {
  569. document.querySelector('#echartTip').style.top = `${e.event.y +
  570. scrollTop +
  571. 20 -
  572. offsetTop}px`;
  573. }
  574. // Blank area on the right of the chart is 80
  575. if (width + e.event.x + 80 > screenWidth && screenWidth > width) {
  576. document.querySelector('#echartTip').style.left = `${e.event.x -
  577. width -
  578. 20}px`;
  579. } else {
  580. document.querySelector('#echartTip').style.left = `${e.event.x +
  581. 20}px`;
  582. }
  583. }
  584. }
  585. this.zrDrawElement.tooltipX = new echarts.graphic.Text({
  586. position: [itemX, gridRect.y + gridRect.height],
  587. style: {
  588. text:
  589. x.toString().length >= 6
  590. ? x.toExponential(3)
  591. : Math.round(x * 1000) / 1000,
  592. textFill: '#fff',
  593. textAlign: 'center',
  594. fontSize: 12,
  595. textBackgroundColor: '#333',
  596. textBorderWidth: 2,
  597. textPadding: [5, 7],
  598. rich: {},
  599. },
  600. z: 2000,
  601. });
  602. this.zr.add(this.zrDrawElement.tooltipX);
  603. if (this.viewName === 1 && linePoints && linePoints.length) {
  604. let text = '';
  605. if (yIndex !== null) {
  606. text = this.yAxisFormatter(hoveredItem.step);
  607. }
  608. this.zrDrawElement.tooltipY = new echarts.graphic.Text({
  609. position: [
  610. gridRect.x + gridRect.width,
  611. linePoints[linePoints.length - 1][1],
  612. ],
  613. style: {
  614. text: text,
  615. textFill: '#fff',
  616. textVerticalAlign: 'middle',
  617. fontSize: 12,
  618. textBackgroundColor: '#333',
  619. textBorderWidth: 2,
  620. textPadding: [5, 7],
  621. rich: {},
  622. },
  623. z: 2000,
  624. });
  625. this.zr.add(this.zrDrawElement.tooltipY);
  626. }
  627. }
  628. },
  629. /**
  630. * Find nearest value
  631. * @param {Array} eventPoint Value
  632. * @return {Object}
  633. */
  634. findNearestValue(eventPoint) {
  635. if (!eventPoint || !eventPoint.length || !this.charObj || !this.oriData) {
  636. return;
  637. }
  638. const value = this.charObj.convertFromPixel('grid', eventPoint);
  639. if (!value || !value.length) {
  640. return;
  641. }
  642. let binIndex = null;
  643. let yIndex = null;
  644. let nearestX = Infinity;
  645. let nearestY = -Infinity;
  646. let nearestYData = Infinity;
  647. const gridRect = this.charObj
  648. .getModel()
  649. .getComponent('grid', 0)
  650. .coordinateSystem.getRect();
  651. const gridRectY = gridRect.y - 10;
  652. const x = value[0];
  653. this.fullData.forEach((dataItem, i) => {
  654. let distY;
  655. let yAxis;
  656. for (let k = 0; k < dataItem.items.length - 1; k++) {
  657. const item = dataItem.items[k];
  658. const itemNext = dataItem.items[k + 1];
  659. const nextX = itemNext[2];
  660. const nextZ = itemNext[3];
  661. if (item.length >= 4) {
  662. if (item[2] < x && nextX >= x) {
  663. const proportionX = (x - item[2]) / (nextX - item[2]);
  664. yAxis = (nextZ - item[3]) * proportionX + item[3];
  665. distY = Math.abs(value[1] - yAxis);
  666. break;
  667. }
  668. }
  669. }
  670. if (this.viewName === 0 && distY < nearestYData) {
  671. nearestYData = distY;
  672. yIndex = i;
  673. } else if (this.viewName === 1) {
  674. const pt = this.getCoord([x, dataItem.step]);
  675. const ptStep = pt[1];
  676. pt[1] -=
  677. ((yAxis - this.oriData.minZ) /
  678. (this.oriData.maxZ - this.oriData.minZ)) *
  679. gridRectY;
  680. if (
  681. eventPoint[1] > pt[1] &&
  682. eventPoint[1] < ptStep &&
  683. ptStep > nearestY
  684. ) {
  685. nearestY = ptStep;
  686. yIndex = i;
  687. }
  688. }
  689. });
  690. if (yIndex === null && this.viewName === 1) {
  691. this.fullData.forEach((item, index) => {
  692. if (index >= value[1]) {
  693. yIndex = yIndex === null ? index : Math.min(yIndex, index);
  694. }
  695. });
  696. }
  697. if (yIndex !== null) {
  698. const yData = this.fullData[yIndex].items;
  699. yData.forEach((ele, index) => {
  700. const distX = Math.abs(ele[2] - value[0]);
  701. if (distX < nearestX) {
  702. nearestX = distX;
  703. binIndex = index;
  704. }
  705. });
  706. binIndex =
  707. binIndex === 0
  708. ? 1
  709. : binIndex === yData.length - 1
  710. ? yData.length - 2
  711. : binIndex;
  712. }
  713. return {
  714. binIndex,
  715. yIndex,
  716. };
  717. },
  718. /**
  719. * Calculate gradient color
  720. * @param {String} startColor
  721. * @param {String} endColor
  722. * @param {Number} step
  723. * @return {Array} Array of gradient color
  724. */
  725. getGrientColor(startColor, endColor, step) {
  726. const startRgb = this.formatColor(startColor);
  727. const endRgb = this.formatColor(endColor);
  728. const gapRgbR = (endRgb[0] - startRgb[0]) / step;
  729. const gapRgbG = (endRgb[1] - startRgb[1]) / step;
  730. const gapRgbB = (endRgb[2] - startRgb[2]) / step;
  731. const colorResult = [];
  732. for (let i = 0; i < step; i++) {
  733. const sR = parseInt(gapRgbR * i + startRgb[0]);
  734. const sG = parseInt(gapRgbG * i + startRgb[1]);
  735. const sB = parseInt(gapRgbB * i + startRgb[2]);
  736. const hex = this.formatColorToHex(`rgb(${sR},${sG},${sB})`);
  737. colorResult.push(hex);
  738. }
  739. return colorResult;
  740. },
  741. /**
  742. * Converts a color string to recognizable format
  743. * @param {String} str Color string
  744. * @return {String}
  745. */
  746. formatColor(str) {
  747. if (!str) {
  748. return;
  749. }
  750. const colorReg = /^([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
  751. let colorStr = str.toLowerCase().slice(1);
  752. if (colorReg.test(colorStr)) {
  753. let colorStrNew = '';
  754. if (colorStr.length === 3) {
  755. for (let i = 0; i < 3; i++) {
  756. colorStrNew += colorStrNew
  757. .slice(i, i + 1)
  758. .concat(colorStrNew.slice(i, i + 1));
  759. }
  760. colorStr = colorStrNew;
  761. }
  762. const colorFormat = [];
  763. for (let i = 0; i < 6; i += 2) {
  764. colorFormat.push(parseInt(`0x${colorStr.slice(i, i + 2)}`));
  765. }
  766. return colorFormat;
  767. } else {
  768. return colorStr;
  769. }
  770. },
  771. /**
  772. * Converts rgb color string to hex
  773. * @param {String} rgb Rgb color
  774. * @return {String} Hex color
  775. */
  776. formatColorToHex(rgb) {
  777. const regRgb = /^(rgb|RGB)/g;
  778. if (regRgb.test(rgb)) {
  779. const colorSplit = rgb.replace(/(?:(|)|rgb|RGB)*/g, '').split(',');
  780. let hexStr = '';
  781. for (let i = 0; i < colorSplit.length; i++) {
  782. let hexItem = Number(colorSplit[i]).toString(16);
  783. hexItem = hexItem < 10 ? `0${hexItem}` : hexItem;
  784. if (hexItem === '0') {
  785. hexItem += hexItem;
  786. }
  787. hexStr += hexItem;
  788. }
  789. if (hexStr.length !== 6) {
  790. hexStr = rgb;
  791. }
  792. return hexStr;
  793. }
  794. },
  795. /**
  796. * Get value
  797. * @param {Object} seriesData
  798. * @param {Number} dataIndex
  799. * @param {Number} i
  800. * @return {Number}
  801. */
  802. getValue(seriesData, dataIndex, i) {
  803. return seriesData[dataIndex][i];
  804. },
  805. /**
  806. * Unbing event
  807. */
  808. clearZrData() {
  809. if (this.zr) {
  810. this.removeTooltip();
  811. this.zr.off('mouseout', 'mousemove');
  812. this.zr = null;
  813. }
  814. },
  815. /**
  816. * Update histogram data
  817. */
  818. updateHistogramData() {
  819. this.$nextTick(() => {
  820. this.formatDataToChar();
  821. this.updateSampleData();
  822. this.sampleEventBind();
  823. });
  824. },
  825. },
  826. destroyed() {
  827. this.clearZrData();
  828. },
  829. };
  830. </script>
  831. <style lang="scss">
  832. .cl-histogram-container {
  833. width: 100%;
  834. height: 100%;
  835. display: flex;
  836. flex-direction: column;
  837. .data-show-container {
  838. width: 100%;
  839. flex: 1;
  840. .data-item {
  841. width: 100%;
  842. height: 100%;
  843. }
  844. }
  845. }
  846. </style>