知名网站建设定制Echarts 3D饼图开发


知名网站建设定制近期的开发需求,知名网站建设定制需要开发一个3D。不同于echarts知名网站建设定制的二维饼图,有完善的API,知名网站建设定制开发起来比较顺手。3D知名网站建设定制类的图资料较少,就连Echarts知名网站建设定制官网提供的相关API知名网站建设定制信息也是模模糊糊的,知名网站建设定制理解起来不容易。

知名网站建设定制以饼图为例子。知名网站建设定制一个完整的2D知名网站建设定制知名网站建设定制饼图是由一个或者多个知名网站建设定制扇形组成的;知名网站建设定制而一个完整的3D饼图是由一个或者多个知名网站建设定制扇形曲面组成。

Echarts知名网站建设定制曲面绘制通过series-. type ="surface"配置项来设置,详细参数说明,请参考官网。
|——》

其实光看官网的配置参数,会很难理解。因为没有足够的示例,无法进行调试测试,导致对于知识难掌握,容易产生厌烦情绪。(没错!说的就是俺)

最好的办法就是先去社区或者网络找找有没有相关案例,感谢这些乐于分享知识的开发者们。——》

其实也有不少,但是并不能完全满足我的开发需求。只能通过现有的满足条件的案例,拿到代码后再跑一遍,理解里面的配置项的涵义,再自己慢慢调整开发。

下面对实现流程进行简单解析。

准备工作

依赖除了必要的echarts依赖外,还需echarts-gl。
|——》

在这里要注意版本匹配,否则会报错:

echarts-gl 2.x版本的是和echarts 5.X的版本相匹配的。
echarts-gl 1.x版本的是和echarts 4.X的版本相匹配的。

也就是说如果你echarts是4.x的,但是echarts-gl是2.x的,这是万万使不得的,会报错哦~

3D饼图实现

如下图的3D饼图,是由4个扇形曲面实现。

生成扇形的曲面参数方程

