8ac0349b3823cfa47b3aa86c037e7cdf49d7bf6aa250ef140c05d05290fc3af6bc036ad358e7c95893d9897a241865e458b4a08019e379ed1cf316b88bc53b 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. import { doUpdate } from '../class/updater.class'
  2. import { gaugeConfig } from '../config/gauge'
  3. import { getCircleRadianPoint } from '@jiaminghi/c-render/lib/plugin/util'
  4. import { deepMerge, initNeedSeries, radianToAngle } from '../util'
  5. import { getRgbaValue } from '@jiaminghi/color'
  6. export function gauge (chart, option = {}) {
  7. let { series } = option
  8. if (!series) series = []
  9. let gauges = initNeedSeries(series, gaugeConfig, 'gauge')
  10. gauges = calcGaugesCenter(gauges, chart)
  11. gauges = calcGaugesRadius(gauges, chart)
  12. gauges = calcGaugesDataRadiusAndLineWidth(gauges, chart)
  13. gauges = calcGaugesDataAngles(gauges, chart)
  14. gauges = calcGaugesDataGradient(gauges, chart)
  15. gauges = calcGaugesAxisTickPosition(gauges, chart)
  16. gauges = calcGaugesLabelPositionAndAlign(gauges, chart)
  17. gauges = calcGaugesLabelData(gauges, chart)
  18. gauges = calcGaugesDetailsPosition(gauges, chart)
  19. gauges = calcGaugesDetailsContent(gauges, chart)
  20. doUpdate({
  21. chart,
  22. series: gauges,
  23. key: 'gaugeAxisTick',
  24. getGraphConfig: getAxisTickConfig
  25. })
  26. doUpdate({
  27. chart,
  28. series: gauges,
  29. key: 'gaugeAxisLabel',
  30. getGraphConfig: getAxisLabelConfig
  31. })
  32. doUpdate({
  33. chart,
  34. series: gauges,
  35. key: 'gaugeBackgroundArc',
  36. getGraphConfig: getBackgroundArcConfig,
  37. getStartGraphConfig: getStartBackgroundArcConfig
  38. })
  39. doUpdate({
  40. chart,
  41. series: gauges,
  42. key: 'gaugeArc',
  43. getGraphConfig: getArcConfig,
  44. getStartGraphConfig: getStartArcConfig,
  45. beforeChange: beforeChangeArc
  46. })
  47. doUpdate({
  48. chart,
  49. series: gauges,
  50. key: 'gaugePointer',
  51. getGraphConfig: getPointerConfig,
  52. getStartGraphConfig: getStartPointerConfig
  53. })
  54. doUpdate({
  55. chart,
  56. series: gauges,
  57. key: 'gaugeDetails',
  58. getGraphConfig: getDetailsConfig
  59. })
  60. }
  61. function calcGaugesCenter (gauges, chart) {
  62. const { area } = chart.render
  63. gauges.forEach(gaugeItem => {
  64. let { center } = gaugeItem
  65. center = center.map((pos, i) => {
  66. if (typeof pos === 'number') return pos
  67. return parseInt(pos) / 100 * area[i]
  68. })
  69. gaugeItem.center = center
  70. })
  71. return gauges
  72. }
  73. function calcGaugesRadius (gauges, chart) {
  74. const { area } = chart.render
  75. const maxRadius = Math.min(...area) / 2
  76. gauges.forEach(gaugeItem => {
  77. let { radius } = gaugeItem
  78. if (typeof radius !== 'number') {
  79. radius = parseInt(radius) / 100 * maxRadius
  80. }
  81. gaugeItem.radius = radius
  82. })
  83. return gauges
  84. }
  85. function calcGaugesDataRadiusAndLineWidth (gauges, chart) {
  86. const { area } = chart.render
  87. const maxRadius = Math.min(...area) / 2
  88. gauges.forEach(gaugeItem => {
  89. const { radius, data, arcLineWidth } = gaugeItem
  90. data.forEach(item => {
  91. let { radius: arcRadius, lineWidth } = item
  92. if (!arcRadius) arcRadius = radius
  93. if (typeof arcRadius !== 'number') arcRadius = parseInt(arcRadius) / 100 * maxRadius
  94. item.radius = arcRadius
  95. if (!lineWidth) lineWidth = arcLineWidth
  96. item.lineWidth = lineWidth
  97. })
  98. })
  99. return gauges
  100. }
  101. function calcGaugesDataAngles (gauges, chart) {
  102. gauges.forEach(gaugeItem => {
  103. const { startAngle, endAngle, data, min, max } = gaugeItem
  104. const angleMinus = endAngle - startAngle
  105. const valueMinus = max - min
  106. data.forEach(item => {
  107. const { value } = item
  108. const itemAngle = Math.abs((value - min) / valueMinus * angleMinus)
  109. item.startAngle = startAngle
  110. item.endAngle = startAngle + itemAngle
  111. })
  112. })
  113. return gauges
  114. }
  115. function calcGaugesDataGradient (gauges, chart) {
  116. gauges.forEach(gaugeItem => {
  117. const { data } = gaugeItem
  118. data.forEach(item => {
  119. let { color, gradient } = item
  120. if (!gradient || !gradient.length) gradient = color
  121. if (!(gradient instanceof Array)) gradient = [gradient]
  122. item.gradient = gradient
  123. })
  124. })
  125. return gauges
  126. }
  127. function calcGaugesAxisTickPosition (gauges, chart) {
  128. gauges.forEach(gaugeItem => {
  129. const { startAngle, endAngle, splitNum, center, radius, arcLineWidth, axisTick } = gaugeItem
  130. const { tickLength, style: { lineWidth } } = axisTick
  131. const angles = endAngle - startAngle
  132. const outerRadius = radius - (arcLineWidth / 2)
  133. const innerRadius = outerRadius - tickLength
  134. const angleGap = angles / (splitNum - 1)
  135. const arcLength = 2 * Math.PI * radius * angles / (Math.PI * 2)
  136. const offset = Math.ceil(lineWidth / 2) / arcLength * angles
  137. gaugeItem.tickAngles = []
  138. gaugeItem.tickInnerRadius = []
  139. gaugeItem.tickPosition = new Array(splitNum).fill(0)
  140. .map((foo, i) => {
  141. let angle = startAngle + angleGap * i
  142. if (i === 0) angle += offset
  143. if (i === splitNum - 1) angle -= offset
  144. gaugeItem.tickAngles[i] = angle
  145. gaugeItem.tickInnerRadius[i] = innerRadius
  146. return [
  147. getCircleRadianPoint(...center, outerRadius, angle),
  148. getCircleRadianPoint(...center, innerRadius, angle)
  149. ]
  150. })
  151. })
  152. return gauges
  153. }
  154. function calcGaugesLabelPositionAndAlign (gauges, chart) {
  155. gauges.forEach(gaugeItem => {
  156. const { center, tickInnerRadius, tickAngles, axisLabel: { labelGap } } = gaugeItem
  157. const position = tickAngles.map((angle, i) => getCircleRadianPoint(
  158. ...center,
  159. tickInnerRadius[i] - labelGap,
  160. tickAngles[i]
  161. ))
  162. const align = position.map(([x, y]) => ({
  163. textAlign: x > center[0] ? 'right' : 'left',
  164. textBaseline: y > center[1] ? 'bottom' : 'top'
  165. }))
  166. gaugeItem.labelPosition = position
  167. gaugeItem.labelAlign = align
  168. })
  169. return gauges
  170. }
  171. function calcGaugesLabelData (gauges, chart) {
  172. gauges.forEach(gaugeItem => {
  173. const { axisLabel, min, max, splitNum } = gaugeItem
  174. let { data, formatter } = axisLabel
  175. const valueGap = (max - min) / (splitNum - 1)
  176. const value = new Array(splitNum).fill(0).map((foo, i) => parseInt(min + valueGap * i))
  177. const formatterType = typeof formatter
  178. data = deepMerge(value, data).map((v, i) => {
  179. let label = v
  180. if (formatterType === 'string') {
  181. label = formatter.replace('{value}', v)
  182. }
  183. if (formatterType === 'function') {
  184. label = formatter({ value: v, index: i })
  185. }
  186. return label
  187. })
  188. axisLabel.data = data
  189. })
  190. return gauges
  191. }
  192. function calcGaugesDetailsPosition (gauges, chart) {
  193. gauges.forEach(gaugeItem => {
  194. const { data, details, center } = gaugeItem
  195. const { position, offset } = details
  196. const detailsPosition = data.map(({ startAngle, endAngle, radius }) => {
  197. let point = null
  198. if (position === 'center') {
  199. point = center
  200. } else if (position === 'start') {
  201. point = getCircleRadianPoint(...center, radius, startAngle)
  202. } else if (position === 'end') {
  203. point = getCircleRadianPoint(...center, radius, endAngle)
  204. }
  205. return getOffsetedPoint(point, offset)
  206. })
  207. gaugeItem.detailsPosition = detailsPosition
  208. })
  209. return gauges
  210. }
  211. function calcGaugesDetailsContent (gauges, chart) {
  212. gauges.forEach(gaugeItem => {
  213. const { data, details } = gaugeItem
  214. const { formatter } = details
  215. const formatterType = typeof formatter
  216. const contents = data.map(dataItem => {
  217. let content = dataItem.value
  218. if (formatterType === 'string') {
  219. content = formatter.replace('{value}', '{nt}')
  220. content = content.replace('{name}', dataItem.name)
  221. }
  222. if (formatterType === 'function') content = formatter(dataItem)
  223. return content.toString()
  224. })
  225. gaugeItem.detailsContent = contents
  226. })
  227. return gauges
  228. }
  229. function getOffsetedPoint ([x, y], [ox, oy]) {
  230. return [x + ox, y + oy]
  231. }
  232. function getAxisTickConfig (gaugeItem) {
  233. const { tickPosition, animationCurve, animationFrame, rLevel } = gaugeItem
  234. return tickPosition.map((foo, i) => ({
  235. name: 'polyline',
  236. index: rLevel,
  237. visible: gaugeItem.axisTick.show,
  238. animationCurve,
  239. animationFrame,
  240. shape: getAxisTickShape(gaugeItem, i),
  241. style: getAxisTickStyle(gaugeItem, i)
  242. }))
  243. }
  244. function getAxisTickShape (gaugeItem, i) {
  245. const { tickPosition } = gaugeItem
  246. return { points: tickPosition[i] }
  247. }
  248. function getAxisTickStyle (gaugeItem, i) {
  249. const { axisTick: { style } } = gaugeItem
  250. return style
  251. }
  252. function getAxisLabelConfig (gaugeItem) {
  253. const { labelPosition, animationCurve, animationFrame, rLevel } = gaugeItem
  254. return labelPosition.map((foo, i) => ({
  255. name: 'text',
  256. index: rLevel,
  257. visible: gaugeItem.axisLabel.show,
  258. animationCurve,
  259. animationFrame,
  260. shape: getAxisLabelShape(gaugeItem, i),
  261. style: getAxisLabelStyle(gaugeItem, i)
  262. }))
  263. }
  264. function getAxisLabelShape (gaugeItem, i) {
  265. const { labelPosition, axisLabel: { data } } = gaugeItem
  266. return {
  267. content: data[i].toString(),
  268. position: labelPosition[i]
  269. }
  270. }
  271. function getAxisLabelStyle (gaugeItem, i) {
  272. const { labelAlign, axisLabel } = gaugeItem
  273. const { style } = axisLabel
  274. return deepMerge({ ...labelAlign[i] }, style)
  275. }
  276. function getBackgroundArcConfig (gaugeItem) {
  277. const { animationCurve, animationFrame, rLevel } = gaugeItem
  278. return [{
  279. name: 'arc',
  280. index: rLevel,
  281. visible: gaugeItem.backgroundArc.show,
  282. animationCurve,
  283. animationFrame,
  284. shape: getGaugeBackgroundArcShape(gaugeItem),
  285. style: getGaugeBackgroundArcStyle(gaugeItem)
  286. }]
  287. }
  288. function getGaugeBackgroundArcShape (gaugeItem) {
  289. let { startAngle, endAngle, center, radius } = gaugeItem
  290. return {
  291. rx: center[0],
  292. ry: center[1],
  293. r: radius,
  294. startAngle,
  295. endAngle
  296. }
  297. }
  298. function getGaugeBackgroundArcStyle (gaugeItem) {
  299. const { backgroundArc, arcLineWidth } = gaugeItem
  300. const { style } = backgroundArc
  301. return deepMerge({ lineWidth: arcLineWidth }, style)
  302. }
  303. function getStartBackgroundArcConfig (gaugeItem) {
  304. const config = getBackgroundArcConfig(gaugeItem)[0]
  305. const shape = { ...config.shape }
  306. shape.endAngle = config.shape.startAngle
  307. config.shape = shape
  308. return [config]
  309. }
  310. function getArcConfig (gaugeItem) {
  311. const { data, animationCurve, animationFrame, rLevel } = gaugeItem
  312. return data.map((foo, i) => ({
  313. name: 'agArc',
  314. index: rLevel,
  315. animationCurve,
  316. animationFrame,
  317. shape: getGaugeArcShape(gaugeItem, i),
  318. style: getGaugeArcStyle(gaugeItem, i)
  319. }))
  320. }
  321. function getGaugeArcShape (gaugeItem, i) {
  322. let { data, center, endAngle: gradientEndAngle } = gaugeItem
  323. const { radius, startAngle, endAngle, localGradient } = data[i]
  324. if (localGradient) gradientEndAngle = endAngle
  325. return {
  326. rx: center[0],
  327. ry: center[1],
  328. r: radius,
  329. startAngle,
  330. endAngle,
  331. gradientEndAngle
  332. }
  333. }
  334. function getGaugeArcStyle (gaugeItem, i) {
  335. const { data, dataItemStyle } = gaugeItem
  336. let { lineWidth, gradient } = data[i]
  337. gradient = gradient.map(c => getRgbaValue(c))
  338. return deepMerge({ lineWidth, gradient }, dataItemStyle)
  339. }
  340. function getStartArcConfig (gaugeItem) {
  341. const configs = getArcConfig(gaugeItem)
  342. configs.map(config => {
  343. const shape = { ...config.shape }
  344. shape.endAngle = config.shape.startAngle
  345. config.shape = shape
  346. })
  347. return configs
  348. }
  349. function beforeChangeArc (graph, config) {
  350. const graphGradient = graph.style.gradient
  351. const cacheNum = graphGradient.length
  352. const needNum = config.style.gradient.length
  353. if (cacheNum > needNum) {
  354. graphGradient.splice(needNum)
  355. } else {
  356. const last = graphGradient.slice(-1)[0]
  357. graphGradient.push(...new Array(needNum - cacheNum).fill(0).map(foo => [...last]))
  358. }
  359. }
  360. function getPointerConfig (gaugeItem) {
  361. const { animationCurve, animationFrame, center, rLevel } = gaugeItem
  362. return [{
  363. name: 'polyline',
  364. index: rLevel,
  365. visible: gaugeItem.pointer.show,
  366. animationCurve,
  367. animationFrame,
  368. shape: getPointerShape(gaugeItem),
  369. style: getPointerStyle(gaugeItem),
  370. setGraphCenter (foo, graph) { graph.style.graphCenter = center }
  371. }]
  372. }
  373. function getPointerShape (gaugeItem) {
  374. const { center } = gaugeItem
  375. return {
  376. points: getPointerPoints(center),
  377. close: true
  378. }
  379. }
  380. function getPointerStyle (gaugeItem) {
  381. const { startAngle, endAngle, min, max, data, pointer, center } = gaugeItem
  382. const { valueIndex, style } = pointer
  383. let value = data[valueIndex] ? data[valueIndex].value : 0
  384. const angle = (value - min) / (max - min) * (endAngle - startAngle) + startAngle + Math.PI / 2
  385. return deepMerge({ rotate: radianToAngle(angle), scale: [1, 1], graphCenter: center }, style)
  386. }
  387. function getPointerPoints ([x, y]) {
  388. const point1 = [x, y - 40]
  389. const point2 = [x + 5, y]
  390. const point3 = [x, y + 10]
  391. const point4 = [x - 5, y]
  392. return [point1, point2, point3, point4]
  393. }
  394. function getStartPointerConfig (gaugeItem) {
  395. const { startAngle } = gaugeItem
  396. const config = getPointerConfig(gaugeItem)[0]
  397. config.style.rotate = radianToAngle(startAngle + Math.PI / 2)
  398. return [config]
  399. }
  400. function getDetailsConfig (gaugeItem) {
  401. const { detailsPosition, animationCurve, animationFrame, rLevel } = gaugeItem
  402. const visible = gaugeItem.details.show
  403. return detailsPosition.map((foo, i) => ({
  404. name: 'numberText',
  405. index: rLevel,
  406. visible,
  407. animationCurve,
  408. animationFrame,
  409. shape: getDetailsShape(gaugeItem, i),
  410. style: getDetailsStyle(gaugeItem, i)
  411. }))
  412. }
  413. function getDetailsShape (gaugeItem, i) {
  414. const { detailsPosition, detailsContent, data, details } = gaugeItem
  415. const position = detailsPosition[i]
  416. const content = detailsContent[i]
  417. const dataValue = data[i].value
  418. const toFixed = details.valueToFixed
  419. return {
  420. number: [dataValue],
  421. content,
  422. position,
  423. toFixed
  424. }
  425. }
  426. function getDetailsStyle (gaugeItem, i) {
  427. const { details, data } = gaugeItem
  428. const { style } = details
  429. const { color } = data[i]
  430. return deepMerge({ fill: color }, style)
  431. }