首页 前端知识 使用Echart绘制3D饼环图、仪表盘、电池图

使用Echart绘制3D饼环图、仪表盘、电池图

2024-06-07 00:06:56 前端知识 前端哥 482 758 我要收藏

3D饼环图

参考博客

https://blog.csdn.net/m0_67266787/article/details/123155878

插件引入
  • echarts.js
  • echarts-gl.min.js
效果图

在这里插入图片描述

代码
/**
 * 绘制3d图
 * @param pieData 总数据
 * @param internalDiameterRatio:透明的空心占比
 * @param distance 视角到主体的距离
 * @param alpha 旋转角度
 * @param pieHeight 立体的高度
 * @param opacity 饼或者环的透明度
 */
const getPie3D = (pieData, internalDiameterRatio, distance, alpha, pieHeight, opacity = 1) => {
    const series = []
    let sumValue = 0
    let startValue = 0
    let endValue = 0
    let legendData = []
    let legendBfb = []
    const k = 1 - internalDiameterRatio
    pieData.sort((a, b) => {
        return b.value - a.value
    })
    // 为每一个饼图数据,生成一个 series-surface 配置
    for (let i = 0; i < pieData.length; i++) {
        sumValue += pieData[i].value
        const seriesItem = {
            name: typeof pieData[i].name === 'undefined' ?
                `series${i}` : pieData[i].name,
            type: 'surface',
            parametric: true,
            wireframe: {
                show: false
            },
            pieData: pieData[i],
            pieStatus: {
                selected: false,
                hovered: false,
                k: k
            },
            center: ['10%', '50%'],
        }
        if (typeof pieData[i].itemStyle !== 'undefined') {
            const itemStyle = {}
            itemStyle.color =
                typeof pieData[i].itemStyle.color !== 'undefined' ?
                pieData[i].itemStyle.color :
                opacity
            itemStyle.opacity =
                typeof pieData[i].itemStyle.opacity !== 'undefined' ?
                pieData[i].itemStyle.opacity :
                opacity
            seriesItem.itemStyle = itemStyle
        }
        series.push(seriesItem)
    }

    // 使用上一次遍历时,计算出的数据和 sumValue,调用 getParametricEquation 函数,
    // 向每个 series-surface 传入不同的参数方程 series-surface.parametricEquation,也就是实现每一个扇形。
    legendData = []
    legendBfb = []
    for (let i = 0; i < series.length; i++) {
        endValue = startValue + series[i].pieData.value
        series[i].pieData.startRatio = startValue / sumValue
        series[i].pieData.endRatio = endValue / sumValue
        series[i].parametricEquation = getParametricEquation(
            series[i].pieData.startRatio,
            series[i].pieData.endRatio,
            false,
            false,
            k,
            series[i].pieData.value
        )
        startValue = endValue
        const bfb = fomatFloat(series[i].pieData.value / sumValue, 4)
        legendData.push({
            name: series[i].name,
            value: bfb
        })
        legendBfb.push({
            name: series[i].name,
            value: bfb
        })
    }
    let legends_left = legendData.splice(0, legendData.length / 2)
    const boxHeight = getHeight3D(series, pieHeight) + 0.005 // 通过pieHeight设定3d饼/环的高度,单位是px
    // 准备待返回的配置项,把准备好的 legendData、series 传入。
    const option = {
        legend: [{
            show: true,
            data: legends_left,
            orient: 'horizontal',
            right: 50,
            top: -5,
            itemHeight: 15,
            itemWidth: 15,
            itemGap: 80,
            textStyle: {
                color: '#fff',
                rich: {
                    value: {
                        fontSize: 22,
                        width: 50,
                        color: "#fff",
                        fontWeight: 800,
                        lineHeight: 30
                    },
                    name: {
                        width: 50,
                        fontSize: 14,
                        color: "#565F68",
                    }
                }
            },
            icon: 'rect',
            formatter: function (param) {
                const item = legendBfb.filter(item => item.name === param)[0]
                const bfs = fomatFloat(item.value * 100, 2) + '%'
                return [
                    `{value|${bfs}}`,
                    `{name|${item.name}}`
                ].join('\n')
            },
        }, {
            show: true,
            data: legendData,
            orient: 'horizontal',
            right: 50,
            top: 55,
            itemHeight: 15,
            itemWidth: 15,
            itemGap: 80,
            textStyle: {
                color: '#fff',
                rich: {
                    value: {
                        fontSize: 22,
                        color: "#fff",
                        fontWeight: 800,
                        lineHeight: 30,
                        width: 50,
                    },
                    name: {
                        fontSize: 14,
                        color: "#565F68",
                        width: 50,
                    }
                }
            },
            icon: 'rect',
            formatter: function (param) {
                const item = legendBfb.filter(item => item.name === param)[0]
                const bfs = fomatFloat(item.value * 100, 2) + '%'
                return [
                    `{value|${bfs}}`,
                    `{name|${item.name}}`
                ].join('\n')
            }
        }],
        labelLine: {
            show: true,
            lineStyle: {
                color: '#fff'
            }
        },
        label: {
            show: true,
            position: 'center',
            formatter: '{b} {c} {d}%'
        },
        tooltip: {
            backgroundColor: '#033b77',
            borderColor: '#21f2c4',
            textStyle: {
                color: '#fff',
                fontSize: 13
            },
            formatter: params => {
                if (
                    params.seriesName !== 'mouseoutSeries' &&
                    params.seriesName !== 'pie2d'
                ) {
                    const bfb = (
                        (option.series[params.seriesIndex].pieData.endRatio -
                            option.series[params.seriesIndex].pieData.startRatio) *
                        100
                    ).toFixed(2)
                    return (
                        `${params.seriesName}<br/>` +
                        `<span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${params.color};"></span>` +
                        `${bfb}%`
                    )
                }
            }
        },
        xAxis3D: {
            min: -1,
            max: 1
        },
        yAxis3D: {
            min: -1,
            max: 1
        },
        zAxis3D: {
            min: -1,
            max: 1
        },
        grid3D: {
            show: false,
            left: -190, //设置 组件的视图离容器左侧的距离
            boxHeight: boxHeight, // 圆环的高度
            viewControl: {
                // 3d效果可以放大、旋转等,请自己去查看官方配置
                alpha, // 角度
                distance, // 调整视角到主体的距离,类似调整zoom
                rotateSensitivity: 0, // 设置为0无法旋转
                zoomSensitivity: 0, // 设置为0无法缩放
                panSensitivity: 0, // 设置为0无法平移
                autoRotate: false // 自动旋转
            }
        },
        series: series
    }
    return option
}

