8f9c6b832a6cf06f59274b88446c2787ee1031962de570f18cdfdb74b9c264bee4b609f05278a0d8853cbc6d67d5538933a18b61683a78af93418f9b2a8fe1 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742
  1. import { doUpdate } from '../class/updater.class'
  2. import { barConfig } from '../config'
  3. import { deepClone } from '@jiaminghi/c-render/lib/plugin/util'
  4. import { deepMerge, initNeedSeries, mergeSameStackData } from '../util'
  5. export function bar (chart, option = {}) {
  6. const { xAxis, yAxis, series } = option
  7. let bars = []
  8. if (xAxis && yAxis && series) {
  9. bars = initNeedSeries(series, barConfig, 'bar')
  10. bars = setBarAxis(bars, chart)
  11. bars = setBarPositionData(bars, chart)
  12. bars = calcBarsPosition(bars, chart)
  13. }
  14. doUpdate({
  15. chart,
  16. series: bars.slice(-1),
  17. key: 'backgroundBar',
  18. getGraphConfig: getBackgroundBarConfig
  19. })
  20. bars.reverse()
  21. doUpdate({
  22. chart,
  23. series: bars,
  24. key: 'bar',
  25. getGraphConfig: getBarConfig,
  26. getStartGraphConfig: getStartBarConfig,
  27. beforeUpdate: beforeUpdateBar
  28. })
  29. doUpdate({
  30. chart,
  31. series: bars,
  32. key: 'barLabel',
  33. getGraphConfig: getLabelConfig
  34. })
  35. }
  36. function setBarAxis (bars, chart) {
  37. const { axisData } = chart
  38. bars.forEach(bar => {
  39. let { xAxisIndex, yAxisIndex } = bar
  40. if (typeof xAxisIndex !== 'number') xAxisIndex = 0
  41. if (typeof yAxisIndex !== 'number') yAxisIndex = 0
  42. const xAxis = axisData.find(({ axis, index }) => `${axis}${index}` === `x${xAxisIndex}`)
  43. const yAxis = axisData.find(({ axis, index }) => `${axis}${index}` === `y${yAxisIndex}`)
  44. const axis = [xAxis, yAxis]
  45. const valueAxisIndex = axis.findIndex(({ data }) => data === 'value')
  46. bar.valueAxis = axis[valueAxisIndex]
  47. bar.labelAxis = axis[1 - valueAxisIndex]
  48. })
  49. return bars
  50. }
  51. function setBarPositionData (bars, chart) {
  52. const labelBarGroup = groupBarByLabelAxis(bars)
  53. labelBarGroup.forEach(group => {
  54. setBarIndex(group)
  55. setBarNum(group)
  56. setBarCategoryWidth(group, chart)
  57. setBarWidthAndGap(group)
  58. setBarAllWidthAndGap(group)
  59. })
  60. return bars
  61. }
  62. function setBarIndex (bars) {
  63. let stacks = getBarStack(bars)
  64. stacks = stacks.map(stack => ({ stack, index: -1 }))
  65. let currentIndex = 0
  66. bars.forEach(bar => {
  67. const { stack } = bar
  68. if (!stack) {
  69. bar.barIndex = currentIndex
  70. currentIndex++
  71. } else {
  72. const stackData = stacks.find(({ stack:s }) => s === stack)
  73. if (stackData.index === -1) {
  74. stackData.index = currentIndex
  75. currentIndex++
  76. }
  77. bar.barIndex = stackData.index
  78. }
  79. })
  80. }
  81. function groupBarByLabelAxis (bars) {
  82. let labelAxis = bars.map(({ labelAxis: { axis, index } }) => (axis + index))
  83. labelAxis = [...new Set(labelAxis)]
  84. return labelAxis.map(axisIndex => {
  85. return bars.filter(({ labelAxis: { axis, index } }) => (axis + index) === axisIndex)
  86. })
  87. }
  88. function getBarStack (bars) {
  89. const stacks = []
  90. bars.forEach(({ stack }) => {
  91. if (stack) stacks.push(stack)
  92. })
  93. return [...new Set(stacks)]
  94. }
  95. function setBarNum (bars) {
  96. const barNum = [...new Set(bars.map(({ barIndex}) => barIndex))].length
  97. bars.forEach(bar => (bar.barNum = barNum))
  98. }
  99. function setBarCategoryWidth (bars) {
  100. const lastBar = bars.slice(-1)[0]
  101. const { barCategoryGap, labelAxis: { tickGap } } = lastBar
  102. let barCategoryWidth = 0
  103. if (typeof barCategoryGap === 'number') {
  104. barCategoryWidth = barCategoryGap
  105. } else {
  106. barCategoryWidth = (1 - (parseInt(barCategoryGap) / 100)) * tickGap
  107. }
  108. bars.forEach(bar => (bar.barCategoryWidth = barCategoryWidth))
  109. }
  110. function setBarWidthAndGap (bars) {
  111. const { barCategoryWidth, barWidth, barGap, barNum } = bars.slice(-1)[0]
  112. let widthAndGap = []
  113. if (typeof barWidth === 'number' || barWidth !== 'auto') {
  114. widthAndGap = getBarWidthAndGapWithPercentOrNumber(barCategoryWidth, barWidth, barGap, barNum)
  115. } else if (barWidth === 'auto') {
  116. widthAndGap = getBarWidthAndGapWidthAuto(barCategoryWidth, barWidth, barGap, barNum)
  117. }
  118. const [width, gap] = widthAndGap
  119. bars.forEach(bar => {
  120. bar.barWidth = width
  121. bar.barGap = gap
  122. })
  123. }
  124. function getBarWidthAndGapWithPercentOrNumber (barCategoryWidth, barWidth, barGap) {
  125. let [width, gap] = [0, 0]
  126. if (typeof barWidth === 'number') {
  127. width = barWidth
  128. } else {
  129. width = parseInt(barWidth) / 100 * barCategoryWidth
  130. }
  131. if (typeof barGap === 'number') {
  132. gap = barGap
  133. } else {
  134. gap = parseInt(barGap) / 100 * width
  135. }
  136. return [width, gap]
  137. }
  138. function getBarWidthAndGapWidthAuto (barCategoryWidth, barWidth, barGap, barNum) {
  139. let [width, gap] = [0, 0]
  140. const barItemWidth = barCategoryWidth / barNum
  141. if (typeof barGap === 'number') {
  142. gap = barGap
  143. width = barItemWidth - gap
  144. } else {
  145. const percent = 10 + parseInt(barGap) / 10
  146. if (percent === 0) {
  147. width = barItemWidth * 2
  148. gap = -width
  149. } else {
  150. width = barItemWidth / percent * 10
  151. gap = barItemWidth - width
  152. }
  153. }
  154. return [width, gap]
  155. }
  156. function setBarAllWidthAndGap (bars) {
  157. const { barGap, barWidth, barNum } = bars.slice(-1)[0]
  158. const barAllWidthAndGap = (barGap + barWidth) * barNum - barGap
  159. bars.forEach(bar => (bar.barAllWidthAndGap = barAllWidthAndGap))
  160. }
  161. function calcBarsPosition (bars, chart) {
  162. bars = calcBarValueAxisCoordinate(bars)
  163. bars = calcBarLabelAxisCoordinate(bars)
  164. bars = eliminateNullBarLabelAxis(bars)
  165. bars = keepSameNumBetweenBarAndData(bars)
  166. return bars
  167. }
  168. function calcBarLabelAxisCoordinate (bars) {
  169. return bars.map(bar => {
  170. const { labelAxis, barAllWidthAndGap, barGap, barWidth, barIndex } = bar
  171. const { tickGap, tickPosition, axis } = labelAxis
  172. const coordinateIndex = axis === 'x' ? 0 : 1
  173. const barLabelAxisPos = tickPosition.map((tick, i) => {
  174. const barCategoryStartPos = tickPosition[i][coordinateIndex] - tickGap / 2
  175. const barItemsStartPos = barCategoryStartPos + (tickGap - barAllWidthAndGap) / 2
  176. return barItemsStartPos + (barIndex + 0.5) * barWidth + barIndex * barGap
  177. })
  178. return {
  179. ...bar,
  180. barLabelAxisPos
  181. }
  182. })
  183. }
  184. function calcBarValueAxisCoordinate (bars) {
  185. return bars.map(bar => {
  186. let data = mergeSameStackData(bar, bars)
  187. data = eliminateNonNumberData(bar, data)
  188. const { axis, minValue, maxValue, linePosition } = bar.valueAxis
  189. const startPos = getValuePos(
  190. minValue,
  191. maxValue,
  192. minValue < 0 ? 0 : minValue,
  193. linePosition,
  194. axis
  195. )
  196. const endPos = data.map(v => getValuePos(
  197. minValue,
  198. maxValue,
  199. v,
  200. linePosition,
  201. axis
  202. ))
  203. const barValueAxisPos = endPos.map(p => ([startPos, p]))
  204. return {
  205. ...bar,
  206. barValueAxisPos: barValueAxisPos
  207. }
  208. })
  209. }
  210. function eliminateNonNumberData (barItem, barData) {
  211. const { data } = barItem
  212. return barData
  213. .map((v, i) => typeof data[i] === 'number' ? v : null)
  214. .filter(d => d !== null)
  215. }
  216. function eliminateNullBarLabelAxis (bars) {
  217. return bars.map(bar => {
  218. const { barLabelAxisPos, data } = bar
  219. data.forEach((d, i) => {
  220. if (typeof d === 'number') return
  221. barLabelAxisPos[i] = null
  222. })
  223. return {
  224. ...bar,
  225. barLabelAxisPos: barLabelAxisPos.filter(p => p !== null)
  226. }
  227. })
  228. }
  229. function keepSameNumBetweenBarAndData (bars) {
  230. bars.forEach(bar => {
  231. const { data, barLabelAxisPos, barValueAxisPos } = bar
  232. const dataNum = data.filter(d => typeof d === 'number').length
  233. const axisPosNum = barLabelAxisPos.length
  234. if (axisPosNum > dataNum) {
  235. barLabelAxisPos.splice(dataNum)
  236. barValueAxisPos.splice(dataNum)
  237. }
  238. })
  239. return bars
  240. }
  241. function getValuePos (min, max, value, linePosition, axis) {
  242. if (typeof value !== 'number') return null
  243. const valueMinus = max - min
  244. const coordinateIndex = axis === 'x' ? 0 : 1
  245. const posMinus = linePosition[1][coordinateIndex] - linePosition[0][coordinateIndex]
  246. let percent = (value - min) / valueMinus
  247. if (valueMinus === 0) percent = 0
  248. const pos = percent * posMinus
  249. return pos + linePosition[0][coordinateIndex]
  250. }
  251. function getBackgroundBarConfig (barItem) {
  252. const { animationCurve, animationFrame, rLevel } = barItem
  253. const shapes = getBackgroundBarShapes(barItem)
  254. const style = getBackgroundBarStyle(barItem)
  255. return shapes.map(shape => ({
  256. name: 'rect',
  257. index: rLevel,
  258. visible: barItem.backgroundBar.show,
  259. animationCurve,
  260. animationFrame,
  261. shape,
  262. style
  263. }))
  264. }
  265. function getBackgroundBarShapes (barItem) {
  266. const { labelAxis, valueAxis } = barItem
  267. const { tickPosition } = labelAxis
  268. const { axis, linePosition } = valueAxis
  269. const width = getBackgroundBarWidth(barItem)
  270. const haltWidth = width / 2
  271. const posIndex = axis === 'x' ? 0 : 1
  272. const centerPos = tickPosition.map(p => p[1 - posIndex])
  273. const [start, end] = [linePosition[0][posIndex], linePosition[1][posIndex]]
  274. return centerPos.map(center => {
  275. if (axis === 'x') {
  276. return { x: start, y: center - haltWidth, w: end - start, h: width }
  277. } else {
  278. return { x: center - haltWidth, y: end, w: width, h: start - end }
  279. }
  280. })
  281. }
  282. function getBackgroundBarWidth (barItem) {
  283. const { barAllWidthAndGap, barCategoryWidth, backgroundBar } = barItem
  284. const { width } = backgroundBar
  285. if (typeof width === 'number') return width
  286. if (width === 'auto') return barAllWidthAndGap
  287. return parseInt(width) / 100 * barCategoryWidth
  288. }
  289. function getBackgroundBarStyle (barItem) {
  290. return barItem.backgroundBar.style
  291. }
  292. function getBarConfig (barItem) {
  293. const { barLabelAxisPos, animationCurve, animationFrame, rLevel } = barItem
  294. const name = getBarName(barItem)
  295. return barLabelAxisPos.map((foo, i) => ({
  296. name,
  297. index: rLevel,
  298. animationCurve,
  299. animationFrame,
  300. shape: getBarShape(barItem, i),
  301. style: getBarStyle(barItem, i)
  302. }))
  303. }
  304. function getBarName (barItem) {
  305. const { shapeType } = barItem
  306. if (shapeType === 'leftEchelon' || shapeType === 'rightEchelon') return 'polyline'
  307. return 'rect'
  308. }
  309. function getBarShape (barItem, i) {
  310. const { shapeType } = barItem
  311. if (shapeType === 'leftEchelon') {
  312. return getLeftEchelonShape(barItem, i)
  313. } else if (shapeType === 'rightEchelon') {
  314. return getRightEchelonShape(barItem, i)
  315. } else {
  316. return getNormalBarShape(barItem, i)
  317. }
  318. }
  319. function getLeftEchelonShape (barItem, i) {
  320. const { barValueAxisPos, barLabelAxisPos, barWidth, echelonOffset } = barItem
  321. const [start, end] = barValueAxisPos[i]
  322. const labelAxisPos = barLabelAxisPos[i]
  323. const halfWidth = barWidth / 2
  324. const valueAxis = barItem.valueAxis.axis
  325. const points = []
  326. if (valueAxis === 'x') {
  327. points[0] = [end, labelAxisPos - halfWidth]
  328. points[1] = [end, labelAxisPos + halfWidth]
  329. points[2] = [start, labelAxisPos + halfWidth]
  330. points[3] = [start + echelonOffset, labelAxisPos - halfWidth]
  331. if (end - start < echelonOffset) points.splice(3, 1)
  332. } else {
  333. points[0] = [labelAxisPos - halfWidth, end]
  334. points[1] = [labelAxisPos + halfWidth, end]
  335. points[2] = [labelAxisPos + halfWidth, start]
  336. points[3] = [labelAxisPos - halfWidth, start - echelonOffset]
  337. if (start - end < echelonOffset) points.splice(3, 1)
  338. }
  339. return { points, close: true }
  340. }
  341. function getRightEchelonShape (barItem, i) {
  342. const { barValueAxisPos, barLabelAxisPos, barWidth, echelonOffset } = barItem
  343. const [start, end] = barValueAxisPos[i]
  344. const labelAxisPos = barLabelAxisPos[i]
  345. const halfWidth = barWidth / 2
  346. const valueAxis = barItem.valueAxis.axis
  347. const points = []
  348. if (valueAxis === 'x') {
  349. points[0] = [end, labelAxisPos + halfWidth]
  350. points[1] = [end, labelAxisPos - halfWidth]
  351. points[2] = [start, labelAxisPos - halfWidth]
  352. points[3] = [start + echelonOffset, labelAxisPos + halfWidth]
  353. if (end - start < echelonOffset) points.splice(2, 1)
  354. } else {
  355. points[0] = [labelAxisPos + halfWidth, end]
  356. points[1] = [labelAxisPos - halfWidth, end]
  357. points[2] = [labelAxisPos - halfWidth, start]
  358. points[3] = [labelAxisPos + halfWidth, start - echelonOffset]
  359. if (start - end < echelonOffset) points.splice(2, 1)
  360. }
  361. return { points, close: true }
  362. }
  363. function getNormalBarShape (barItem, i) {
  364. const { barValueAxisPos, barLabelAxisPos, barWidth } = barItem
  365. const [start, end] = barValueAxisPos[i]
  366. const labelAxisPos = barLabelAxisPos[i]
  367. const valueAxis = barItem.valueAxis.axis
  368. const shape = {}
  369. if (valueAxis === 'x') {
  370. shape.x = start
  371. shape.y = labelAxisPos - barWidth / 2
  372. shape.w = end - start
  373. shape.h = barWidth
  374. } else {
  375. shape.x = labelAxisPos - barWidth / 2
  376. shape.y = end
  377. shape.w = barWidth
  378. shape.h = start - end
  379. }
  380. return shape
  381. }
  382. function getBarStyle (barItem, i) {
  383. const { barStyle, gradient, color, independentColor, independentColors } = barItem
  384. const fillColor = [barStyle.fill || color]
  385. let gradientColor = deepMerge(fillColor, gradient.color)
  386. if (independentColor) {
  387. const idtColor = independentColors[i % independentColors.length]
  388. gradientColor = idtColor instanceof Array ? idtColor : [idtColor]
  389. }
  390. if (gradientColor.length === 1) gradientColor.push(gradientColor[0])
  391. const gradientParams = getGradientParams(barItem, i)
  392. return deepMerge({
  393. gradientColor,
  394. gradientParams,
  395. gradientType: 'linear',
  396. gradientWith: 'fill'
  397. }, barStyle)
  398. }
  399. function getGradientParams (barItem, i) {
  400. const { barValueAxisPos, barLabelAxisPos, data } = barItem
  401. const { linePosition, axis } = barItem.valueAxis
  402. const [start, end] = barValueAxisPos[i]
  403. const labelAxisPos = barLabelAxisPos[i]
  404. const value = data[i]
  405. const [lineStart, lineEnd] = linePosition
  406. const valueAxisIndex = axis === 'x' ? 0 : 1
  407. let endPos = end
  408. if (!barItem.gradient.local) {
  409. endPos = value < 0 ? lineStart[valueAxisIndex] : lineEnd[valueAxisIndex]
  410. }
  411. if (axis === 'y') {
  412. return [labelAxisPos, endPos, labelAxisPos, start]
  413. } else {
  414. return [endPos, labelAxisPos, start, labelAxisPos]
  415. }
  416. }
  417. function getStartBarConfig (barItem) {
  418. const configs = getBarConfig(barItem)
  419. const { shapeType } = barItem
  420. configs.forEach(config => {
  421. let { shape } = config
  422. if (shapeType === 'leftEchelon') {
  423. shape = getStartLeftEchelonShape(shape, barItem)
  424. } else if (shapeType === 'rightEchelon') {
  425. shape = getStartRightEchelonShape(shape, barItem)
  426. } else {
  427. shape = getStartNormalBarShape(shape, barItem)
  428. }
  429. config.shape = shape
  430. })
  431. return configs
  432. }
  433. function getStartLeftEchelonShape (shape, barItem) {
  434. const axis = barItem.valueAxis.axis
  435. shape = deepClone(shape)
  436. const { points } = shape
  437. const index = axis === 'x' ? 0 : 1
  438. const start = points[2][index]
  439. points.forEach(point => (point[index] = start))
  440. return shape
  441. }
  442. function getStartRightEchelonShape (shape, barItem) {
  443. const axis = barItem.valueAxis.axis
  444. shape = deepClone(shape)
  445. const { points } = shape
  446. const index = axis === 'x' ? 0 : 1
  447. const start = points[2][index]
  448. points.forEach(point => (point[index] = start))
  449. return shape
  450. }
  451. function getStartNormalBarShape(shape, barItem) {
  452. const axis = barItem.valueAxis.axis
  453. let { x, y, w, h } = shape
  454. if (axis === 'x') {
  455. w = 0
  456. } else {
  457. y = y + h
  458. h = 0
  459. }
  460. return { x, y, w, h }
  461. }
  462. function beforeUpdateBar (graphs, barItem, i, updater) {
  463. const { render } = updater.chart
  464. const name = getBarName(barItem)
  465. if (graphs[i] && graphs[i][0].name !== name) {
  466. graphs[i].forEach(g => render.delGraph(g))
  467. graphs[i] = null
  468. }
  469. }
  470. function getLabelConfig (barItem) {
  471. let { animationCurve, animationFrame, rLevel } = barItem
  472. const shapes = getLabelShapes(barItem)
  473. const style = getLabelStyle(barItem)
  474. return shapes.map(shape => ({
  475. name: 'text',
  476. index: rLevel,
  477. visible: barItem.label.show,
  478. animationCurve,
  479. animationFrame,
  480. shape,
  481. style
  482. }))
  483. }
  484. function getLabelShapes (barItem) {
  485. const contents = getFormatterLabels (barItem)
  486. const position = getLabelsPosition(barItem)
  487. return position.map((pos, i) => ({
  488. position: pos,
  489. content: contents[i]
  490. }))
  491. }
  492. function getFormatterLabels (barItem) {
  493. let { data, label} = barItem
  494. let { formatter } = label
  495. data = data.filter(d => typeof d === 'number').map(d => d.toString())
  496. if (!formatter) return data
  497. const type = typeof formatter
  498. if (type === 'string') return data.map(d => formatter.replace('{value}', d))
  499. if (type === 'function') return data.map((d, i) => formatter({ value:d, index: i }))
  500. return data
  501. }
  502. function getLabelsPosition (barItem) {
  503. const { label, barValueAxisPos, barLabelAxisPos } = barItem
  504. const { position, offset } = label
  505. const axis = barItem.valueAxis.axis
  506. return barValueAxisPos.map(([start, end], i) => {
  507. const labelAxisPos = barLabelAxisPos[i]
  508. let pos = [end, labelAxisPos]
  509. if (position === 'bottom') {
  510. pos = [start, labelAxisPos]
  511. }
  512. if (position === 'center') {
  513. pos = [(start + end) / 2, labelAxisPos]
  514. }
  515. if (axis === 'y') pos.reverse()
  516. return getOffsetedPoint(pos, offset)
  517. })
  518. }
  519. function getOffsetedPoint ([x, y], [ox, oy]) {
  520. return [x + ox, y + oy]
  521. }
  522. function getLabelStyle (barItem) {
  523. let { color, label: { style }, gradient: { color: gc } } = barItem
  524. if (gc.length) color = gc[0]
  525. style = deepMerge({ fill: color }, style)
  526. return style
  527. }