赞
踩
实现动态的3D饼图(3D曲面图)
饼图数据过多的时候,全部展示不直观,看起来很乱
这里通过实现鼠标点击图例切换饼图,仅展示所选的单个城市,剩余数据统称为其他
"echarts": "^4.3.2",
"echarts-gl": "^1.1.2",
涉及的一些变量
data() { return { chartDom: null, allCityList: [], // 全部城市数据 optionsData: [], // 图例选中的城市数据 默认济南 // optionsData: [ // { // name: '济南', // value: 30, // itemStyle: { // color: '#41adf8', // } // } // ], dataObj: {}, // 用于tooltip展示 {'济南': { count: 30, percent: 15% }, ... } series: null, seriesList: null, option: {}, currentCity: '', }
绘制饼图方法:
getPieData() { let colorList = [ '#6054FE', '#3AFF97', '#FF7662', '#9793d1', '#f1be8b', '#eff70f', '#3d93f7', '#FF5A5A', '#23EDFF', '#FFA44B', '#FAFF6B', '#FF8484', '#ad5d5d', '#04ccdf', '#82da4f', ] fetchChartData().then((res) => { if (res.code === 200) { const { data} = res.data this.allCityList = data.map((item, i) => { let colorIndex = i < colorList.length ? i : i / colorList.length let percent = parseInt(item.percent * 10000) / 100 + '%' this.dataObj[item.cityName] = { count: item.value, percent, } return { name: item.cityName, value: item.value, itemStyle: { color: colorList[colorIndex], }, } }) this.chartDom = this.$echarts.init(document.getElementById('pieChart')) this.filterCityData() setTimeout(() => { // 增加图例点击事件 this.chartDom.on('legendselectchanged', (params) => { console.log('图例点击', params.name) this.filterCityData(params.name) // this.chartDom.setOption(func.barSelectesShadowOptions(option, true)) this.chartDom.setOption({ legend: { selected: { [params.name]: true } }, }) }) }, 200) } }) }, filterCityData(cityName = '济南') { this.currentCity = cityName // 除选中城市以外的所有数值之和 统称为其他 let totalCount = 0 this.optionsData = this.allCityList.map((item) => { if (item.name === cityName) { return item } else { totalCount += item.value } return { ...item, value: 0 } }) this.optionsData.push({ name: '其他', value: totalCount, itemStyle: { color: '#b969ff', }, }) let itemPercent = this.dataObj[cityName].percent.split('%')[0] let percent = (10000 - parseInt(itemPercent * 100)) / 100 + '%' this.draw3DChart() }, draw3DChart() { // 传入数据生成 option this.option = this.getPie3D(this.optionsData, 0.59) // 绘制图表 this.chartDom.setOption(this.option) },
// 生成扇形的曲面参数方程,用于 series-surface.parametricEquation getParametricEquation(startRatio, endRatio, isSelected, isHovered, k) { // 计算 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 } // 通过扇形内径/外径的值,换算出辅助参数 k(默认值 1/3) k = typeof k !== 'undefined' ? k : 1 / 3 // 计算选中效果分别在 x 轴、y 轴方向上的位移(未选中,则位移均为 0) let offsetX = isSelected ? Math.cos(midRadian) * 0.1 : 0 let offsetY = isSelected ? Math.sin(midRadian) * 0.1 : 0 // 计算高亮效果的放大比例(未高亮,则比例为 1) let hoverRate = isHovered ? 1.05 : 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) { return Math.sin(u) } if (u > Math.PI * 2.5) { return Math.sin(u) } return Math.sin(v) > 0 ? 1 : -1 }, } }, // 生成模拟 3D 饼图的配置项 getPie3D(pieData, internalDiameterRatio) { let series = [] let sumValue = 0 let startValue = 0 let endValue = 0 let legendData = [] let k = typeof internalDiameterRatio !== 'undefined' ? (1 - internalDiameterRatio) / (1 + internalDiameterRatio) : 1 / 3 // 为每一个饼图数据,生成一个 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, k: k, }, } if (typeof pieData[i].itemStyle != 'undefined') { let itemStyle = {} typeof pieData[i].itemStyle.color != 'undefined' ? (itemStyle.color = pieData[i].itemStyle.color) : null typeof pieData[i].itemStyle.opacity != 'undefined' ? (itemStyle.opacity = pieData[i].itemStyle.opacity) : null 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 = this.getParametricEquation( series[i].pieData.startRatio, series[i].pieData.endRatio, true, false, 1 ) startValue = endValue legendData.push(series[i].name) } // 补充一个透明的圆环,用于支撑高亮功能的近似实现。 series.push({ name: 'mouseoutSeries', type: 'surface', parametric: true, wireframe: { show: false, }, itemStyle: { opacity: 1, color: 'rgba(18,236,252,.5)', }, parametricEquation: { u: { min: 0, max: Math.PI * 2, step: Math.PI / 20, }, v: { min: 0, max: Math.PI, step: Math.PI / 1.4, }, 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 = { //animation: false, legend: { show: true, data: legendData, type: 'scroll', orient: 'vertical', right: 5, top: 5, bottom: 6, textStyle: { color: '#CAEFFF', }, pageIconColor: '#CAEFFF', //翻页箭头颜色 pageIconInactiveColor: '#a6b4ba', //翻页(即翻页到头时箭头的颜色) pageIconSize: 10, //翻页按钮大小 pageTextStyle: { color: '#CAEFFF', //翻页数字颜色 }, }, tooltip: { formatter: (params) => { if (params.seriesName !== 'mouseoutSeries' && params.seriesName !== 'pie2d') { // 当前饼图中没有展示这个城市 暂时隐藏掉不想展示的城市 let noInChart = ![this.currentCity, '其他'].includes(params.seriesName) if (noInChart) return '' const { count, percent } = this.dataObj[params.seriesName] return ( `${params.seriesName}<br/>` + `<span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${params.color};"></span>` + `${count}场次 ${percent}` ) } }, }, // 饼图加背景图 不需要可以注释 graphic: [ { type: 'image', // 图形元素类型 id: 'logo', // 更新或删除图形元素时指定更新哪个图形元素,如果不需要用可以忽略。 left: '12%', // 根据父元素进行定位 (居中) 10% bottom: '0', // 根据父元素进行定位 (0%), 如果bottom的值是 0,也可以删除该bottom属性值。 z: 0, // 层叠 bounding: 'all', // 决定此图形元素在定位时,对自身的包围盒计算方式 style: { image: require('../assets/image/pie-bg.png'), // 如果线上地址,必须是https开头的图片路径地址 width: 150, height: 110, }, }, ], xAxis3D: { min: -1, max: 1, }, yAxis3D: { min: -1, max: 1, }, zAxis3D: { min: -1.3, max: 1.3, }, grid3D: { show: false, boxHeight: 25, // 饼图厚度 left: '-15%', top: '-15%', viewControl: { // 3d效果可以放大、旋转等,请自己去查看官方配置 alpha: 40, distance: 280, //调整视角到主体的距离,类似调整zoom rotateSensitivity: 0, zoomSensitivity: 0, panSensitivity: 0, autoRotate: false, // 控制是否自动旋转 // autoRotateSpeed: 5, // autoRotateAfterStill: 10 }, }, series: series, } return option },
转自ECharts作品集 感谢里面大佬的分享
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。