vislite
Version:
灵活、快速、简单的数据可视化交互式跨端前端库
147 lines (114 loc) • 4.79 kB
text/typescript
import type PieLayoutType from '../../types/PieLayout'
import type { PieResultType } from '../../types/Pie'
import type PieOptionType from '../../types/PieOption'
import type { default as PieConfigType, nameType, valueType } from '../../types/PieConfig'
import { initOption } from '../common/option'
import rotate from '../rotate'
import { animation } from "oipage/web/animation/index"
class PieLayout implements PieLayoutType {
readonly name: string = 'PieLayout'
private __config: PieConfigType
constructor(config: PieConfigType = {}) {
this.__config = initOption(config, {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
name: (pieData: any, initPie: Array<any>) => pieData.name,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
value: (pieData: any, initPie: Array<any>) => pieData.value
})
}
private __option = {
cx: 200,
cy: 200,
radius: [50, 100],
duration: 200,
}
setOption(option: PieOptionType) {
initOption(option, this.__option)
return this
}
private __rback: (pie: PieResultType) => void
private __oralPie: any
private __prePie: PieResultType | null
private __hoverIndex = -1 // 表示当前悬浮的序号
use(initPie: Array<any>, hoverIndex: number = -1) {
const pie: PieResultType = {
count: initPie.length,
cx: this.__option.cx,
cy: this.__option.cy,
radius: this.__option.radius,
hoverIndex,
node: []
}
let totalValue = 0
const names = [], values = []
for (let i = 0; i < initPie.length; i++) {
names[i] = (this.__config.name as nameType)(initPie[i], initPie)
values[i] = (this.__config.value as valueType)(initPie[i], initPie)
totalValue += values[i]
}
let beginDeg = Math.PI * -0.5, currenDeg
for (let i = 0; i < initPie.length; i++) {
currenDeg = values[i] / totalValue * Math.PI * 2
const radius = [this.__option.radius[0] * (1 + (this.__option.radius[0] > 0 && hoverIndex === i ? -0.1 : 0)), this.__option.radius[1] * (1 + (hoverIndex === i ? 0.05 : 0))]
const pdeg = beginDeg + currenDeg * 0.5, pradius = Math.max(this.__option.radius[0], this.__option.radius[1])
const p0 = rotate(pie.cx, pie.cy, pdeg, pie.cx + pradius, pie.cy)
const p1 = rotate(pie.cx, pie.cy, pdeg, pie.cx + pradius + 15, pie.cy)
const pflag = p0[0] > pie.cx ? 1 : -1
const p2 = [p1[0] + pflag * 20, p1[1]]
const p3 = [p1[0] + pflag * 25, p1[1]]
pie.node[i] = {
value: values[i],
name: names[i],
beginDeg,
deg: currenDeg,
isHover: hoverIndex === i,
radius,
label: {
line: [p0, p1, p2],
position: p3,
align: pflag === -1 ? "right" : "left"
}
}
beginDeg += currenDeg
}
return pie
}
bind(initPie: Array<any>, renderBack: (pie: PieResultType) => void) {
this.__rback = renderBack
this.__oralPie = initPie
this.__prePie = this.use(this.__oralPie, this.__hoverIndex)
this.__rback(this.__prePie)
return this
}
unbind() {
this.__rback = () => null
this.__oralPie = null
this.__prePie = null
this.__hoverIndex = -1
return this
}
setHover(index: number) {
if (!this.__prePie || this.__hoverIndex === index) return this
this.__hoverIndex = index
this.doUpdate()
return this
}
doUpdate() {
const newPie = this.use(this.__oralPie, this.__hoverIndex)
const cachePie = JSON.parse(JSON.stringify(newPie))
animation((deep) => {
if (this.__prePie) {
for (let i = 0; i < cachePie.count; i++) {
cachePie.node[i].radius[0] = this.__prePie.node[i].radius[0] + (newPie.node[i].radius[0] - this.__prePie.node[i].radius[0]) * deep
cachePie.node[i].radius[1] = this.__prePie.node[i].radius[1] + (newPie.node[i].radius[1] - this.__prePie.node[i].radius[1]) * deep
}
}
this.__rback(cachePie)
}, this.__option.duration, () => {
this.__prePie = newPie
this.__rback(this.__prePie)
})
return this
}
}
export default PieLayout