let data = [ { value: 45, name: "2室1厅1卫" }, { value: 52, name: "3室1厅1卫" }, { value: 30, name: "2室2厅1卫" }, { value: 60, name: "3室2厅1卫" } ]; let color = [ { colorStart: "rgba(7, 166, 255,0)", colorEnd: "rgba(7, 166, 255,0)", solid: 'rgba(7, 166, 255,1)' }, { colorStart: "rgba(255, 255, 255,0)", colorEnd: "rgba(255, 255, 255,0)", solid: 'rgba(255, 255, 255,1)' }, { colorStart: "rgba(255, 208, 118,0)", colorEnd: "rgba(255, 208, 118,0.4)", solid: 'rgba(255, 208, 118,1)' }, { colorStart: "rgba(72, 235, 236,0)", colorEnd: "rgba(72, 235, 236,0.4)", solid: 'rgba(72, 235, 236,1)' } ]; // 无渐变颜色 let colorNoGradient = ["rgba(7, 166, 255,1)", "rgba(255, 255, 255,1)", "rgba(255, 208, 118,1)", "rgba(72, 235, 236,1)"] // 计算总数 let total = data.map(v => v.value).reduce((o, n) => o + n); // 计算每一个data的其实角度和末了角度 θ1和θ2 data.reduce((o, v) => { v.theta1 = o; v.theta2 = o + v.value / total; return v.theta2 }, 0); // 添加渐变 data.forEach((v, i) => { let ops = calc(v.theta1 * 2 * Math.PI, v.theta2 * 2 * Math.PI); if (v.value) v.itemStyle = { color: { type: 'radial', x: ops.center[0], y: ops.center[1], r: ops.radius, colorStops: [{ offset: 0, color: color[i].colorStart }, { offset: 0.32, color: color[i].colorStart }, { offset: 0.95, color: color[i].colorEnd }, { offset: 0.95, color: color[i].solid }, { offset: 1, color: color[i].solid }], globalCoord: false // 缺省为 false } } }) let option = { backgroundColor:'#00266b', grid: { left: 0, // -100, top: 50, bottom: 10, right: 10 // containLabel: true }, tooltip: { trigger: "item", axisPointer: { type: 'shadow', }, backgroundColor: 'rgba(9, 24, 48, 0.5)', borderColor: 'rgba(255,255,255,0.4)', textStyle: { color: '#fff', align: 'left', textAlign: 'left' }, borderWidth: 1, formatter: "{b} : {c} ({d}%)" }, calculable: true, legend: { icon: 'none', show: false, orient: 'vertical', right: '0%', top: '20%', itemGap: 30, itemWidth: 12, itemHeight: 12, selectedMode:false, // 关闭图例选择 textStyle: { color: '#fff', fontSize: 13, rich: { // iconName: { // width: 5, // height: 8, // background: '#fff' // }, name: { color: '#FFF', fontSize: 28, // width: 200, padding: [0, 0, 0, 30], }, value: { color: '#2BDFD4', fontSize: 28, // width: 80, padding: [0, 0, 0, 30] }, percent: { color: '#2BDFD4', fontSize: 28, padding: [0, 0, 0, 30] }, unit: { color: '#ACDCE4', fontSize: 18, padding: [0, 0, 0, 5] } } }, formatter: name => { let obj = data.find(item => item.name === name); let datas = data; let total = 0; let target = obj.value for (let i = 0; i < datas.length; i++) { total += datas[i].value; } const arr = [ `{iconName|}{name|${name}}{value|${obj.value}}{unit|人}{percent|${((target / total) * 100).toFixed(0)}}{unit|%}` ]; return arr.join('') }, data: data.map((dItem,dIndex) => { return { ...dItem, textStyle: { rich: { iconName: { width: 4, height: 16, borderRadius: 5, backgroundColor: colorNoGradient[dIndex], }, percent: { color: colorNoGradient[dIndex], }, } }, } }), }, series: [ { // radius: ["90", "100"], // center: ["40%", "50%"], type: "pie", radius: "10%", hoverAnimation: false, labelLine: { show: false, }, itemStyle: { color: '#c2d7fd' }, tooltip: { show: false }, data: [0] }, { type: "pie", // center: ["40%", "50%"], // radius: ["15%", "16%"], radius: '10%', hoverAnimation: false, labelLine: { show: false, }, itemStyle: { color: '#c2d7fd', opacity: 0.4 }, tooltip: { show: false }, data: [0] }, { type: "pie", // center: ["40%", "50%"], radius: ["71%", "72%"], hoverAnimation: false, labelLine: { show: false, }, itemStyle: { color: '#b7cffc', opacity: 0.4 }, tooltip: { show: false }, data: [0] }, { type: "pie", // center: ["40%", "50%"], radius: ["20%", "80%"], name: '保障房套数分析', roseType: true, zlevel: 10, label: { show: false, formatter: params => { let datas = data; let target; var total = 0; for (let i = 0; i < datas.length; i++) { total += datas[i].value; if (datas[i].name == params.name) { target = datas[i].value; } } let arr = ["{a|" + params.data.name + ' ' + ((target / total) * 100).toFixed(0) + "%}"]; return arr.join("\n"); }, rich: { a: { fontSize: 12, color: "#eef4ff", lineHeight: 50, align: 'left' } }, height: 70, padding: [0, -30], distanceToLabelLine: 0, alignTo: 'labelLine' }, labelLine: { length: 10, length2: 35, lineStyle: { color: "rgba(255,255,255,0.4)", type: "dashed" } }, data } ] }; function calc(theta1, theta2) { let r = 0.5; // 半径是0.5 其实表示0.5个直径 let inner = 0.5; // 里面镂空部分占60% option中radius为[33%, 55%] 55 * 0.6 == 33 let cos = Math.cos; let sin = Math.sin; let PI = Math.PI; let min = Math.min; let max = Math.max; let bottom = 0; let left = 2 * r; let right = 0; // y0: θ1对应的外部点的y值 // y1: θ2对应的外部点的y值 // _y0: θ1对应的内部点的y值 // _y1: θ2对应的内部点的y值 // x0: θ1对应的外部点的x值 // x1: θ2对应的外部点的x值 // _x0: θ1对应的内部点的x值 // _x1: θ2对应的内部点的x值 let y0 = r * (1 - cos(theta1)); let y1 = r * (1 - cos(theta2)); let _y0 = r * (1 - inner * cos(theta1)); let _y1 = r * (1 - inner * cos(theta2)); // 如果这个弧经过θ == PI的点 则bottom = 2PI // bottom用于之后的max计算中 if (theta1 < PI && theta2 > PI) { bottom = 2 * r; } // 计算这几个点的最大最小值 let ymin = min(_y0, _y1, y0, y1); let ymax = max(_y0, _y1, y0, y1, bottom); let x0 = r * (1 + sin(theta1)); let x1 = r * (1 + sin(theta2)); let _x0 = r * (1 + inner * sin(theta1)); let _x1 = r * (1 + inner * sin(theta2)); // 如果这个弧经过θ == PI / 2的点 则right = 2PI if (theta1 < PI / 2 && theta2 > PI / 2) { right = 2 * r; } // 如果这个弧经过θ == PI / 2 * 3的点 则left = 0 if (theta1 < PI / 2 * 3 && theta2 > PI / 2 * 3) { left = 0; } let xmin = min(_x0, _x1, x0, x1, left); let xmax = max(_x0, _x1, x1, x0, right); return { // 计算圆心以及半径 center: [(r - xmin) / (xmax - xmin), (r - ymin) / (ymax - ymin)], radius: r / min(xmax - xmin, ymax - ymin) } }