本来想用popups然后用echarts拿popups里的自定义DOM初始化饼图,方法确实可行但是会影响下方图层的点击事件,移入事件等(popup会遮挡图层)。下方dome可解决遮挡问题,可复制替换自己mapbox的tk直接运行,各位大佬如有更好的方法欢迎分享~~~
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3.js在mapbox上添加饼图</title>
<link href="https://cdn.bootcdn.net/ajax/libs/mapbox-gl/2.10.0/mapbox-gl.css" rel="stylesheet">
<script src="https://cdn.bootcdn.net/ajax/libs/mapbox-gl/2.10.0/mapbox-gl.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/d3/7.9.0/d3.js"></script>
<style>
body {
margin: 0;
padding: 0;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
.pie-chart {
position: absolute;
}
.tooltip {
position: absolute;
padding: 5px;
background-color: white;
border: 1px solid #ccc;
border-radius: 3px;
pointer-events: none;
opacity: 0;
transition: opacity 0.2s;
z-index: 1000;
/* 默认设置一个较高的 z-index */
}
</style>
</head>
<body>
<div id="map"></div>
<script>
mapboxgl.accessToken = '你的mapbox tk';
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v11',
center: [
114.21988636442639,
22.6870220693995
],
zoom: 10,
pitch: 45
});
// 用于存储饼图标记
const districtMarkers = [];
const usageColorList = [
{ name: '用途1', color: '#6B8BEB' },
{ name: '用途2', color: '#3FD4BA' },
{ name: '用途3', color: '#F9D04B' },
{ name: '用途4', color: '#6DC2F1' },
];
// 数据结构 可根据自己需求改
const pieData = [
{
"coordinates": [ // 饼图中心
114.3573332397592,
22.69108655884441
],
"data": [ // 该点的数据
{
"label": "用途1",
"value": 101,
"pie": {
"用途1": 101,
"用途2": 22,
"name": "区域1",
"x": 114.3573332397592,
"y": 22.69108655884441,
"全部": 236,
"用途3": 75,
"用途4": 38,
"key": "index1"
}
},
{
"label": "用途2",
"value": 22,
"pie": {
"用途1": 101,
"用途2": 22,
"name": "区域1",
"x": 114.3573332397592,
"y": 22.69108655884441,
"全部": 236,
"用途3": 75,
"用途4": 38,
"key": "index1"
}
},
{
"label": "用途3",
"value": 75,
"pie": {
"用途1": 101,
"用途2": 22,
"name": "区域1",
"x": 114.3573332397592,
"y": 22.69108655884441,
"全部": 236,
"用途3": 75,
"用途4": 38,
"key": "index1"
}
},
{
"label": "用途4",
"value": 38,
"pie": {
"用途1": 101,
"用途2": 22,
"name": "区域1",
"x": 114.3573332397592,
"y": 22.69108655884441,
"全部": 236,
"用途3": 75,
"用途4": 38,
"key": "index1"
}
}
],
"text": [
"区域1",
236,
"XX数量"
]
},
{
"coordinates": [
114.14335649496182,
22.577424965775723
],
"data": [
{
"label": "用途1",
"value": 1130,
"pie": {
"用途1": 1130,
"用途2": 428,
"name": "区域2",
"x": 114.14335649496182,
"y": 22.577424965775723,
"全部": 2473,
"用途3": 696,
"用途4": 219,
"key": "index2"
}
},
{
"label": "用途2",
"value": 428,
"pie": {
"用途1": 1130,
"用途2": 428,
"name": "区域2",
"x": 114.14335649496182,
"y": 22.577424965775723,
"全部": 2473,
"用途3": 696,
"用途4": 219,
"key": "index2"
}
},
{
"label": "用途3",
"value": 696,
"pie": {
"用途1": 1130,
"用途2": 428,
"name": "区域2",
"x": 114.14335649496182,
"y": 22.577424965775723,
"全部": 2473,
"用途3": 696,
"用途4": 219,
"key": "index2"
}
},
{
"label": "用途4",
"value": 219,
"pie": {
"用途1": 1130,
"用途2": 428,
"name": "区域2",
"x": 114.14335649496182,
"y": 22.577424965775723,
"全部": 2473,
"用途3": 696,
"用途4": 219,
"key": "index2"
}
}
],
"text": [
"区域2",
2473,
"XX数量"
]
},
{
"coordinates": [
113.9414783873699,
22.558112817122733
],
"data": [
{
"label": "用途1",
"value": 1000,
"pie": {
"用途1": 1000,
"用途2": 294,
"name": "区域3",
"x": 113.9414783873699,
"y": 22.558112817122733,
"全部": 2388,
"用途3": 766,
"用途4": 328,
"key": "index3"
}
},
{
"label": "用途2",
"value": 294,
"pie": {
"用途1": 1000,
"用途2": 294,
"name": "区域3",
"x": 113.9414783873699,
"y": 22.558112817122733,
"全部": 2388,
"用途3": 766,
"用途4": 328,
"key": "index3"
}
},
{
"label": "用途3",
"value": 766,
"pie": {
"用途1": 1000,
"用途2": 294,
"name": "区域3",
"x": 113.9414783873699,
"y": 22.558112817122733,
"全部": 2388,
"用途3": 766,
"用途4": 328,
"key": "index3"
}
},
{
"label": "用途4",
"value": 328,
"pie": {
"用途1": 1000,
"用途2": 294,
"name": "区域3",
"x": 113.9414783873699,
"y": 22.558112817122733,
"全部": 2388,
"用途3": 766,
"用途4": 328,
"key": "index3"
}
}
],
"text": [
"区域3",
2388,
"XX数量"
]
},
{
"coordinates": [
114.02988636442639,
22.6870220693995
],
"data": [
{
"label": "用途1",
"value": 474,
"pie": {
"用途1": 474,
"用途2": 74,
"name": "区域4",
"x": 114.02988636442639,
"y": 22.6870220693995,
"全部": 932,
"用途3": 273,
"用途4": 111,
"key": "index4"
}
},
{
"label": "用途2",
"value": 74,
"pie": {
"用途1": 474,
"用途2": 74,
"name": "区域4",
"x": 114.02988636442639,
"y": 22.6870220693995,
"全部": 932,
"用途3": 273,
"用途4": 111,
"key": "index4"
}
},
{
"label": "用途3",
"value": 273,
"pie": {
"用途1": 474,
"用途2": 74,
"name": "区域4",
"x": 114.02988636442639,
"y": 22.6870220693995,
"全部": 932,
"用途3": 273,
"用途4": 111,
"key": "index4"
}
},
{
"label": "用途4",
"value": 111,
"pie": {
"用途1": 474,
"用途2": 74,
"name": "区域4",
"x": 114.02988636442639,
"y": 22.6870220693995,
"全部": 932,
"用途3": 273,
"用途4": 111,
"key": "index4"
}
}
],
"text": [
"区域4",
932,
"XX数量"
]
},
{
"coordinates": [
114.26641888001215,
22.598780315920703
],
"data": [
{
"label": "用途1",
"value": 353,
"pie": {
"用途1": 353,
"用途2": 112,
"name": "区域5",
"x": 114.26641888001215,
"y": 22.598780315920703,
"全部": 748,
"用途4": 87,
"用途3": 196,
"key": "index5"
}
},
{
"label": "用途2",
"value": 112,
"pie": {
"用途1": 353,
"用途2": 112,
"name": "区域5",
"x": 114.26641888001215,
"y": 22.598780315920703,
"全部": 748,
"用途4": 87,
"用途3": 196,
"key": "index5"
}
},
{
"label": "用途3",
"value": 196,
"pie": {
"用途1": 353,
"用途2": 112,
"name": "区域5",
"x": 114.26641888001215,
"y": 22.598780315920703,
"全部": 748,
"用途4": 87,
"用途3": 196,
"key": "index5"
}
},
{
"label": "用途4",
"value": 87,
"pie": {
"用途1": 353,
"用途2": 112,
"name": "区域5",
"x": 114.26641888001215,
"y": 22.598780315920703,
"全部": 748,
"用途4": 87,
"用途3": 196,
"key": "index5"
}
}
],
"text": [
"区域5",
748,
"XX数量"
]
},
{
"coordinates": [
114.49800579316843,
22.579158819024364
],
"data": [
{
"label": "用途1",
"value": 97,
"pie": {
"用途1": 97,
"用途2": 20,
"name": "区域6",
"x": 114.49800579316843,
"y": 22.579158819024364,
"全部": 181,
"用途3": 53,
"用途4": 11,
"key": "index6"
}
},
{
"label": "用途2",
"value": 20,
"pie": {
"用途1": 97,
"用途2": 20,
"name": "区域6",
"x": 114.49800579316843,
"y": 22.579158819024364,
"全部": 181,
"用途3": 53,
"用途4": 11,
"key": "index6"
}
},
{
"label": "用途3",
"value": 53,
"pie": {
"用途1": 97,
"用途2": 20,
"name": "区域6",
"x": 114.49800579316843,
"y": 22.579158819024364,
"全部": 181,
"用途3": 53,
"用途4": 11,
"key": "index6"
}
},
{
"label": "用途4",
"value": 11,
"pie": {
"用途1": 97,
"用途2": 20,
"name": "区域6",
"x": 114.49800579316843,
"y": 22.579158819024364,
"全部": 181,
"用途3": 53,
"用途4": 11,
"key": "index6"
}
}
],
"text": [
"区域6",
181,
"XX数量"
]
},
{
"coordinates": [
113.8574291143083,
22.6842750470609
],
"data": [
{
"label": "用途1",
"value": 1206,
"pie": {
"用途1": 1206,
"用途2": 347,
"name": "区域7",
"x": 113.8574291143083,
"y": 22.6842750470609,
"全部": 5064,
"用途3": 834,
"用途4": 2677,
"key": "index7"
}
},
{
"label": "用途2",
"value": 347,
"pie": {
"用途1": 1206,
"用途2": 347,
"name": "区域7",
"x": 113.8574291143083,
"y": 22.6842750470609,
"全部": 5064,
"用途3": 834,
"用途4": 2677,
"key": "index7"
}
},
{
"label": "用途3",
"value": 834,
"pie": {
"用途1": 1206,
"用途2": 347,
"name": "区域7",
"x": 113.8574291143083,
"y": 22.6842750470609,
"全部": 5064,
"用途3": 834,
"用途4": 2677,
"key": "index7"
}
},
{
"label": "用途4",
"value": 2677,
"pie": {
"用途1": 1206,
"用途2": 347,
"name": "区域7",
"x": 113.8574291143083,
"y": 22.6842750470609,
"全部": 5064,
"用途3": 834,
"用途4": 2677,
"key": "index7"
}
}
],
"text": [
"区域7",
5064,
"XX数量"
]
},
{
"coordinates": [
114.2067663059172,
22.69559735797399
],
"data": [
{
"label": "用途1",
"value": 1206,
"pie": {
"用途1": 1206,
"用途2": 182,
"name": "区域8",
"x": 114.2067663059172,
"y": 22.69559735797399,
"全部": 3283,
"用途3": 849,
"用途4": 1046,
"key": "index8"
}
},
{
"label": "用途2",
"value": 182,
"pie": {
"用途1": 1206,
"用途2": 182,
"name": "区域8",
"x": 114.2067663059172,
"y": 22.69559735797399,
"全部": 3283,
"用途3": 849,
"用途4": 1046,
"key": "index8"
}
},
{
"label": "用途3",
"value": 849,
"pie": {
"用途1": 1206,
"用途2": 182,
"name": "区域8",
"x": 114.2067663059172,
"y": 22.69559735797399,
"全部": 3283,
"用途3": 849,
"用途4": 1046,
"key": "index8"
}
},
{
"label": "用途4",
"value": 1046,
"pie": {
"用途1": 1206,
"用途2": 182,
"name": "区域8",
"x": 114.2067663059172,
"y": 22.69559735797399,
"全部": 3283,
"用途3": 849,
"用途4": 1046,
"key": "index8"
}
}
],
"text": [
"区域8",
3283,
"XX数量"
]
},
{
"coordinates": [
114.04449564558202,
22.54946366732896
],
"data": [
{
"label": "用途1",
"value": 917,
"pie": {
"用途1": 917,
"用途2": 320,
"name": "区域9",
"x": 114.04449564558202,
"y": 22.54946366732896,
"全部": 2169,
"用途3": 743,
"用途4": 189,
"key": "index9"
}
},
{
"label": "用途2",
"value": 320,
"pie": {
"用途1": 917,
"用途2": 320,
"name": "区域9",
"x": 114.04449564558202,
"y": 22.54946366732896,
"全部": 2169,
"用途3": 743,
"用途4": 189,
"key": "index9"
}
},
{
"label": "用途3",
"value": 743,
"pie": {
"用途1": 917,
"用途2": 320,
"name": "区域9",
"x": 114.04449564558202,
"y": 22.54946366732896,
"全部": 2169,
"用途3": 743,
"用途4": 189,
"key": "index9"
}
},
{
"label": "用途4",
"value": 189,
"pie": {
"用途1": 917,
"用途2": 320,
"name": "区域9",
"x": 114.04449564558202,
"y": 22.54946366732896,
"全部": 2169,
"用途3": 743,
"用途4": 189,
"key": "index9"
}
}
],
"text": [
"区域9",
2169,
"XX数量"
]
}
];
// 动态创建 tooltip 元素并添加到 body 中
const tooltip = d3.select('body').append('div').attr('class', 'tooltip');
const markers = []; // 用于存储饼图标记
// 添加饼图
function addPieCharts () {
const colors = []; // 各用扇区途颜色
usageColorList.forEach((e) => {
colors.push(e.color);
});
pieData.forEach((pie) => {
const svg = d3
.create('svg')
.attr('class', 'pie-chart')
.attr('width', 120)
.attr('height', 120);
const radius = Math.min(120, 120) / 2;
const g = svg.append('g').attr('transform', `translate(${radius},${radius})`);
// 白色实心圆
g.append('circle').attr('r', radius).attr('fill', 'white');
const color = d3
.scaleOrdinal()
.domain(pie.data.map((d) => d.label))
.range(colors);
const pieGenerator = d3.pie().value((d) => d.value);
const arc = d3
.arc()
.innerRadius(radius * 0.6) // 白色饼图半径
.outerRadius(radius);
g.selectAll('path')
.data(pieGenerator(pie.data))
.enter()
.append('path')
.attr('d', arc)
.attr('fill', (d) => color(d.data.label))
.on('mouseover', (event, d) => {
let _ = '';
// 自定义tooltip dom结构和样式 样式可以不写内联提取出去
usageColorList.forEach((use) => {
_ += `
<div style="display: flex; flex-direction: row; align-items: center;margin-top: 17px;">
<div style="width: 10px; height: 10px; background: ${use.color
};border-radius: 50%;"></div>
<div style="font-family: Microsoft YaHei, Microsoft YaHei;font-weight: 400;font-size: 16px;color: #5677A8; margin: 0 30px 0 10px">${use.name
}</div>
<div style="font-family: D-DIN, D-DIN;font-weight: 700;font-size: 20px;color: #5677A8;">${d.data.pie[use.name]
}</div>
</div>
`;
});
const html = `
<div style="padding: 16px 20px;">
<div style="font-family: Microsoft YaHei, Microsoft YaHei; font-weight: 700; font-size: 16px; color: #1D70EB; ma">XX数量(个)</div>
${_}
</div>
`;
tooltip
.style('opacity', 1)
.html(html)
.style('left', `${event.pageX + 10}px`)
.style('top', `${event.pageY + 10}px`);
})
.on('mouseout', () => {
tooltip.style('opacity', 0);
});
// 添加饼图中心文字 三行
const textLines = pie.text;
const fontSizes = ['14px', '22px', '12px']; // 每一行的位置
const lineHeight = 5; // 设置行间距为5px
let currentY = [-14, 8, 24]; // 每一行的位置
const fontWeights = ['normal', 'bold', 'normal']; // 每一行的加粗样式
textLines.forEach((line, index) => {
g.append('text')
.attr('text-anchor', 'middle')
.attr('dy', currentY[index])
.style('font-size', fontSizes[index])
.style('fill', '#1D70EB')
.style('font-weight', fontWeights[index])
.text(line);
});
const marker = new mapboxgl.Marker({ element: svg.node() })
.setLngLat(pie.coordinates)
.addTo(map);
districtMarkers.push(marker);
});
};
// 移除饼图方法
function removePieCharts () {
districtMarkers.forEach(marker => marker.remove());
districtMarkers.length = 0; // 清空标记数组
tooltip.style("opacity", 0); // 隐藏tooltip
}
map.on('load', () => {
addPieCharts();
});
// zoom 大于 13 移除饼图 小于 10 添加饼图
map.on('zoom', () => {
const zoom = map.getZoom();
if (zoom > 13) {
removePieCharts();
} else if (zoom <= 10) {
if (districtMarkers.length === 0) {
addPieCharts();
}
}
});
</script>
</body>
</html>