用于 series-surface.parametricEquation

 function getParametricEquation(startRatio, endRatio, isSelected, isHovered) {            // 计算            let midRatio = (startRatio + endRatio) / 2;            let startRadian = startRatio * Math.PI * 2;            let endRadian = endRatio * Math.PI * 2;            let midRadian = midRatio * Math.PI * 2;            // 如果只有一个扇形,则不实现选中效果。            if (startRatio === 0 && endRatio === 1) {                isSelected = false;            }            // 计算选中效果分别在 x 轴、y 轴方向上的位移(位移均为 0)            let offsetX = 0;            let offsetY = 0;            // 计算选中效果在 z 轴方向上的位移(未选中,位移均为 0)            let offsetZ = isSelected ? 0.15 : 0;            // 计算高亮效果的放大比例(未高亮,则比例为 1)            let hoverRate = isHovered ? 1.05 : 1;            let tmp = 0;            // 返回曲面参数方程            return {                u: {                    min: 0,                    max: Math.PI * 2,                    step: Math.PI / 100,                },                v: {                    min: 0,                    max: Math.PI,                    step: Math.PI / 50,                },                x: function (u, v) {                    if (midRatio - 0.5 < 0) {                        if (u < startRadian || u > midRadian + Math.PI) {                            tmp =                                u - Math.PI - midRadian < 0                                    ? u + Math.PI - midRadian                                    : u - Math.PI - midRadian;                            return (                                offsetX +                                ((Math.sin(startRadian) * tmp) /                                    (Math.PI - midRadian + startRadian)) *                                hoverRate                            );                        }                        if (u > endRadian && u < midRadian + Math.PI) {                            tmp = midRadian + Math.PI - u;                            return (                                offsetX +                                ((Math.sin(endRadian) * tmp) /                                    (Math.PI - midRadian + startRadian)) *                                hoverRate                            );                        }                    } else {                        if (u < startRadian && u > midRadian - Math.PI) {                            tmp = u + Math.PI - midRadian;                            return (                                offsetX +                                ((Math.sin(startRadian) * tmp) /                                    (Math.PI - midRadian + startRadian)) *                                hoverRate                            );                        }                        if (u > endRadian || u < midRadian - Math.PI) {                            tmp =                                midRadian - Math.PI - u < 0                                    ? midRadian + Math.PI - u                                    : midRadian - Math.PI - u;                            return (                                offsetX +                                ((Math.sin(endRadian) * tmp) /                                    (Math.PI - midRadian + startRadian)) *                                hoverRate                            );                        }                    }                    return offsetX + Math.sin(v) * Math.sin(u) * hoverRate;                },                y: function (u, v) {                    if (midRatio - 0.5 < 0) {                        if (u < startRadian || u > midRadian + Math.PI) {                            tmp =                                u - Math.PI - midRadian < 0                                    ? u + Math.PI - midRadian                                    : u - Math.PI - midRadian;                            return (                                offsetY +                                ((Math.cos(startRadian) * tmp) /                                    (Math.PI - midRadian + startRadian)) *                                hoverRate                            );                        }                        if (u > endRadian && u < midRadian + Math.PI) {                            tmp = midRadian + Math.PI - u;                            return (                                offsetY +                                ((Math.cos(endRadian) * tmp) /                                    (Math.PI - midRadian + startRadian)) *                                hoverRate                            );                        }                    } else {                        if (u < startRadian && u > midRadian - Math.PI) {                            tmp = u + Math.PI - midRadian;                            return (                                offsetY +                                ((Math.cos(startRadian) * tmp) /                                    (Math.PI - midRadian + startRadian)) *                                hoverRate                            );                        }                        if (u > endRadian || u < midRadian - Math.PI) {                            tmp =                                midRadian - Math.PI - u < 0                                    ? midRadian + Math.PI - u                                    : midRadian - Math.PI - u;                            return (                                offsetY +                                ((Math.cos(endRadian) * tmp) /                                    (Math.PI - midRadian + startRadian)) *                                hoverRate                            );                        }                    }                    return offsetY + Math.sin(v) * Math.cos(u) * hoverRate;                },                z: function (u, v) {                    return offsetZ + (Math.cos(v) > 0 ? 0.1 : -0.1);                },            };        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131

大部分小可爱肯定会在这一步感到疑惑,u、v、x、y、z这都是啥呀(((o(゚▽゚)o)))?赶紧去官网查查。
然而,

┻━┻︵╰(‵□′)╯︵┻━┻

没错,依然还是不懂呀!!!(希望官网能再完善一下,写得再详细一些。)

其实,u、v、x、y、z是与球坐标系相关的参数。

球坐标系是三维坐标系的一种,用以确定三维空间中点、线、面以及体的位置,它以坐标原点为参考点,由方位角、仰角和距离构成。

在数学里,球坐标系(英语:Spherical coordinate system)是一种利用球坐标表示一个点 p 在三维空间的位置的三维正交坐标系。图1显示了球坐标的几何意义:原点到 P 点的距离 r ,原点到点 P 的连线与正 z-轴之间的天顶角以及原点到点 P 的连线,在 xy-平面的投影线,与正 x-轴之间的方位角。

例子:
假设P(x,y,z)为空间内一点,则点P也可用这样三个有次序的数(r,θ,φ)来确定,其中r为原点O与点P间的距离;θ为有向线段OP与z轴正向的夹角;φ为从正z轴来看自x轴按逆时针方向转到OM所转过的角,这里M为点P在xOy面上的投影;。这样的三个数r,θ,φ叫做点P的球面坐标,显然,这里r,θ,φ的变化范围为r∈[0,+∞),θ∈[0, π], φ∈[0,2π] ,如图所示。
当r,θ或φ分别为常数时,可以表示如下特殊曲面:r = 常数,即以原点为心的球面;θ= 常数,即以原点为顶点、z轴为轴的圆锥面;φ= 常数,即过z轴的半平面。
转化:
1).球坐标系(r,θ,φ)与直角坐标系(x,y,z)的转换关系:
x=rsinθcosφ.
y=rsinθsinφ.
z=rcosθ.
2).反之,直角坐标系(x,y,z)与球坐标系(r,θ,φ)的转换关系为:


