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"
}
}
},
}]
}