最近做的一个d3地图工作,需要是画出中国地图,在指定区域显示亮色并在指定城市显示光晕动画,做下笔记备忘。

标几个将来可能会再次遇到的需要点:

地图中指定区域设置色彩

通过.style(fill) 回调返回自定义填充色,指定区域上的text也可通过此办法自定义色彩

let gmap = svg.append("g")gmap.selectAll("path")      .data(mapdata)      .enter()      .append("path")      .style("fill", (d, i, a) => {        if (Object.keys(cityordinate).includes(d.properties.name)) {          return 'rgba(29, 223, 201, 1)'        }        return "rgba(62, 241, 230, .4)"      });

光晕动画

用d3.interval 设置circle的大小和透明度;d3.scaleLinear设置映射参数和对应值的区间,用时间差取余作为参数造成3000毫秒一周期,绘制光晕成果

var scale = d3.scaleLinear();    scale.domain([0, 1800, 3000])//设置淡>亮>淡的趋势      .range([0, 0.9, 0]);    var scale2 = d3.scaleLinear();    scale2.domain([0, 3000])      .range([0, 8]);//设置从小变大的趋势    var start = Date.now(); this.dInter = d3.interval(function () {      var s1 = scale((Date.now() - start) % 3000);      var s2 = scale2((Date.now() - start) % 3000) < 0 ? -scale2((Date.now() - start) % 3000) : scale2((Date.now() - start) % 3000);      gmap.select("circle#hangzhouC")        .attr("fill-opacity", s1)        .attr("r", s2)      marker.select("circle#markerC")        .attr("fill-opacity", s1)        .attr("r", s2)    }, 100);

画两点射线

  • 调用之前建设的project()办法,将起点经纬度转换为平面坐标。
  • 计算终点(长沙)和起点之前的间隔,做为线条长度和动画工夫参数。
  • 在线条上绘制一个圆形标记,并实现从终点到起点的挪动动画。
  • 标记挪动到起点后,即删除,节俭资源。
// 创立办法,输出data坐标,绘制发射线,此处按需要把连线stroke设为通明    let mline = svg.append("g").attr("id", "moveto").attr("stroke", "rgba(255, 192, 67, 0)").attr("stroke-width", 1.5).attr("fill", "#FFC043");    var moveto = function (city, data) {      var pf = { x: projection([120.21, 30.25])[0], y: projection([120.21, 30.25])[1] };      var pt = { x: projection(data)[0], y: projection(data)[1] };      var distance = Math.sqrt((pt.x - pf.x) ** 2 + (pt.y - pf.y) ** 2);            mline.append("line")        .attr("x1", pf.x)        .attr("y1", pf.y)        .attr("x2", pt.x)        .attr("y2", pt.y)        .attr("marker-end", "url(#pointer)") //设置线尾标识款式        // .style("stroke-dasharray", " " + distance + ", " + distance + " ") //可设置虚线然而没有画线成果了        .transition()        .duration(distance * 30) //继续时长        .styleTween("stroke-dashoffset", function () {          return d3.interpolateNumber(distance, 0);        });      mline.append("circle")        .attr("cx", pf.x)        .attr("cy", pf.y)        .attr("r", 2)        .transition()        .duration(distance * 30)        .attr("transform", "translate(" + (pt.x - pf.x) + "," + (pt.y - pf.y) + ")")        .remove(); //射线头的款式,射线达到指标后移出线头的圆    };

残缺的代码