在这里,u就代表球坐标系中的φ,v代表球坐标系中的θ。

有了这层知识护航,再去看上面的曲面方程,理解立刻+1了!↖(⁎⁍̴̛ᴗ⁍̴̛⁎)↗

再搭配这个3D球示例看,会更上一层楼哦~
|——》

调用曲面方程生成3D图

为每一个配置项生成曲面3D扇形。

  // 生成模拟 3D 饼图的配置项        function getPie3D(pieData) {            let series = [];            let sumValue = 0;            let startValue = 0;            let endValue = 0;            let legendData = [];            // 为每一个饼图数据,生成一个 series-surface 配置            for (let i = 0; i < pieData.length; i++) {                sumValue += pieData[i].value;                let 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,                    },                };                if (typeof pieData[i].itemStyle != "undefined") {                    let itemStyle = {};                    if (typeof pieData[i].itemStyle.color != "undefined") {                        itemStyle.color = pieData[i].itemStyle.color;                    }                    if (typeof pieData[i].itemStyle.opacity != "undefined") {                        itemStyle.opacity = pieData[i].itemStyle.opacity;                    }                    seriesItem.itemStyle = itemStyle;                }                series.push(seriesItem);            }            // 使用上一次遍历时,计算出的数据和 sumValue,调用 getParametricEquation 函数,            // 向每个 series-surface 传入不同的参数方程 series-surface.parametricEquation,也就是实现每一个扇形。            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                );                startValue = endValue;                legendData.push(series[i].name);            }            // 补充一个透明的圆环,用于支撑高亮功能的近似实现。            series.push({                name: "mouseoutSeries",                type: "surface",                parametric: true,                wireframe: {                    show: false,                },                itemStyle: {                    opacity: 0,                },                parametricEquation: {                    u: {                        min: 0,                        max: Math.PI * 2,                        step: Math.PI / 20,                    },                    v: {                        min: 0,                        max: Math.PI,                        step: Math.PI / 20,                    },                    x: function (u, v) {                        return Math.sin(v) * Math.sin(u) + Math.sin(u);                    },                    y: function (u, v) {                        return Math.sin(v) * Math.cos(u) + Math.cos(u);                    },                    z: function (u, v) {                        return Math.cos(v) > 0 ? 0.1 : -0.1;                    },                },            });            // 准备待返回的配置项,把准备好的 legendData、series 传入。            let option = {                legend: {                    show: false,                    data: legendData,                },                xAxis3D: {                    min: -1,                    max: 1,                },                yAxis3D: {                    min: -1,                    max: 1,                },                zAxis3D: {                    min: -1,                    max: 1,                },                grid3D: {                    show: false,                    boxHeight: 100, // 厚度                    top: 4,                    left: 0,                    boxWidth: 144,                    viewControl: {                        //3d效果可以放大、旋转等,请自己去查看官方配置                        alpha: 43, // 角度                        beta: 0, // 饼块开始位置角度                        rotateSensitivity: 0,                        zoomSensitivity: 0,                        panSensitivity: 0,                        autoRotate: false,                    },                    // !!!不要配置postEffect,会影响性能。                    //后处理特效可以为画面添加高光、景深、环境光遮蔽(SSAO)、调色等效果。可以让整个画面更富有质感。                      // postEffect: {                    //     //配置这项会出现锯齿,请自己去查看官方配置有办法解决                    //     enable: true,                    //     bloom: {                    //         enable: true,                    //         bloomIntensity: 0.1,                    //     },                    //     SSAO: {                    //         enable: true,                    //         quality: "medium",                    //         radius: 2,                    //     },                    // },                    light: {                        main: {                            color: "rgb(85, 84, 84)", // 主光源的颜色。                            shadow: true, // 主光源是否投射阴影                            alpha: 80, // 主光源绕 x 轴,即上下旋转的角度                        },                    },                },                series: series,            };            return option;        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147