/**
 * 生成扇形的曲面参数方程,用于 series-surface.parametricEquation
 */
const getParametricEquation = (startRatio, endRatio, isSelected, isHovered, k, h) => {
    // 计算
    const midRatio = (startRatio + endRatio) / 2
    const startRadian = startRatio * Math.PI * 2
    const endRadian = endRatio * Math.PI * 2
    const midRadian = midRatio * Math.PI * 2
    // 如果只有一个扇形,则不实现选中效果。
    if (startRatio === 0 && endRatio === 1) {
        isSelected = false
    }
    // 通过扇形内径/外径的值,换算出辅助参数 k(默认值 1/3)
    k = typeof k !== 'undefined' ? k : 1 / 3
    // 计算选中效果分别在 x 轴、y 轴方向上的位移(未选中,则位移均为 0)
    const offsetX = isSelected ? Math.cos(midRadian) * 0.1 : 0
    const offsetY = isSelected ? Math.sin(midRadian) * 0.1 : 0
    // 计算高亮效果的放大比例(未高亮,则比例为 1)
    const hoverRate = isHovered ? 1.1 : 1
    // 返回曲面参数方程
    return {
        u: {
            min: -Math.PI,
            max: Math.PI * 3,
            step: Math.PI / 32
        },
        v: {
            min: 0,
            max: Math.PI * 2,
            step: Math.PI / 20
        },
        x: function (u, v) {
            if (u < startRadian) {
                return (
                    offsetX +
                    Math.cos(startRadian) * (1 + Math.cos(v) * k) * hoverRate
                )
            }
            if (u > endRadian) {
                return (
                    offsetX + Math.cos(endRadian) * (1 + Math.cos(v) * k) * hoverRate
                )
            }
            return offsetX + Math.cos(u) * (1 + Math.cos(v) * k) * hoverRate
        },
        y: function (u, v) {
            if (u < startRadian) {
                return (
                    offsetY +
                    Math.sin(startRadian) * (1 + Math.cos(v) * k) * hoverRate
                )
            }
            if (u > endRadian) {
                return (
                    offsetY + Math.sin(endRadian) * (1 + Math.cos(v) * k) * hoverRate
                )
            }
            return offsetY + Math.sin(u) * (1 + Math.cos(v) * k) * hoverRate
        },
        z: function (u, v) {

            if (u < -Math.PI * 0.5) {
                // console.log("1",u,Math.sin(u))
                return Math.sin(u)
            }
            if (u > Math.PI * 2.5) {
                // console.log("2",u,Math.sin(u) * h * 0.1)
                return Math.sin(u) * h * 0.1
            }
            return Math.sin(v) > 0 ? 1 * h * 0.1 : -1
        }
    }
}

