利用echarts的pie类型绘制仪表盘

1,969 阅读7分钟

画仪表盘的需要用到echarts的画饼图(type:pie)的功能和一些技巧:

(1)series设置data,data是一段均匀的数字

(2)series设置radius,设置表盘(环形)的宽度或者厚度

(3)serias设置label,formatter属性设置文字的内容(刻度),position设置文字位置。echarts的默认outside或inside都丑,可以使用另一组_data_数据,用于专门展示文字,而且默认文字是居中的,不好改,需要用一些技巧帮助展示到边缘。

(4)serias设置itemStyle的color属性设置表盘背景色,可以设置透明色或者渐变色哦

(5)可以通过对每项(表盘的每个子单元)设置样式,包括backgroundColor,borderColor,borderSize,shadowColor等

案例1

const TotalShowBlocks = 40 //默认展示的块数40,始终表示不变
const LevelTexts = ['0', '差', '25', '中', '50', '良', '75', '优', '100']//用于展示的刻度
const ShowPercent = .75 //用于展示的仪表盘角度的百分比,剩下的是透明的
const TotalBlocks = Math.floor(40/ ShowPercent)
const StartAngle = -180 + 90 - (360*(1-ShowPercent) / 2)
const KeduLength=5 //每5个显示一个刻度
const Value = 55//表示目标数值
const Kedu = 100/TotalShowBlocks

function rgbToHex(r, g, b) {
    var hex = ((r << 16) | (g << 8) | b).toString(16);
    return "#" + new Array(Math.abs(hex.length - 7)).join("0") + hex;
}

// hex to rgb
function hexToRgb(hex) {
    var rgb = [];
    for (var i = 1; i < 7; i += 2) {
        rgb.push(parseInt("0x" + hex.slice(i, i + 2)));
    }
    return rgb;
}

// 计算渐变过渡色 startColor:初始色号,endColor最终色号,step渐变步数
function gradient(startColor, endColor, step) {
    // 将 hex 转换为rgb
    var sColor = hexToRgb(startColor),
        eColor = hexToRgb(endColor);

    // 计算R\G\B每一步的差值
    var rStep = (eColor[0] - sColor[0]) / step;
    gStep = (eColor[1] - sColor[1]) / step;
    bStep = (eColor[2] - sColor[2]) / step;

    var gradientColorArr = [];
    for (var i = 0; i < step; i++) {
        // 计算每一步的hex值
        gradientColorArr.push(rgbToHex(parseInt(rStep * i + sColor[0]), parseInt(gStep * i + sColor[1]), parseInt(bStep * i + sColor[2])));
    }
    return gradientColorArr;
}

let grades = []
let grades_txt=[]
const objValue = Math.ceil(Value /Kedu )//55表示目标数值,2.5是仪表盘上每个单位代表的数值
const colorArr = gradient('#5081FF',"#3DFEFF",TotalBlocks)

for(let i=0;i<TotalBlocks;i++){//取40作为需要设置的块数
    let item = {
         name:i === objValue?'55.5%':'',
         value:1,
    }
    if(i===objValue){//目标位置,需要被选中的
        // item.selected = true  选中后默认有位移
        item.label={
            borderColor:colorArr[objValue],
            borderWidth:8,
            width:40,
            height:40,
            borderRadius:20,
            color:'transparent',
            backgroundColor:'#fff'
        }
    }
    grades.push(item)
    grades_txt.push({
        name:'',
        value:1
    })
}