initMap = async () => {    let cityordinate = await this.getAllJoinMedical() //获取已入驻城市数据    var width = 750.43 * screenWidth / 1920, height = screenWidth <= minWidth ? 620 * screenHeight / 1080 : 690 * screenHeight / 1080;  // 定义SVG宽高    let svg    if (!this.svg) { //因为父组件会刷新,这里避免画出第二个地图      svg = d3.select(".fxmap")        .append("svg")        .attr("width", width)        .attr("height", height)    } else {      svg = this.svg    }    const defs = svg.append("defs"); //插入defs    const linearGradient = defs //defs中插入<linearGradient>      .append("linearGradient")      .attr("id", "gradient"); //设置对应id    linearGradient //linearGradient中插入stop元素      .append("stop")      .attr("offset", "0%") //设置坡度,下同      .attr("stop-color", '#60F3F9');//设置对应色彩,下同    linearGradient //linearGradient中插入stop元素      .append("stop")      .attr("offset", "20%") //设置坡度,下同      .attr("stop-color", '#60F3F9');//设置对应色彩,下同    linearGradient //linearGradient中插入stop元素      .append("stop")      .attr("offset", "98%") //设置坡度,下同      .attr("stop-color", '#19B2BC');//设置对应色彩,下同    const lineDefs = svg.append("defs"); //插入defs    const linearGradient2 = lineDefs //defs中插入<linearGradient>      .append("linearGradient")      .attr("id", "lineGradient"); //设置对应id    linearGradient2 //linearGradient中插入stop元素      .append("stop")      .attr("offset", "0%") //设置坡度,下同      .attr("stop-color", '#60F3F9');//设置对应色彩,下同    linearGradient2 //linearGradient中插入stop元素      .append("stop")      .attr("offset", "20%") //设置坡度,下同      .attr("stop-color", '#60F3F9');//设置对应色彩,下同    linearGradient2 //linearGradient中插入stop元素      .append("stop")      .attr("offset", "98%") //设置坡度,下同      .attr("stop-color", '#19B2BC');//设置对应色彩,下同    this.svg = svg    let gmap2 = svg.append("g").attr("id", "map2") //因为南海诸岛面积小不显色,边框又和背景色彩雷同,故重建画布用填充色画边框,将南海数据抽出填进去      .attr("stroke", 'rgba(62, 241, 230, .4)').attr("stroke-width", 1)      .attr("fill", "rgba(62, 241, 230, .4)");    let gmap = svg.append("g").attr("id", "map")      .attr("stroke", 'rgba(4, 23, 62, .6)').attr("stroke-width", 1)    // var projection = d3.geoEquirectangular()    var projection = d3.geoMercator() //不同投影函数成果不同      .center([465, 395])  // 指定投影核心,留神[]中的是经纬度      .scale(height - 50) //依据什么进行缩放      .translate([width / 2 + 17, screenWidth <= minWidth ? height / 2 - 10 : height / 2 - 45]); //图形在画布中偏移设置    // var projection = d3    // .geoStereographic()//球形投影    // .center([465, 395])  // 指定投影核心,留神[]中的是经纬度    //   .scale(height)    //   // .clipAngle(180)    // .translate([width / 2, height / 2+20])    var path = d3.geoPath().projection(projection);    let marker = svg.append("defs")      .append("marker")      .append("marker")      .attr("id", "pointer")      .attr("viewBox", "0 0 24 24")    // 可见范畴      .attr("markerWidth", "24")        // 标记宽度      .attr("markerHeight", "24")       // 标记高度      .attr("orient", "auto")         //      .attr("markerUnits", "strokeWidth") // 随连接线宽度进行缩放      .attr("refX", "6")              // 连接点坐标      .attr("refY", "6")    // 绘制标记核心圆    marker.append("circle")      .attr("cx", "6")      .attr("cy", "6")      .attr("r", "2")      .attr("fill", "#FFC043");    // 绘制标记外圆,之后在timer()中增加闪动成果    marker.append("circle")      .attr("id", "markerC")      .attr("cx", "6")      .attr("cy", "6")      .attr("r", "0")      .attr("fill-opacity", "0")      .attr("fill", "#FFC043")    // 记录杭州坐标    var hangzhou = projection([120.219375416, 30.2592444615]);    // 读取地图数据,并绘制中国地图    let mapdata = [];    // 读取地图数据    mapdata = chinaJson.features;    let mapdata2 = chinaJson2.features;    // 绘制地图    gmap2.selectAll("path") //画行政区边界      .data(mapdata2)      .enter()      .append("path")      .attr("d", path)      .style("fill", (d, i, a) => {        if (Object.keys(cityordinate).includes(d.properties.name)) { //已入驻地图显示另一种色彩          return 'rgba(29, 223, 201, 1)'        }        return "rgba(62, 241, 230, .4)"      });    gmap.selectAll("path")      .data(mapdata)      .enter()      .append("path")      .attr("d", path)      .style("fill", (d, i, a) => {        if (Object.keys(cityordinate).includes(d.properties.name)) {          return 'rgba(29, 223, 201, 1)'        }        return "rgba(62, 241, 230, .4)"      });    svg      .selectAll('text')      .data(mapdata)      .enter()      .append('text') //显示行政区名      .text((d, i) => {        return d.properties.name      })      .attr('font-size', 14)      .attr("x", function (d) {        let xNum = d.properties.centroid && Number(projection(d.properties.centroid)[0]) - (d.properties.name.length) * 14 / 2 || 0        if (d.properties.name == '香港') return xNum + 20        return xNum      })      .attr("y", function (d) {        let yNum = d.properties.centroid && projection(d.properties.centroid)[1] || 0        if (d.properties.name == '内蒙古') return yNum + 20        if (d.properties.name == '澳门') return yNum + 12        return yNum      })      .attr("stroke-width", 0)      .style('fill', (d, i, a) => {        if (Object.keys(cityordinate).includes(d.properties.name)) { return 'rgba(3, 2, 27, 1)' }        return "rgba(29, 223, 201, 1)"      })        // 标记杭州    gmap.append("circle").attr("id", "hangzhou")      .attr("cx", hangzhou[0])      .attr("cy", hangzhou[1])      .attr("r", "4")      .attr("fill", "#FFC043")    gmap.append("circle").attr("id", "hangzhouC")      .attr("cx", hangzhou[0])      .attr("cy", hangzhou[1])      .attr("r", "0")      .attr("stroke-width", "2")      .attr("fill-opacity", "0")      .attr("fill", "#FFC043")      .attr("stroke-opacity", 0)      .attr("stroke", "#FFC043")    // 创立办法,输出data坐标,绘制发射线,此处按需要把连线stroke设为通明    let mline = svg.append("g").attr("id", "moveto").attr("stroke", "rgba(255, 192, 67, 0)").attr("stroke-width", 1.5).attr("fill", "#FFC043");    var moveto = function (city, data) {      var pf = { x: projection([120.21, 30.25])[0], y: projection([120.21, 30.25])[1] };      var pt = { x: projection(data)[0], y: projection(data)[1] };      var distance = Math.sqrt((pt.x - pf.x) ** 2 + (pt.y - pf.y) ** 2);            mline.append("line")        .attr("x1", pf.x)        .attr("y1", pf.y)        .attr("x2", pt.x)        .attr("y2", pt.y)        .attr("marker-end", "url(#pointer)") //设置线尾标识款式        // .style("stroke-dasharray", " " + distance + ", " + distance + " ") //可设置虚线然而没有画线成果了        .transition()        .duration(distance * 30) //继续时长        .styleTween("stroke-dashoffset", function () {          return d3.interpolateNumber(distance, 0);        });      mline.append("circle")        .attr("cx", pf.x)        .attr("cy", pf.y)        .attr("r", 2)        .transition()        .duration(distance * 30)        .attr("transform", "translate(" + (pt.x - pf.x) + "," + (pt.y - pf.y) + ")")        .remove(); //射线头的款式,射线达到指标后移出线头的圆    };    gmap2      .selectAll('text')      .data(mapdata2)      .enter()      .append('text')      .text((d, i) => {        return d.properties.name      })      .attr('font-size', 14)      .attr("x", function (d) {        let xNum = d.properties.centroid && Number(projection(d.properties.centroid)[0]) - (d.properties.name.length) * 14 / 2 || 0        return xNum      })      .attr("y", function (d) {        let yNum = d.properties.centroid && projection(d.properties.centroid)[1] || 0        return yNum + 5      })      .attr("stroke-width", 0)      .style('fill', (d, i, a) => {        if (Object.keys(cityordinate).includes(d.properties.name)) { return 'rgba(3, 2, 27, 1)' }        return "rgba(29, 223, 201, 1)"      })    // .style('fill', 'rgba(29, 223, 201, 1)')    var scale = d3.scaleLinear(); //映射函数    scale.domain([0, 1800, 3000])//映射区间,参数传入1800时返回0.9,设置淡>亮>淡的趋势      .range([0, 0.9, 0]);    var scale2 = d3.scaleLinear();    scale2.domain([0, 3000])      .range([0, 8]);//设置从小变大的趋势    var start = Date.now();    this.dInter = d3.interval(function () {      var s1 = scale((Date.now() - start) % 3000); //三秒为周期由小到大循环      var s2 = scale2((Date.now() - start) % 3000) < 0 ? -scale2((Date.now() - start) % 3000) : scale2((Date.now() - start) % 3000);      gmap.select("circle#hangzhouC")        .attr("fill-opacity", s1)        .attr("r", s2)      marker.select("circle#markerC")        .attr("fill-opacity", s1)        .attr("r", s2)    }, 100);    for (var key in cityordinate) {      if (key == '杭州') continue      moveto(key, cityordinate[key]); //发射线    };  }

技术过程借鉴:无鱼二饼