如何在d3.js中的文本后面放置矩形

时间:2017-02-14 12:05:45

标签: javascript d3.js gis

我很难在d3.js中将矩形放在文本后面作为背景。我读到为了做到这一点,你必须附加到相同的g元素,但在我的情况下,它不会那样工作。 我的代码:plunker

var urls = [{
  "wor": "Nordmerika",
  "number": "10.9",
  "lon": "-100.33",
  "lat": "47.61"
}, {
  "wor": "Latinamerika",
  "number": "14.2",
  "lon": "-56.62",
  "lat": "-8.53"
}, {
  "wor": "Afrika",
  "number": "51.8",
  "lon": "24.5085",
  "lat": "8.7832"
}, {
  "wor": "Asien",
  "number": "27.5",
  "lon": "104.238281",
  "lat": "34.51561"
}, {
  "wor": "GUS | Russland",
  "number": "3.4",
  "lon": "62.753906",
  "lat": "47.923705"
}, {
  "wor": "Europa | MSOE",
  "number": "10.9",
  "lon": "15.2551",
  "lat": "54.526"
}]

//starting map
var margin = {
    top: 10,
    left: 10,
    bottom: 10,
    right: 10
  },
  width = parseInt(d3.select('#map').style('width')),
  width = width - margin.left - margin.right,
  mapRatio = .5,
  height = width * mapRatio;

//Map projection
var projection = d3.geo.equirectangular()
  .scale(width / 5.8)
  .translate([width / 2, height / 2]) //translate to center the map in view

//Generate paths based on projection
var path = d3.geo.path()
  .projection(projection);

//Create an SVG
var svg = d3.select("#map")
  .append("svg")
  .attr("viewBox", "0 0 " + width + " " + height)
  .attr("preserveAspectRatio", "xMinYMin");

//Group for the map features
var features = svg.append("g")
  .attr("class", "features");

var labelWidths = [];

d3.json("countries.topojson", function(error, geodata) {
  if (error) return console.log(error); //unknown error, check the console

  var layerOne = svg.append("g");
  var layerTwo = svg.append("g");
  var layerThree = svg.append("g");

  //Create a path for each map feature in the data
  features.selectAll("path")
    .data(topojson.feature(geodata, geodata.objects.subunits).features) //generate features from TopoJSON
    .enter()
    .append("path")
    .attr("d", path)
    .on("click", clicked)
    .style('fill', '#cdd5db')
    .style('stroke', '#ffffff')
    .style('stroke-width', '0.5px')
    .on('mouseover', function(d, i) {
      d3.select(this).style('stroke-width', '2px');
    })
    .on('mouseout', function(d, i) {
      d3.select(this).style('stroke-width', '0.5px');
    });


  var bubbles = layerOne.attr("class", "bubble")
    .selectAll("circle")
    .data(urls)
    .enter()
    .append("circle")
    .attr("cx", function(d, i) {
      return projection([d.lon, d.lat])[0];
    })
    .attr("cy", function(d, i) {
      return projection([d.lon, d.lat])[1];
    })
    .attr("r", function(d) {
      if (width >= 1000) {
        return (d.number)
      } else {
        return d.number
      }

    })
    .style('fill', function(d) {
      if (d.wor == 'Afrika') {
        return '#dc0f6e'
      } else {
        return '#3e3e3e'
      }
    });



  var text = layerTwo
    .attr('class', 'text')
    .selectAll('text')
    .data(urls)
    .enter()
    .append('text')
    .attr('x', function(d, i) {
      if (d.number < 10) {
        return projection([d.lon, d.lat])[0] + 60;
      } else {
        return projection([d.lon, d.lat])[0]
      }

    })
    .attr('y', function(d, i) {
      return projection([d.lon, d.lat])[1];
    })
    .text(function(d) {
      return d.number;
    })
    .attr("dy", function(d) {
      if (this.getBBox().width > d.number * 3) {
        return '-2em'
      } else {
        return "0.3em"
      }
    })
    .attr("text-anchor", "middle")
    .style('fill', '#fff')
    .style('font-weight', 'bold')
    .style('font-size', '1em');


  var labels = layerThree
    .attr('class', 'labels')
    .selectAll('text')
    .data(urls)
    .enter()
    .append('text')
    .attr('x', function(d, i) {
      return projection([d.lon, d.lat])[0] + 60;

    })[plunker][1]
    .attr('y', function(d, i) {
      return projection([d.lon, d.lat])[1];
    })

  .attr("text-anchor", "middle")
    .text(function(d) {
      return d.wor;
    })
    .attr('dy', function(d) {
      labelWidths.push(this.getBBox().width)
      var radius = d.number * 2
      if (radius > 10) {
        return d.number * 4;
      } else {
        return '-0.5em'
      }
    })
    .style('font-size', '1em')
    .style('font-weight', 'bold');

  var rect = layerThree
    .attr('class', 'rectlabels')
    .selectAll('rect')
    .data(urls)
    .enter()
    .append('rect')
    .attr('x', function(d, i) {
      return projection([d.lon, d.lat])[0] + 60;
    })
    .attr('y', function(d, i) {
      return projection([d.lon, d.lat])[1];
    })
    .attr('dy', function(d) {
      return '1em'
    })

  .style('fill', '#ffffff')
    .attr('width', function(d, i) {
      return labelWidths[i] / 10 + 'em'
    })
    .attr('height', '1em');

  function clicked(d, i) {

  }

});

1 个答案:

答案 0 :(得分:0)

看起来矩形与文本没有正确对齐,原因有两个:

  • 您的<text>锚定在中间,<rects>锚定在<rect>的左上角。
  • 您在dy元素上以不同的金额设置<text>,这会将文字向下移动该距离,但所有<text>元素都有dy=1em,所以他们没有减少相同数量。

我建议您创建6个<g>元素,每个标签一个,每个元素包含<rect>元素和<text>元素。那么你只需要设置x&amp; <{1}}元素上的y属性,它的子节点应该保持在一起。

我认为您还应该尝试删除<g>属性,而是将text-anchor元素向左移动一半宽度(<g>)以使其在坐标上居中。由于您的宽度为(labelWidths[i] / 10) / 2单位,因此您可能需要以像素为单位进行相应调整。

尝试不使用em元素,看看这是否有助于垂直对齐。