option = {
    grid:{
      top:40,  
    },
    series: [//用于画一个圆形的线,修饰用
        {
            name:'',
            type:'pie',
            radius:['64%','65%'],
            data:[//长度为1一位数组,画单色线
            {   value:1,
                name:'55%',label:{
                color:'#000',
                fontSize:60
            }}],
            itemStyle:{
                color:'#dedede',//线的颜色
            },
            label:{
                position:'center',//内容展示在圆形正中间
            }
        },
        {
          name:'',//刻度文字
          type:'pie',
          radius: ['65%', '82%'],
          data:grades_txt.concat([1/1000]), //越小越好,更接近起点
          startAngle:StartAngle,
          label:{
              position:'inside',
              formatter:(props={value})=>{
                    const {value,dataIndex}=props
                    // console.log(props,dataIndex)
                    const labels = ['0','差','25','中','50','良','75','优','100']
                    //5
                    console.log(dataIndex)
                    if((dataIndex+1) % KeduLength===0 && dataIndex<TotalShowBlocks){
                        
                        return labels[(dataIndex+1)/KeduLength]
                    }
                    
                    else if(dataIndex===TotalBlocks){
                        return '0'
                    }
                    return null
                },
                color:'#AEAEAE',
                fontSize:10
          },
          itemStyle:{
              color:'transparent',
          }
          
        },
        {
            name: '',
            type: 'pie',
            radius: ['85%', '100%'],
            startAngle:StartAngle,
            itemStyle:{
                shadowColor: 'rgba(255, 255, 255, 1)',
                shadowBlur: 2,
                borderWidth:1,
                color:(props)=>{
                  const  {seriesIndex, dataIndex, data, value}=props
                    if(dataIndex>=TotalShowBlocks){
                        return 'transparent'
                    }else {
                        return colorArr[dataIndex]
                    }
                },
            },
            selectedMode:'single',
            label: {
                position:'inner',
                backgroundColor: 'transparent',
                rich: {
                    a: {
                        color: 'transparent',
                        lineHeight: 22,
                        align: 'center'
                    },
                }
            },
            data:grades
        }
    ]
}

展示效果如下: evernotecid://E6FB590A-664F-47BB-8E48-D99D9C95AA62/appyinxiangcom/29499089/ENResource/p39

案例2

const Length = 40 //默认分割40份(显示的扇形部分)
const showPercent = 0.8
const props = {
    max:600,
    value:100,
    low:80,
    high:300
}
const radius = 600//直径
const c1 = '#FF630D'
const c2 = '#FFBA00'
const c3 = '#A0DB1B'
const c_blue = '#77BCF4'//画圈的颜色
const FontSize = 10


function rgbToHex(r, g, b) {
    var hex = ((r << 16) | (g << 8) | b).toString(16);
    return "#" + new Array(Math.abs(hex.length - 7)).join("0") + hex;
}

// hex to rgb
function hexToRgb(hex) {
    var rgb = [];
    for (var i = 1; i < 7; i += 2) {
        rgb.push(parseInt("0x" + hex.slice(i, i + 2)));
    }
    return rgb;
}

// 计算渐变过渡色 startColor:初始色号,endColor最终色号,step渐变步数
function gradient(startColor, endColor, step) {
    // 将 hex 转换为rgb
    var sColor = hexToRgb(startColor),
        eColor = hexToRgb(endColor);

    // 计算R\G\B每一步的差值
    var rStep = (eColor[0] - sColor[0]) / step;
    gStep = (eColor[1] - sColor[1]) / step;
    bStep = (eColor[2] - sColor[2]) / step;

    var gradientColorArr = [];
    for (var i = 0; i < step; i++) {
        // 计算每一步的hex值
        gradientColorArr.push(rgbToHex(parseInt(rStep * i + sColor[0]), parseInt(gStep * i + sColor[1]), parseInt(bStep * i + sColor[2])));
    }
    return gradientColorArr;
}
    
function formatData() {
        const { max, value } = props
        if (value > max) {
            console.error('超出最大值,请检查是否出错')
        }
        const MaxNum = max * (1 + 30 / 600)
        const CriticalValue = max
        const showNum = max
        const TotalNum = max / showPercent
        const hideNum = TotalNum - showNum
        const unit = showNum / 600
        const kedu = showNum / Length //默认 60对应4格,则一个刻度=15
        const startAngle = -180 + 90 - ((360 * (1 - showPercent) / 2))

        return { MaxNum, CriticalValue, showNum, hideNum, TotalNum, unit, kedu, startAngle }
    }
    
   function  getColors() {
        const { low, high } = props
        const { showNum, unit, kedu } = formatData()// metricData
        const p1 = Math.floor(low / kedu)
        const p2 = Math.floor(high / kedu)
        if (p1 < Length) {
            if (p2 < Length) {
                let ss = gradient(c1, c2, p1)
                    .concat(gradient(c2, c3, Math.floor((p2 - p1) / 2)))
                    .concat(gradient(c3, c2, Math.ceil((p2 - p1) / 2)))
                    .concat(gradient(c2, c1, (Length - p2 + 1)))
                console.log('color length', ss.length)
                return ss
            } else {
                console.error('较慢值已经超出限定值' + Length)
            }
        } else {
            console.error('较快值已经超出限定值' + Length)
        }
        return []
    }
   function getData() {
        const { low, high } = props
        const { showNum, unit, hideNum } = formatData()//metricData
        const p1 = low 
        const p2 = high
        return [
            { value: p1, name: '较快' },
            { value: p2 - p1, name: '适中' },
            { value: showNum - p2, name: '较慢' },
            { value: hideNum, name: '' }
        ]
    }
    