/**
 * 获取3d饼图的最高扇区的高度
 */
const getHeight3D = (series, height) => {
    series.sort((a, b) => {
        return b.pieData.value - a.pieData.value
    })
    return (height * 25) / series[0].pieData.value
}

/**
 * 格式化浮点数
 */
const fomatFloat = (num, n) => {
    let f = parseFloat(num)
    if (isNaN(f)) {
        return false
    }
    f = Math.round(num * Math.pow(10, n)) / Math.pow(10, n) // n 幂
    let s = f.toString()
    let rs = s.indexOf('.')
    // 判定如果是整数,增加小数点再补0
    if (rs < 0) {
        rs = s.length
        s += '.'
    }
    while (s.length <= rs + n) {
        s += '0'
    }
    return s
}
var option_6 = getPie3D(optionData, 0.8, 110, 20, 15, 1);

myChart_6.setOption(option_6);
let hoveredIndex = ''
let optionName = 'option_6'
myChart_6.on('mouseover', (params) => {
    // 准备重新渲染扇形所需的参数
    let isSelected
    let isHovered
    let startRatio
    let endRatio
    let k
    // console.log(params.seriesIndex)
    // console.log(this['option_6'])
    // 如果触发 mouseover 的扇形当前已高亮,则不做操作
    if (hoveredIndex === params.seriesIndex) {
        // 否则进行高亮及必要的取消高亮操作
    } else {
        // 如果当前有高亮的扇形,取消其高亮状态(对 option 更新)
        if (hoveredIndex !== '') {
            // 从 option.series 中读取重新渲染扇形所需的参数,将是否高亮设置为 false。
            isSelected = this[optionName].series[hoveredIndex].pieStatus.selected
            isHovered = false
            startRatio = this[optionName].series[hoveredIndex].pieData.startRatio
            endRatio = this[optionName].series[hoveredIndex].pieData.endRatio
            k = this[optionName].series[hoveredIndex].pieStatus.k
            // 对当前点击的扇形,执行取消高亮操作(对 option 更新)
            this[optionName].series[
                hoveredIndex
            ].parametricEquation = getParametricEquation(
                startRatio,
                endRatio,
                isSelected,
                isHovered,
                k,
                this[optionName].series[hoveredIndex].pieData.value
            )
            this[optionName].series[hoveredIndex].pieStatus.hovered = isHovered
            // 将此前记录的上次选中的扇形对应的系列号 seriesIndex 清空
            hoveredIndex = ''
        }
        // 如果触发 mouseover 的扇形不是透明圆环,将其高亮(对 option 更新)
        if (
            params.seriesName !== 'mouseoutSeries' &&
            params.seriesName !== 'pie2d'
        ) {
            // 从 option.series 中读取重新渲染扇形所需的参数,将是否高亮设置为 true。
            isSelected =
                this[optionName].series[params.seriesIndex].pieStatus.selected
            isHovered = true
            startRatio =
                this[optionName].series[params.seriesIndex].pieData.startRatio
            endRatio = this[optionName].series[params.seriesIndex].pieData.endRatio
            k = this[optionName].series[params.seriesIndex].pieStatus.k
            // 对当前点击的扇形,执行高亮操作(对 option 更新)
            this[optionName].series[
                params.seriesIndex
            ].parametricEquation = getParametricEquation(
                startRatio,
                endRatio,
                isSelected,
                isHovered,
                k,
                this[optionName].series[params.seriesIndex].pieData.value + 60
            )
            this[optionName].series[
                params.seriesIndex
            ].pieStatus.hovered = isHovered
            // 记录上次高亮的扇形对应的系列号 seriesIndex
            hoveredIndex = params.seriesIndex
        }
        // 使用更新后的 option,渲染图表
        myChart_6.setOption(this[optionName])
    }
})
// 修正取消高亮失败的 bug
myChart_6.on('globalout', () => {
    // 准备重新渲染扇形所需的参数
    let isSelected
    let isHovered
    let startRatio
    let endRatio
    let k
    if (hoveredIndex !== '') {
        // 从 option.series 中读取重新渲染扇形所需的参数,将是否高亮设置为 true。
        isSelected = this[optionName].series[hoveredIndex].pieStatus.selected
        isHovered = false
        k = this[optionName].series[hoveredIndex].pieStatus.k
        startRatio = this[optionName].series[hoveredIndex].pieData.startRatio
        endRatio = this[optionName].series[hoveredIndex].pieData.endRatio
        // 对当前点击的扇形,执行取消高亮操作(对 option 更新)
        this[optionName].series[
            hoveredIndex
        ].parametricEquation = getParametricEquation(
            startRatio,
            endRatio,
            isSelected,
            isHovered,
            k,
            this[optionName].series[hoveredIndex].pieData.value
        )
        this[optionName].series[hoveredIndex].pieStatus.hovered = isHovered
        // 将此前记录的上次选中的扇形对应的系列号 seriesIndex 清空
        hoveredIndex = ''
    }
    // 使用更新后的 option,渲染图表
    myChart_6.setOption(this[optionName])
})

