| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578 |
- import { doUpdate } from '../class/updater.class'
- import { pieConfig } from '../config/pie'
- import { getCircleRadianPoint } from '@jiaminghi/c-render/lib/plugin/util'
- import { deepMerge, initNeedSeries, mulAdd, getPolylineLength } from '../util'
- export function pie (chart, option = {}) {
- let { series } = option
- if (!series) series = []
- let pies = initNeedSeries(series, pieConfig, 'pie')
- pies = calcPiesCenter(pies, chart)
- pies = calcPiesRadius(pies, chart)
- pies = calcRosePiesRadius(pies, chart)
- pies = calcPiesPercent(pies)
- pies = calcPiesAngle(pies, chart)
- pies = calcPiesInsideLabelPos(pies)
- pies = calcPiesEdgeCenterPos(pies)
- pies = calcPiesOutSideLabelPos(pies)
- doUpdate({
- chart,
- series: pies,
- key: 'pie',
- getGraphConfig: getPieConfig,
- getStartGraphConfig: getStartPieConfig,
- beforeChange: beforeChangePie
- })
- doUpdate({
- chart,
- series: pies,
- key: 'pieInsideLabel',
- getGraphConfig: getInsideLabelConfig
- })
- doUpdate({
- chart,
- series: pies,
- key: 'pieOutsideLabelLine',
- getGraphConfig: getOutsideLabelLineConfig,
- getStartGraphConfig: getStartOutsideLabelLineConfig
- })
- doUpdate({
- chart,
- series: pies,
- key: 'pieOutsideLabel',
- getGraphConfig: getOutsideLabelConfig,
- getStartGraphConfig: getStartOutsideLabelConfig
- })
- }
- function calcPiesCenter(pies, chart) {
- const { area } = chart.render
- pies.forEach(pie => {
- let { center } = pie
- center = center.map((pos, i) => {
- if (typeof pos === 'number') return pos
- return parseInt(pos) / 100 * area[i]
- })
- pie.center = center
- })
- return pies
- }
- function calcPiesRadius (pies, chart) {
- const maxRadius = Math.min(...chart.render.area) / 2
- pies.forEach(pie => {
- let { radius, data } = pie
- radius = getNumberRadius(radius, maxRadius)
- data.forEach(item => {
- let { radius: itemRadius } = item
- if (!itemRadius) itemRadius = radius
- itemRadius = getNumberRadius(itemRadius, maxRadius)
- item.radius = itemRadius
- })
- pie.radius = radius
- })
- return pies
- }
- function getNumberRadius (radius, maxRadius) {
- if (!(radius instanceof Array)) radius = [0, radius]
- radius = radius.map(r => {
- if (typeof r === 'number') return r
- return parseInt(r) / 100 * maxRadius
- })
- return radius
- }
- function calcRosePiesRadius (pies, chart) {
- const rosePie = pies.filter(({ roseType }) => roseType)
- rosePie.forEach(pie => {
- let { radius, data, roseSort } = pie
- const roseIncrement = getRoseIncrement(pie)
- const dataCopy = [...data]
- data = sortData(data)
- data.forEach((item, i) => {
- item.radius[1] = radius[1] - roseIncrement * i
- })
- if (roseSort) {
- data.reverse()
- } else {
- pie.data = dataCopy
- }
- pie.roseIncrement = roseIncrement
- })
- return pies
- }
- function sortData (data) {
- return data.sort(({ value: a }, { value: b }) => {
- if (a === b) return 0
- if (a > b) return -1
- if (a < b) return 1
- })
- }
- function getRoseIncrement (pie) {
- const { radius, roseIncrement } = pie
- if (typeof roseIncrement === 'number') return roseIncrement
- if (roseIncrement === 'auto') {
- const { data } = pie
- const allRadius = data.reduce((all, { radius }) => [...all, ...radius], [])
- const minRadius = Math.min(...allRadius)
- const maxRadius = Math.max(...allRadius)
- return (maxRadius - minRadius) * 0.6 / (data.length - 1 || 1)
- }
- return parseInt(roseIncrement) / 100 * radius[1]
- }
- function calcPiesPercent (pies) {
- pies.forEach(pie => {
- const { data, percentToFixed } = pie
- const sum = getDataSum(data)
- data.forEach(item => {
- const { value } = item
- item.percent = value / sum * 100
- item.percentForLabel = toFixedNoCeil(value / sum * 100, percentToFixed)
- })
- const percentSumNoLast = mulAdd(data.slice(0, -1).map(({ percent }) => percent))
- data.slice(-1)[0].percent = 100 - percentSumNoLast
- data.slice(-1)[0].percentForLabel = toFixedNoCeil(100 - percentSumNoLast, percentToFixed)
- })
- return pies
- }
- function toFixedNoCeil (number, toFixed = 0) {
- const stringNumber = number.toString()
- const splitedNumber = stringNumber.split('.')
- const decimal = splitedNumber[1] || '0'
- const fixedDecimal = decimal.slice(0, toFixed)
- splitedNumber[1] = fixedDecimal
- return parseFloat(splitedNumber.join('.'))
- }
- function getDataSum (data) {
- return mulAdd(data.map(({ value }) => value))
- }
- function calcPiesAngle (pies) {
- pies.forEach(pie => {
- const { startAngle: start, data } = pie
- data.forEach((item, i) => {
- const [startAngle, endAngle] = getDataAngle(data, i)
- item.startAngle = start + startAngle
- item.endAngle = start + endAngle
- })
- })
- return pies
- }
- function getDataAngle (data, i) {
- const fullAngle = Math.PI * 2
- const needAddData = data.slice(0, i + 1)
- const percentSum = mulAdd(needAddData.map(({ percent }) => percent))
- const { percent } = data[i]
- const startPercent = percentSum - percent
- return [fullAngle * startPercent / 100, fullAngle * percentSum / 100]
- }
- function calcPiesInsideLabelPos (pies) {
- pies.forEach(pieItem => {
- const { data } = pieItem
- data.forEach(item => {
- item.insideLabelPos = getPieInsideLabelPos(pieItem, item)
- })
- })
- return pies
- }
- function getPieInsideLabelPos (pieItem, dataItem) {
- const { center } = pieItem
- const { startAngle, endAngle, radius: [ir, or] } = dataItem
- const radius = (ir + or) / 2
- const angle = (startAngle + endAngle) / 2
- return getCircleRadianPoint(...center, radius, angle)
- }
- function calcPiesEdgeCenterPos(pies) {
- pies.forEach(pie => {
- const { data, center } = pie
- data.forEach(item => {
- const { startAngle, endAngle, radius } = item
- const centerAngle = (startAngle + endAngle) / 2
- const pos = getCircleRadianPoint(...center, radius[1], centerAngle)
- item.edgeCenterPos = pos
- })
- })
- return pies
- }
- function calcPiesOutSideLabelPos (pies) {
- pies.forEach(pieItem => {
- let leftPieDataItems = getLeftOrRightPieDataItems(pieItem)
- let rightPieDataItems = getLeftOrRightPieDataItems(pieItem, false)
- leftPieDataItems = sortPiesFromTopToBottom(leftPieDataItems)
- rightPieDataItems = sortPiesFromTopToBottom(rightPieDataItems)
- addLabelLineAndAlign(leftPieDataItems, pieItem)
- addLabelLineAndAlign(rightPieDataItems, pieItem, false)
- })
- return pies
- }
- function getLabelLineBendRadius (pieItem) {
- let { labelLineBendGap } = pieItem.outsideLabel
- const maxRadius = getPieMaxRadius(pieItem)
- if (typeof labelLineBendGap !== 'number') {
- labelLineBendGap = parseInt(labelLineBendGap) / 100 * maxRadius
- }
- return labelLineBendGap + maxRadius
- }
- function getPieMaxRadius(pieItem) {
- const { data } = pieItem
- const radius = data.map(({ radius: [foo, r] }) => r)
- return Math.max(...radius)
- }
- function getLeftOrRightPieDataItems (pieItem, left = true) {
- const { data, center } = pieItem
- const centerXPos = center[0]
- return data.filter(({ edgeCenterPos }) => {
- const xPos = edgeCenterPos[0]
- if (left) return xPos <= centerXPos
- return xPos > centerXPos
- })
- }
- function sortPiesFromTopToBottom (dataItem) {
- dataItem.sort(({ edgeCenterPos: [t, ay] }, { edgeCenterPos: [tt, by] }) => {
- if (ay > by) return 1
- if (ay < by) return -1
- if (ay === by) return 0
- })
- return dataItem
- }
- function addLabelLineAndAlign (dataItem, pieItem, left = true) {
- const { center, outsideLabel } = pieItem
- const radius = getLabelLineBendRadius(pieItem)
- dataItem.forEach(item => {
- const { edgeCenterPos, startAngle, endAngle } = item
- const { labelLineEndLength } = outsideLabel
- const angle = (startAngle + endAngle) / 2
- const bendPoint = getCircleRadianPoint(...center, radius, angle)
- let endPoint = [...bendPoint]
- endPoint[0] += labelLineEndLength * (left ? -1 : 1)
- item.labelLine = [edgeCenterPos, bendPoint, endPoint]
- item.labelLineLength = getPolylineLength(item.labelLine)
- item.align = { textAlign: 'left', textBaseline: 'middle' }
- if (left) item.align.textAlign = 'right'
- })
- }
- function getPieConfig (pieItem) {
- const { data, animationCurve, animationFrame, rLevel } = pieItem
- return data.map((foo, i) => ({
- name: 'pie',
- index: rLevel,
- animationCurve,
- animationFrame,
- shape: getPieShape(pieItem, i),
- style: getPieStyle(pieItem, i)
- }))
- }
- function getStartPieConfig (pieItem) {
- const { animationDelayGap, startAnimationCurve } = pieItem
- const configs = getPieConfig(pieItem)
- configs.forEach((config, i) => {
- config.animationCurve = startAnimationCurve
- config.animationDelay = i * animationDelayGap
- config.shape.or = config.shape.ir
- })
- return configs
- }
- function beforeChangePie (graph) {
- graph.animationDelay = 0
- }
- function getPieShape (pieItem, i) {
- const { center, data } = pieItem
- const dataItem = data[i]
- const { radius, startAngle, endAngle } = dataItem
- return {
- startAngle,
- endAngle,
- ir: radius[0],
- or: radius[1],
- rx: center[0],
- ry: center[1]
- }
- }
- function getPieStyle (pieItem, i) {
- const { pieStyle, data } = pieItem
- const dataItem = data[i]
- const { color } = dataItem
- return deepMerge({ fill: color }, pieStyle)
- }
- function getInsideLabelConfig (pieItem) {
- const { animationCurve, animationFrame, data, rLevel } = pieItem
- return data.map((foo, i) => ({
- name: 'text',
- index: rLevel,
- visible: pieItem.insideLabel.show,
- animationCurve,
- animationFrame,
- shape: getInsideLabelShape(pieItem, i),
- style: getInsideLabelStyle(pieItem, i)
- }))
- }
- function getInsideLabelShape (pieItem, i) {
- const { insideLabel, data } = pieItem
- const { formatter } = insideLabel
- const dataItem = data[i]
- const formatterType = typeof formatter
- let label = ''
- if (formatterType === 'string') {
- label = formatter.replace('{name}', dataItem.name)
- label = label.replace('{percent}', dataItem.percentForLabel)
- label = label.replace('{value}', dataItem.value)
- }
- if (formatterType === 'function') {
- label = formatter(dataItem)
- }
- return {
- content: label,
- position: dataItem.insideLabelPos
- }
- }
- function getInsideLabelStyle (pieItem, i) {
- const { insideLabel: { style } } = pieItem
- return style
- }
- function getOutsideLabelLineConfig (pieItem) {
- const { animationCurve, animationFrame, data, rLevel } = pieItem
- return data.map((foo, i) => ({
- name: 'polyline',
- index: rLevel,
- visible: pieItem.outsideLabel.show,
- animationCurve,
- animationFrame,
- shape: getOutsideLabelLineShape(pieItem, i),
- style: getOutsideLabelLineStyle(pieItem, i)
- }))
- }
- function getStartOutsideLabelLineConfig (pieItem) {
- const { data } = pieItem
- const configs = getOutsideLabelLineConfig(pieItem)
- configs.forEach((config, i) => {
- config.style.lineDash = [0, data[i].labelLineLength]
- })
- return configs
- }
- function getOutsideLabelLineShape (pieItem, i) {
- const { data } = pieItem
- const dataItem = data[i]
- return {
- points: dataItem.labelLine
- }
- }
- function getOutsideLabelLineStyle (pieItem, i) {
- const { outsideLabel, data } = pieItem
- const { labelLineStyle } = outsideLabel
- const { color } = data[i]
- return deepMerge({ stroke: color, lineDash: [data[i].labelLineLength, 0] }, labelLineStyle)
- }
- function getOutsideLabelConfig (pieItem) {
- const { animationCurve, animationFrame, data, rLevel } = pieItem
- return data.map((foo, i) => ({
- name: 'text',
- index: rLevel,
- visible: pieItem.outsideLabel.show,
- animationCurve,
- animationFrame,
- shape: getOutsideLabelShape(pieItem, i),
- style: getOutsideLabelStyle(pieItem, i)
- }))
- }
- function getStartOutsideLabelConfig (pieItem) {
- const { data } = pieItem
- const configs = getOutsideLabelConfig(pieItem)
- configs.forEach((config, i) => {
- config.shape.position = data[i].labelLine[1]
- })
- return configs
- }
- function getOutsideLabelShape (pieItem, i) {
- const { outsideLabel, data } = pieItem
- const { formatter } = outsideLabel
- const { labelLine, name, percentForLabel, value } = data[i]
- const formatterType = typeof formatter
- let label = ''
- if (formatterType === 'string') {
- label = formatter.replace('{name}', name)
- label = label.replace('{percent}', percentForLabel)
- label = label.replace('{value}', value)
- }
- if (formatterType === 'function') {
- label = formatter(data[i])
- }
- return {
- content: label,
- position: labelLine[2],
- }
- }
- function getOutsideLabelStyle (pieItem, i) {
- const { outsideLabel, data } = pieItem
- const { color, align } = data[i]
- const { style } = outsideLabel
- return deepMerge({ fill: color, ...align }, style)
- }
|