function getCircleSelectedLabel() {
        const per = 0.06
        return {
            borderColor: c_blue,
            borderWidth: radius * per / 4,
            width: radius * per,
            height: radius * per,
            borderRadius: radius * per / 2,
            color: 'transparent',
            backgroundColor: '#fff',
            position: 'inner'
        }
    }
   function  getGradesText() {
        let grades_txt = []
        const { showNum, TotalNum } = formatData()
        for (let i = 0; i < Length * (TotalNum / showNum); i++) {
            const item = {
                value: 1,
                name: ''
            }
            grades_txt.push(item)
        }
        // console.log('grades_txt length', grades_txt.length)
        return grades_txt
    }
    
 function  getOption() {
        const { max,value } = props
        const { MaxNum, showNum, TotalNum, hideNum, kedu, unit, startAngle } = formatData(props)
        let objValue = Math.floor(value / unit)
        objValue = objValue > showNum ? MaxNum : objValue
        const colorArr = getColors()
        const data___ = getGradesText()
        return {
            series: [
                {
                    name:'',
                    type:'pie',
                    radius:['64%','65%'],
                    data:[//长度为1一位数组,画单色线
                    {   value:1,
                        name:props.value,label:{
                        color:'#000',
                        fontSize:60
                    }}],
                    itemStyle:{
                        color:'transparent',//线的颜色
                    },
                    label:{
                        position:'center',//内容展示在圆形正中间
                    }
                },
                {
                    name: '',//文字较快-适中-较慢
                    type: 'pie',
                    radius: ['42%', '62%'],
                    startAngle: startAngle,
                    data: getData(),
                    label: {
                        position: 'inside',
                        color: '#AEAEAE',
                        fontSize: FontSize,
                    },
                    itemStyle: {
                        color: 'transparent',
                    },
                },
                {//画圆圈⭕️ 背景 蓝灰
                    name: '',
                    type: 'pie',
                    radius: ['62%', '68%'],
                    startAngle: startAngle,
                    itemStyle: {
                        color: (props) => {
                            const { dataIndex } = props
                            if (value <= showNum) {
                                if (dataIndex === 0) {
                                    return c_blue
                                }
                                if (dataIndex === 1) return '#e3e3e3'
                                else return 'transparent'
                            } else {
                                if (dataIndex === 0 || dataIndex === 1) {
                                    return c_blue
                                }
                                else return 'transparent'
                            }
                        },
                    },
                    label: {
                        position: 'inner',
                        rich: {}
                    },
                    data: value <= showNum ?
                        [value, showNum - value, hideNum] :
                        [showNum, MaxNum - showNum, TotalNum - MaxNum]
                },

                {//仅仅是⭕️
                    name: '',//蓝色,灰色部分的环形
                    type: 'pie',
                    radius: ['62%', '68%'],
                    startAngle: startAngle,
                    label: {
                        position: 'inside',
                        rich: {},
                    },
                    itemStyle: {
                        color: (props) => {
                            const { dataIndex } = props
                            return 'transparent'
                        },
                    },
                    data: TotalNum - value * 2 >= 0 ? [
                        { value: value * 2, name: '1', label: getCircleSelectedLabel() },
                        { value: TotalNum - value * 2 }]
                        :
                        [
                            { value: TotalNum / 2 },
                            { value: ((value > showNum ? MaxNum : value) - TotalNum / 2) * 2, name: '1', label: getCircleSelectedLabel() },
                            { value: TotalNum - TotalNum / 2 - ((value > showNum ? MaxNum : value) - TotalNum / 2) * 2 }
                        ]
                },
                {//⭕️下面的指示线
                    name: '',//蓝色,灰色部分的环形
                    type: 'pie',
                    radius: ['52%', '70%'],
                    startAngle: startAngle,
                    markPoint: {
                        symbol: 'circle'
                    },
                    label: {
                        position: 'inside',
                        rich: {},
                    },
                    labelLine: {
                        show: true,
                        length: 20,
                    },
                    itemStyle: {
                        color: (props) => {
                            const { dataIndex } = props
                            if (value <= showNum) {
                                if (dataIndex === 1) return c_blue
                            } else {
                                if (dataIndex === 1) {
                                    return c_blue
                                }
                            }
                            return 'transparent'
                        },
                    },
                    data: value <= showNum ? [value, unit * 3, TotalNum - value - unit * 3]
                        :
                        [MaxNum, unit * 3, TotalNum - MaxNum - unit * 3]
                },
                {
                    name: '',//文字
                    type: 'pie',
                    radius: ['67%', '87%'],//20%
                    data: data___.concat([1 / MaxNum]), // 为了将最后一项标记为A
                    startAngle: startAngle,
                    label: {
                        position: 'inside',
                        formatter: (props = { value }) => {
                            const { value, dataIndex } = props
                            // const num = (dataIndex + 1) * kedu
                            const num = (dataIndex + 1) * kedu
                            const OneKedu = kedu * 4 //用于显示文字
                           
                            if ((dataIndex + 1) % 4 === 0 && num <= showNum) {
                                return Math.floor(num * 100) / 100
                            }
                            if (dataIndex === data___.length) return '0'

                            if (max > showNum || props.value > props.max) {
                                if (MaxNum === num) {
                                    return '其他'
                                }
                            }
                            return null
                        },
                        color: '#AEAEAE',
                        fontSize: FontSize
                    },
                    itemStyle: {
                        color: 'transparent',
                    }
                },
                {
                    name: '',
                    type: 'pie',
                    radius: ['87%', '97%'],
                    startAngle: startAngle,
                    itemStyle: {
                        shadowColor: 'rgba(255, 255, 255, 1)',
                        shadowBlur: 2,
                        borderWidth: 1,
                        color: (props) => {
                            const { dataIndex } = props
                            const num = dataIndex * kedu
                            const setGridentColor = (dataIndex_) => {
                                if (colorArr[dataIndex_] && colorArr[dataIndex_ + 1])
                                    return new echarts.graphic.LinearGradient(1, 0, 0, 0, [{ //颜色渐变函数 前四个参数分别表示四个位置依次为左、下、右、上
                                        offset: 0,
                                        color: colorArr[dataIndex_]
                                    }, {
                                        offset: 1,
                                        color: colorArr[dataIndex_ + 1]
                                    }])
                                return null
                            }
                            if (num < showNum) {
                                console.log(`dataIndex:${dataIndex}---num:${num},showNum:${showNum},MaxNum${MaxNum}`)
                                // return colorArr[dataIndex] ? colorArr[dataIndex] : 'transparent'
                                return setGridentColor(dataIndex) || 'transparent'
                            }
                            if (max > showNum || props.value > props.max) {
                                console.log(`dataIndex:${dataIndex}---num:${num},showNum:${showNum},MaxNum${MaxNum}`)
                                if (num < MaxNum) {
                                    return colorArr[colorArr.length - 1]
                                }
                            }
                            return 'transparent'
                            // return colorArr[dataIndex] ? colorArr[dataIndex] : 'transparent'
                        },
                    },
                    label: {
                        position: 'inner',
                        backgroundColor: '',
                        rich: {}
                    },
                    data: data___
                }
            ]
        };
    }
option =   getOption()

展示效果如下:

evernotecid://E6FB590A-664F-47BB-8E48-D99D9C95AA62/appyinxiangcom/29499089/ENResource/p40

(1)该案例有用到渐变方法,如下:

new echarts.graphic.LinearGradient(1, 0, 0, 0, [{ //颜色渐变函数 前四个参数分别表示四个位置依次为左、下、右、上
                                        offset: 0,
                                        color: colorArr[dataIndex_]
                                    }, {
                                        offset: 1,
                                        color: colorArr[dataIndex_ + 1]
                                    }])