仪表盘

插件引入
  • echarts.js
效果图

在这里插入图片描述

代码
var val = 36.5;
var value = val / 100;
var startAngle = 215;
var endAngle = -30;
var splitCount = 40; // 刻度数量
var fontSize = 45;
var pointerAngle = (startAngle - endAngle) * (1 - value) + endAngle;
option = {
  backgroundColor: "#062a44",
  tooltip: {
    show: true, // 是否显示提示框,默认为true
  },
  title: {
    show: true,
    x: "center",
    y: "70%",
    text: "示例详情",
    textStyle: {
      color: "#1483ED",
      fontWeight: "bold",
      fontSize: 25,
    },
  },
  series: [
    {
      // 值外
      type: "gauge",
      radius: "75%",
      startAngle: pointerAngle,
      endAngle: endAngle,
      splitNumber: 1,
      axisLine: {
        show: false,
        lineStyle: {
          width: 4,
          opacity: 0,
        },
      },
      title: {
        show: false,
      },
      detail: {
        color: "#fff",
        fontSize,
        offsetCenter: [0, 0],
        formatter: `${val}%`,
      },
      splitLine: {
        show: false,
      },
      axisTick: {
        length: 20,
        splitNumber: Math.ceil((1 - value) * splitCount),
        lineStyle: {
          color: "#2E3B4A",
          width: 3,
        },
      },
      axisLabel: {
        show: false,
      },
      pointer: {
        show: false,
      },
      itemStyle: {},
      markPoint: {
        animation: true,
        silent: false,
        data: [
          {
            symbolSize: 280,
            itemStyle: {
              borderWidth: 3,
            },
          },
          {
            symbol: "circle",
            symbolSize: 200,
          },
        ],
      },
      data: [
        {
          value: value,
          name: "test gauge",
        },
      ],
    },
    {
      // 值内
      type: "gauge",
      radius: "75%",
      startAngle: startAngle,
      endAngle: pointerAngle,
      splitNumber: 1,
      axisLine: {
        show: false,
        lineStyle: {
          width: 3,
          opacity: 0,
        },
      },
      tooltip: {
        show: true,
      },
      title: {
        show: false,
      },
      detail: {
        show: false,
      },
      splitLine: {
        show: false,
      },
      axisTick: {
        length: 20,
        splitNumber: Math.ceil(value * splitCount),
        lineStyle: {
          color: "#0096FD",
          width: 3,
        },
      },
      axisLabel: {
        show: false,
      },
      pointer: {
        show: false,
      },
      itemStyle: {},
      data: [
        {
          value: value,
          name: "test gauge",
        },
      ],
    },
  ],
};

