d3实现地图, 鼠标悬浮显示区域名称
前期准备
- 安装d3.js
npm i d3 --save
- 安装d3-scale-chromatic
npm i d3-scale-chromatic --save
- 安装d3-geo(从球体到平面的投影)
npm i d3-geo --save
- main.js配置
//d3
import * as d3 from 'd3';
import * as geo from 'd3-geo';
import * as d3Color from 'd3-scale-chromatic'
Vue.prototype.$d3 = d3;
Vue.prototype.$geo = geo;
Vue.prototype.$d3Color = d3Color;
- 地图json
点击查看JSON文件
页面布局及样式
<template>
<div class="d3">
<svg id="svg"></svg>
<div id="tooltip"></div>
</div>
</template>
<style scoped>
.d3{
margin: 0 auto;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
#svg {
background: #666;
width: 100%;
height: 100%;
}
#tooltip {
position: absolute;
padding: 3px 5px;
background: #333333;
border: 1px solid #e8e8e8;
color: #33cc99;
font-size: 10px;
border-radius: 4px;
}
</style>
JS准备
<script>
import chinaJson from '@/assets/json/china.json';
export default {
name: "mapOnline",
data() {
return {
svg:undefined,
projection: undefined,
path: undefined
}
},
async mounted(){
this.svg = this.$d3.select('#svg');
const style = getComputedStyle(this.svg.node());
const width = parseInt(style.getPropertyValue('width'), 10);
const height = parseInt(style.getPropertyValue('height'), 10);
// 定义地图的投影
this.projection = this.$geo.geoMercator()
.scale(750)
.center([105, 33])
.translate([width / 2, height / 2]);
// 定义地理路径生成器
this.path = this.$geo.geoPath(this.projection);
// this.getAllProviceJson();
this.drawChinaMap();
},
methods:{
drawChinaMap() {
// 地图路径绘制
this.svg.selectAll('path')
.data(chinaJson.features)
.enter()
.append('path')
.attr('d', this.path)
.attr('fill', (d, i) => {
return 'transparent';
})
.attr('fill-rule', 'evenodd')
.attr('stroke', '#fff')
.attr('stroke-width', 2)
.on('mouseover', (d, i) => {
this.mouseMove(chinaJson.features)
});
// text地理名称
this.svg
.selectAll("text")
.data(chinaJson.features)
.enter()
.append("text")
.attr("x", (d) => {
const position = this.projection(d.properties.centroid || [0, 0]);
return position[0];
})
.attr("y", (d) => {
const position = this.projection(d.properties.centroid || [0, 0]);
return position[1];
})
.text((d) => {
return d.properties.name
})
.attr("text-anchor", "middle")
.attr("dominant-baseline", "middle")
.transition()
.duration(1000)
.delay((d, i) => i * 50)
},
getAllProviceJson(){
chinaJson.features.forEach((obj) => {
if(obj.properties && obj.properties.name){
this.drawProvice(obj.properties.name)
}
})
},
drawProvice(name){
const jsonData = require(`@/assets/json/${name}.json`);
const colors = (i) => this.$d3Color.interpolateGnBu(i);
if (!jsonData || !jsonData.features) {
return;
}
this.svg.append('g')
.attr('id', name)
.selectAll('g')
.data(jsonData.features)
.enter()
.append('g')
.append('path')
.attr('d', this.path)
.attr('fill', (d, i) => {
if(d.properties.name == '泸州市'){
return "transparent"
}else{
return colors(i / 40);
}
})
.attr('fill-rule', 'evenodd')
.attr('id', (d, i) => (d.properties || {}).name)
.attr('stroke', '#ccc')
.attr('stroke-width', 1)
// .on('mouseover', (d, i) => {
// this.mouseMove(jsonData.features, name)
// });
},
mouseMove(jsonData){
var tooltip = this.$d3.select('#tooltip');
const location = this.svg.selectAll('.location')
.data(jsonData)
.enter()
.append('g')
.attr('class', 'location')
.attr('transform', (d) => {
const position = this.projection(d.properties.centroid || [0, 0]);
return 'translate(' + position[0] + ',' + position[1] + ')';
});
location.append('circle')
.attr('r', 4)
.attr('fill', '#e91e63')
.attr('class', 'location')
.on('mouseover', function (d, i) {
return tooltip.style('visibility', 'visible').text(i.properties.name)
})
.on('mousemove', function (d, i) {
return tooltip.style('top', (event.pageY-10)+'px').style('left',(event.pageX+10)+'px')
})
.on('mouseout', function (d, i) {
return tooltip.style('visibility', 'hidden')
})
}
}
};
地图可以设置两个颜色值, 根据value不同, 颜色不同
const maxData = this.mapDataJson.max;
const minData = this.mapDataJson.min;
var palegreen = this.$d3.rgb(2, 175, 102);
var darkgreen = this.$d3.rgb(81, 37, 255);
var color = this.$d3.interpolate(palegreen, darkgreen);
const cliner = this.$d3.scaleLinear().domain([minData, maxData]).range([0, 1]);
//根据value, 不同颜色
svg
.attr("fill", (d) => {
let prov = d.properties.name;
let curProvData = this.mapDataJson.data.find(
(provData) => provData.cityNameCN === prov
);
return color(cliner(curProvData ? curProvData.value : minData));
})
实现效果
传不上去, 就这样吧, 运行起来就看到了