指示线实现

方案:

1.用series-line3D,生成指示线。
我试了一下,这种不好看,生硬,不灵活。而且很容易超出边界。

2.在原来的曲面上,再生成一层2D饼图,调整2D饼图的位置和大小,再使2D饼图透明,保留其指示线和标签。
(这里比较难的是,调整2D图的位置和大小,因为2D图和标签无法完美发生角度偏转,若3D图旋转角度过大,则2D标签无法调整到和3D图完美匹配。)

透明前:

透明后:

关键代码:

        option.series.push({            name: '酒水销售占比', //自己根据场景修改            type: 'pie',            hoverAnimation: false,// 悬停不放大            label: {                position: "bottom",                formatter: function (params) {                    return `{percentSty|${params.percent}%}{nameSty|${params.name}}`;                },                rich: {                    nameSty: {                        fontSize: 16,                        lineHeight: 22,                        fontFamily: "PingFangSC-Regular",                        fintWeight: 400,                    },                    percentSty: {                        fontSize: 14,                        lineHeight: 20,                        fontFamily: "PingFangSC-Regular",                        fintWeight: 400,                        color: '#FFFFFF',                    },                    countSty: {                        fontSize: 14,                        lineHeight: 20,                        fontFamily: "PingFangSC-Regular",                        fintWeight: 400,                        color: '#B9D3ED',                        padding: [0, 8, 0, 8],                        backgroundColor: 'rgb(90,156,241,0.3)',                        borderRadius: 2,                    },                },            },            labelLine: {                showAbove: false,                length: 20, // 视觉引导线第一段的长度                length2: 40, // 视觉引导项第二段的长度                lineStyle: {                    color: "#686868", // 改变标示线的颜色                    width: 1,                    type: 'solid', // 线的类型                },            },            startAngle: 60, // 起始角度,支持范围[0, 360]。            clockwise: true, // 饼图的扇区是否是顺时针排布。上述这两项配置主要是为了对齐3d的样式            radius: ['40%', '52%'],            center: ['50%', '53%'],            data: paramsList,            itemStyle: {                opacity: 0  //这里必须是0,不然2d的图会覆盖在表面            }        })
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
到此,3D饼图的难点之处已介绍完毕,有需要的小可爱可以自己着手试试。看看指示线部分还有没有其他更好的方法实现,若找到,务必@我,让我学习一下☆〜(ゝ。∂)

完整代码贴上

<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <meta http-equiv="X-UA-Compatible" content="IE=edge">    <meta name="viewport" content="width=device-width, initial-scale=1.0">    <title>Ehcarts Demo</title>    <script src="https://cdn.staticfile.org/echarts/4.3.0/echarts.min.js"></script>    <script src="https://echarts.baidu.com/resource/echarts-gl-latest/dist/echarts-gl.min.js"></script></head><body>    <div class="container">        <div id="chartsContent" style="width:560px;height:150px;"></div>    </div>    <script>        var myChart = echarts.init(document.getElementById('chartsContent'));        // 生成扇形的曲面参数方程,用于 series-surface.parametricEquation        function getParametricEquation(startRatio, endRatio, isSelected, isHovered) {            // 计算            let midRatio = (startRatio + endRatio) / 2;            let startRadian = startRatio * Math.PI * 2;            let endRadian = endRatio * Math.PI * 2;            let midRadian = midRatio * Math.PI * 2;            // 如果只有一个扇形,则不实现选中效果。            if (startRatio === 0 && endRatio === 1) {                isSelected = false;            }            // 计算选中效果分别在 x 轴、y 轴方向上的位移(位移均为 0)            let offsetX = 0;            let offsetY = 0;            // 计算选中效果在 z 轴方向上的位移(未选中,位移均为 0)            let offsetZ = isSelected ? 0.15 : 0;            // 计算高亮效果的放大比例(未高亮,则比例为 1)            let hoverRate = isHovered ? 1.05 : 1;            let tmp = 0;            // 返回曲面参数方程            return {                u: {                    min: 0,                    max: Math.PI * 2,                    step: Math.PI / 100,                },                v: {                    min: 0,                    max: Math.PI,                    step: Math.PI / 50,                },                x: function (u, v) {                    if (midRatio - 0.5 < 0) {                        if (u < startRadian || u > midRadian + Math.PI) {                            tmp =                                u - Math.PI - midRadian < 0                                    ? u + Math.PI - midRadian                                    : u - Math.PI - midRadian;                            return (                                offsetX +                                ((Math.sin(startRadian) * tmp) /                                    (Math.PI - midRadian + startRadian)) *                                hoverRate                            );                        }                        if (u > endRadian && u < midRadian + Math.PI) {                            tmp = midRadian + Math.PI - u;                            return (                                offsetX +                                ((Math.sin(endRadian) * tmp) /                                    (Math.PI - midRadian + startRadian)) *                                hoverRate                            );                        }                    } else {                        if (u < startRadian && u > midRadian - Math.PI) {                            tmp = u + Math.PI - midRadian;                            return (                                offsetX +                                ((Math.sin(startRadian) * tmp) /                                    (Math.PI - midRadian + startRadian)) *                                hoverRate                            );                        }                        if (u > endRadian || u < midRadian - Math.PI) {                            tmp =                                midRadian - Math.PI - u < 0                                    ? midRadian + Math.PI - u                                    : midRadian - Math.PI - u;                            return (                                offsetX +                                ((Math.sin(endRadian) * tmp) /                                    (Math.PI - midRadian + startRadian)) *                                hoverRate                            );                        }                    }                    return offsetX + Math.sin(v) * Math.sin(u) * hoverRate;                },                y: function (u, v) {                    if (midRatio - 0.5 < 0) {                        if (u < startRadian || u > midRadian + Math.PI) {                            tmp =                                u - Math.PI - midRadian < 0                                    ? u + Math.PI - midRadian                                    : u - Math.PI - midRadian;                            return (                                offsetY +                                ((Math.cos(startRadian) * tmp) /                                    (Math.PI - midRadian + startRadian)) *                                hoverRate                            );                        }                        if (u > endRadian && u < midRadian + Math.PI) {                            tmp = midRadian + Math.PI - u;                            return (                                offsetY +                                ((Math.cos(endRadian) * tmp) /                                    (Math.PI - midRadian + startRadian)) *                                hoverRate                            );                        }                    } else {                        if (u < startRadian && u > midRadian - Math.PI) {                            tmp = u + Math.PI - midRadian;                            return (                                offsetY +                                ((Math.cos(startRadian) * tmp) /                                    (Math.PI - midRadian + startRadian)) *                                hoverRate                            );                        }                        if (u > endRadian || u < midRadian - Math.PI) {                            tmp =                                midRadian - Math.PI - u < 0                                    ? midRadian + Math.PI - u                                    : midRadian - Math.PI - u;                            return (                                offsetY +                                ((Math.cos(endRadian) * tmp) /                                    (Math.PI - midRadian + startRadian)) *                                hoverRate                            );                        }                    }                    return offsetY + Math.sin(v) * Math.cos(u) * hoverRate;                },                z: function (u, v) {                    return offsetZ + (Math.cos(v) > 0 ? 0.1 : -0.1);                },            };        }        // 生成模拟 3D 饼图的配置项        function getPie3D(pieData) {            let series = [];            let sumValue = 0;            let startValue = 0;            let endValue = 0;            let legendData = [];            // 为每一个饼图数据,生成一个 series-surface 配置            for (let i = 0; i < pieData.length; i++) {                sumValue += pieData[i].value;                let 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,                    },                };                if (typeof pieData[i].itemStyle != "undefined") {                    let itemStyle = {};                    if (typeof pieData[i].itemStyle.color != "undefined") {                        itemStyle.color = pieData[i].itemStyle.color;                    }                    if (typeof pieData[i].itemStyle.opacity != "undefined") {                        itemStyle.opacity = pieData[i].itemStyle.opacity;                    }                    seriesItem.itemStyle = itemStyle;                }                series.push(seriesItem);            }            // 使用上一次遍历时,计算出的数据和 sumValue,调用 getParametricEquation 函数,            // 向每个 series-surface 传入不同的参数方程 series-surface.parametricEquation,也就是实现每一个扇形。            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                );                startValue = endValue;                legendData.push(series[i].name);            }            // 补充一个透明的圆环,用于支撑高亮功能的近似实现。            series.push({                name: "mouseoutSeries",                type: "surface",                parametric: true,                wireframe: {                    show: false,                },                itemStyle: {                    opacity: 0,                },                parametricEquation: {                    u: {                        min: 0,                        max: Math.PI * 2,                        step: Math.PI / 20,                    },                    v: {                        min: 0,                        max: Math.PI,                        step: Math.PI / 20,                    },                    x: function (u, v) {                        return Math.sin(v) * Math.sin(u) + Math.sin(u);                    },                    y: function (u, v) {                        return Math.sin(v) * Math.cos(u) + Math.cos(u);                    },                    z: function (u, v) {                        return Math.cos(v) > 0 ? 0.1 : -0.1;                    },                },            });            // 准备待返回的配置项,把准备好的 legendData、series 传入。            let option = {                legend: {                    show: false,                    data: legendData,                },                xAxis3D: {                    min: -1,                    max: 1,                },                yAxis3D: {                    min: -1,                    max: 1,                },                zAxis3D: {                    min: -1,                    max: 1,                },                grid3D: {                    show: false,                    boxHeight: 100, // 厚度                    top: 4,                    left: 0,                    boxWidth: 144,                    viewControl: {                        //3d效果可以放大、旋转等,请自己去查看官方配置                        alpha: 43, // 角度                        beta: 0, // 饼块开始位置角度                        rotateSensitivity: 0,                        zoomSensitivity: 0,                        panSensitivity: 0,                        autoRotate: false,                    },                    light: {                        main: {                            color: "rgb(85, 84, 84)", // 主光源的颜色。                            shadow: true, // 主光源是否投射阴影                            alpha: 80, // 主光源绕 x 轴,即上下旋转的角度                        },                    },                },                series: series,            };            return option;        }        // 监听鼠标事件,实现饼图选中效果(单选),近似实现高亮(放大)效果。        let selectedIndex = '';        let hoveredIndex = '';        // 监听点击事件,实现选中效果(单选)        myChart.on('click', function (params) {            // 从 option.series 中读取重新渲染扇形所需的参数,将是否选中取反。            let isSelected = !option.series[params.seriesIndex].pieStatus.selected;            let isHovered = option.series[params.seriesIndex].pieStatus.hovered;            let startRatio = option.series[params.seriesIndex].pieData.startRatio;            let endRatio = option.series[params.seriesIndex].pieData.endRatio;            // 如果之前选中过其他扇形,将其取消选中(对 option 更新)            if (selectedIndex !== '' && selectedIndex !== params.seriesIndex) {                option.series[selectedIndex].parametricEquation = getParametricEquation(option.series[selectedIndex].pieData.startRatio, option.series[selectedIndex].pieData.endRatio, false, false);                option.series[selectedIndex].pieStatus.selected = false;            }            // 对当前点击的扇形,执行选中/取消选中操作(对 option 更新)            option.series[params.seriesIndex].parametricEquation = getParametricEquation(startRatio, endRatio, isSelected, isHovered);            option.series[params.seriesIndex].pieStatus.selected = isSelected;            // 如果本次是选中操作,记录上次选中的扇形对应的系列号 seriesIndex            isSelected ? selectedIndex = params.seriesIndex : null;            console.log('option-click: ', option)            // 使用更新后的 option,渲染图表            myChart.setOption(option);        });        // 监听 mouseover,近似实现高亮(放大)效果        myChart.on('mouseover', function (params) {            // 准备重新渲染扇形所需的参数            let isSelected;            let isHovered;            let startRatio;            let endRatio;            // 如果触发 mouseover 的扇形当前已高亮,则不做操作            if (hoveredIndex === params.seriesIndex) {                return;                // 否则进行高亮及必要的取消高亮操作            } else {                // 如果当前有高亮的扇形,取消其高亮状态(对 option 更新)                if (hoveredIndex !== '') {                    // 从 option.series 中读取重新渲染扇形所需的参数,将是否高亮设置为 false。                    isSelected = option.series[hoveredIndex].pieStatus.selected;                    isHovered = false;                    startRatio = option.series[hoveredIndex].pieData.startRatio;                    endRatio = option.series[hoveredIndex].pieData.endRatio;                    // 对当前点击的扇形,执行取消高亮操作(对 option 更新)                    option.series[hoveredIndex].parametricEquation = getParametricEquation(startRatio, endRatio, isSelected, isHovered);                    option.series[hoveredIndex].pieStatus.hovered = isHovered;                    // 将此前记录的上次选中的扇形对应的系列号 seriesIndex 清空                    hoveredIndex = '';                }                // 如果触发 mouseover 的扇形不是透明圆环,将其高亮(对 option 更新)                if (params.seriesName !== 'mouseoutSeries') {                    // 从 option.series 中读取重新渲染扇形所需的参数,将是否高亮设置为 true。                    isSelected = option.series[params.seriesIndex].pieStatus.selected;                    isHovered = true;                    startRatio = option.series[params.seriesIndex].pieData.startRatio;                    endRatio = option.series[params.seriesIndex].pieData.endRatio;                    // 对当前点击的扇形,执行高亮操作(对 option 更新)                    option.series[params.seriesIndex].parametricEquation = getParametricEquation(startRatio, endRatio, isSelected, isHovered);                    option.series[params.seriesIndex].pieStatus.hovered = isHovered;                    // 记录上次高亮的扇形对应的系列号 seriesIndex                    hoveredIndex = params.seriesIndex;                }                // 使用更新后的 option,渲染图表                myChart.setOption(option);            }        });        // 修正取消高亮失败的 bug        myChart.on('globalout', function () {            if (hoveredIndex !== '') {                // 从 option.series 中读取重新渲染扇形所需的参数,将是否高亮设置为 true。                isSelected = option.series[hoveredIndex].pieStatus.selected;                isHovered = false;                startRatio = option.series[hoveredIndex].pieData.startRatio;                endRatio = option.series[hoveredIndex].pieData.endRatio;                // 对当前点击的扇形,执行取消高亮操作(对 option 更新)                option.series[hoveredIndex].parametricEquation = getParametricEquation(startRatio, endRatio, isSelected, isHovered);                option.series[hoveredIndex].pieStatus.hovered = isHovered;                // 将此前记录的上次选中的扇形对应的系列号 seriesIndex 清空                hoveredIndex = '';            }            // 使用更新后的 option,渲染图表            myChart.setOption(option);        });        const colorList = ['#D98053', '#E2B062', '#5A9CF1', '#6ED3D3']        const dataSource = [{            name: "啤酒",            value: 25,        },        {            name: "高粱酒",            value: 25,        },        {            name: "桃花酿",            value: 30,        },        {            name: "白酒",            value: 20,        },]        const paramsList = dataSource.map((item, index) => {            return {                ...item,                shading: 'realistic',                itemStyle: {                    color: colorList[index]                },            }        })        // 传入数据生成 option        let option = getPie3D(paramsList);        // 是否需要label指引线,如果要就添加一个透明的2d饼状图并调整角度使得labelLine和3d的饼状图对齐,并再次setOption        option.series.push({            name: '酒水销售占比', //自己根据场景修改            type: 'pie',            hoverAnimation: false,// 悬停不放大            label: {                position: "bottom",                formatter: function (params) {                    return `{percentSty|${params.percent}%}{nameSty|${params.name}}`;                },                rich: {                    nameSty: {                        fontSize: 16,                        lineHeight: 22,                        fontFamily: "PingFangSC-Regular",                        fintWeight: 400,                    },                    percentSty: {                        fontSize: 14,                        lineHeight: 20,                        fontFamily: "PingFangSC-Regular",                        fintWeight: 400,                        color: '#FFFFFF',                    },                    countSty: {                        fontSize: 14,                        lineHeight: 20,                        fontFamily: "PingFangSC-Regular",                        fintWeight: 400,                        color: '#B9D3ED',                        padding: [0, 8, 0, 8],                        backgroundColor: 'rgb(90,156,241,0.3)',                        borderRadius: 2,                    },                },            },            labelLine: {                showAbove: false,                length: 20, // 视觉引导线第一段的长度                length2: 40, // 视觉引导项第二段的长度                lineStyle: {                    color: "#686868", // 改变标示线的颜色                    width: 1,                    type: 'solid', // 线的类型                },            },            startAngle: 60, // 起始角度,支持范围[0, 360]。            clockwise: true, // 饼图的扇区是否是顺时针排布。上述这两项配置主要是为了对齐3d的样式            radius: ['40%', '52%'],            center: ['50%', '53%'],            data: paramsList,            itemStyle: {                opacity: 0  //这里必须是0,不然2d的图会覆盖在表面            }        })        myChart.setOption(option)    </script>    <style>        .container {            width: 560px;            height: 164px;            background-color: #000000;            position: relative;        }        .imgContent {            width: 164px;            height: 86px;            position: absolute;            left: 50%;            top: 50%;            transform: translate(-50%, -50%);            z-index: 5;        }    </style></body></html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 425
  • 426
  • 427
  • 428
  • 429
  • 430
  • 431
  • 432
  • 433
  • 434
  • 435
  • 436
  • 437
  • 438
  • 439
  • 440
  • 441
  • 442
  • 443
  • 444
  • 445
  • 446
  • 447
  • 448
  • 449
  • 450
  • 451
  • 452
  • 453
  • 454
  • 455
  • 456
  • 457
  • 458
  • 459
  • 460
  • 461
  • 462
  • 463
  • 464
  • 465
  • 466
  • 467
  • 468
  • 469
  • 470
  • 471
  • 472
  • 473
  • 474
  • 475
  • 476
  • 477
  • 478
  • 479
  • 480
  • 481
  • 482
  • 483
  • 484
  • 485
  • 486
  • 487
  • 488
  • 489
  • 490
  • 491
  • 492
  • 493
  • 494
  • 495
  • 496
  • 497
  • 498
  • 499
  • 500
  • 501
  • 502

问题记录

1.页面操作变得卡顿不流畅,性能降低。

解决:去除postEffect,此配置是为了让3D图更有质感,可其实在这里也没起多大作用。

 // postEffect: {                    //     //配置这项会出现锯齿,请自己去查看官方配置有办法解决                    //     enable: true,                    //     bloom: {                    //         enable: true,                    //         bloomIntensity: 0.1,                    //     },                    //     SSAO: {                    //         enable: true,                    //         quality: "medium",                    //         radius: 2,                    //     },                    // },
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发