电池图

插件引入
  • echarts.js
效果图

在这里插入图片描述

代码
 var option_4 = {
        grid: [{ //设置边距
            left: 55,
            bottom: 40,
            top: 20,
            right: 10
        }],
        yAxis: { //Y轴配置
            show: true,
            axisLine: { //轴线
                show: true,
                lineStyle: {
                    color: "#fff",
                    width: 1, //线宽
                    type: 'solid', //线的形状:'solid'、'dashed'、'dotted'
                    opacity: 1 //透明度:0~1
                }, //轴线的样式

            },
            axisTick: { //刻度线
                show: false,
                inside: false, //刻度线是否朝内
                length: 5, //刻度线长度
                lineStyle: { //刻度线样式
                    color: "#fff",
                    width: 1, //线宽
                }
            },
            axisLabel: { //坐标轴刻度标签
                show: true,
                color: "#fff",
                fontSize: 12,
                rotate: 0, //旋转角度,范围:-90~90
            },
            splitLine: { //分割线
                show: true,
                lineStyle: { //分割线样式
                    color: ['#fff'],
                    type: 'dashed', //线的形状:'solid'、'dashed'、'dotted'
                    opacity: .5
                }
            }
        },
        xAxis: { //X轴配置
            data: category, //X轴数据
            show: true,
            axisLabel: { //X轴文字样式
                color: '#a9aabc',
                fontSize: 12,
                interval: 0,
                padding: [10, 0, 0, 0],
                formatter: function (params) {
                    var newParamsName = "";
                    var paramsNameNumber = params.length;
                    var provideNumber = 5; //一行显示几个字
                    var rowNumber = Math.ceil(paramsNameNumber / provideNumber);
                    if (paramsNameNumber > provideNumber) {
                        for (var p = 0; p < rowNumber; p++) {
                            var tempStr = "";
                            var start = p * provideNumber;
                            var end = start + provideNumber;
                            if (p == rowNumber - 1) {
                                tempStr = params.substring(start, paramsNameNumber);
                            } else {
                                tempStr = params.substring(start, end) + "\n";
                            }
                            newParamsName += tempStr;
                        }
                    } else {
                        newParamsName = params;
                    }
                    return newParamsName;
                }
            },
            axisLine: {
                show: false
            },
            axisTick: {
                show: false,
            },
        },
        tooltip: { //以下方法二选一
            trigger: 'axis', //axis:坐标轴触发,主要在柱状图,折线图等会使用类目轴的图表中使用。item:数据项图形触发,主要在散点图,饼图等无类目轴的图表中使用。
            formatter: '时间:{b} <br/>订单数:{c0}', //模板变量有 {a}, {b},{c},{d},{e},分别表示系列名,数据名,数据值等。
            backgroundColor: "rgba(8, 36, 92, 0.8)",
            axisPointer: { //坐标轴指示器配置项。
                type: 'none' //'line' 直线指示器、'shadow' 阴影指示器、'none' 无指示器、'cross' 十字准星指示器。其实是种简写,表示启用两个正交的轴的 axisPointer。
            }
        },

        series: [{
            name: '',
            type: 'pictorialBar', //设置类型为象形柱状图
            symbol: 'roundRect', //图形类型,带圆角的矩形
            barWidth: '11%', //柱图宽度
            barMaxWidth: '20%', //最大宽度
            symbolMargin: '3', //图形垂直间隔
            animationDelay: (dataIndex, params) => { //每个图形动画持续时间
                return params.index * 50;
            },
            itemStyle: {
                normal: {
                    color: "#1EE6E7"

                }
            },
            z: 1,
            symbolRepeat: true, //图形是否重复
            symbolSize: [15, 6], //图形元素的尺寸
            data: count, //Y轴数据
            animationEasing: 'elasticOut', //动画效果,
            label: {
                normal: {
                    show: true,
                    position: 'top',
                    textStyle: {
                        color: "#fff"
                    }
                }
            },

        }]
    }
转载请注明出处或者链接地址:https://www.qianduange.cn//article/11167.html
标签
评论
发布的文章

1.10 Unity中的数据存储 JSON

2024-06-13 21:06:30

JSON 数据格式化方法

2024-06-13 21:06:26

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!