외부 글꼴을 사용할 때 일관되지 않은 d3-legend 위치 지정

Nov 13 2020

내가 사용하고 D3 - 전설을 D3지도에 대한 전설을 만들 수 있습니다. 맵은 외부에서로드 된 글꼴을 사용하며 드롭 다운 메뉴를 사용할 때 전체 맵이 제거되고 다시 그려집니다.

d3-legend를 사용하면 범례 항목 사이의 간격을 사용하여 제어 할 수 .shapePadding있지만 제 경우에는 일관되게 적용되지 않는 것 같습니다. 맵을 다시 그린 후 간격이 변경됩니다. 아래 스크린 샷을 참조하십시오.

NB : 여기서 문제가 일관되지 않게 발생하고 있음을 발견했습니다. 코드를 약간만 변경하면 문제가 해결되는 것 같습니다. 동일한 코드를 로컬에서 실행할 때 문제가 일관되게 발생합니다.

function drawMap1() {

  // Set the size and margins of the svg
  var margin = {
      top: 60,
      right: 10,
      bottom: 40,
      left: 10
    },
    width = 700 - margin.left - margin.right,
    height = 780 - margin.top - margin.bottom;

  d3.selectAll("#map1 > *").remove();

  // Create the svg element
  var svg = d3
    .select("#map1")
    .append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  // Set the colour scale
  var quantize = d3.scaleQuantize(); // domain and range are set as part of loading dataset

  quantize
    .domain([0.6, 1])
    .range(d3.quantize(d3.interpolate("rgb(230,0,126)", "rgb(45,170,225)"), 4));

  // Add a legend
  svg.append("g")
    .attr("class", "legendQuant")

  var legend = d3.legendColor()
    .shapeWidth(30)
    .orient("vertical")
    .shapePadding(2)
    .scale(quantize)
    .labelFormat(d3.format(".0%"));

  svg.select(".legendQuant")
    .call(legend);
}

function updateMap1DateSelector(value) {
  drawMap1()
}

drawMap1()
.d3-map svg {
  font-family: 'Open Sans';
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8" />
  <link rel="icon" href="data:,">
  <link rel='stylesheet' href='styles.css' type='text/css' media='screen,projection' />
  <link rel="stylesheet" href="//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700&amp;lang=en" />
</head>

<body>
  <select id="date-selector-map1" class="dropdown-selector" onchange="updateMap1DateSelector(value)" style="float: right;">
    <option value='20201015'>15 October 2020</option>
    <option selected value='20201105'>5 November 2020</option>
  </select>
  <div id="map1" class="d3-map"></div>
  <script src="https://d3js.org/d3.v4.min.js"></script>
  <script src='https://cdnjs.cloudflare.com/ajax/libs/d3-legend/2.25.6/d3-legend.min.js'></script>
  <script src='legend.js'></script>
</body>

다음과 같은 코드를 사용하여 범례 항목의 위치를 ​​하드 코딩 할 수 있습니다.

d3.select(".legendCells")
    .selectAll(".cell")
    .attr("transform", function (d, i) {
        return "translate(" + 0 + "," + i*19.5 + ")"
    });

그러나 실제로 문제를 일으키는 원인을 이해하는 것이 좋을 것입니다. 관련 CSS 줄을 제거하면 문제가 제거되므로 외부 글꼴 사용과 관련이 있다고 생각합니다. 그리고 덜 해키가 적은 솔루션이 무엇인지 아는 것이 좋습니다.

답변

1 RubenHelsloot Nov 20 2020 at 16:46

맞습니다. 도형을 그릴 때마다 다음 코드가 실행됩니다.

const cellSize = textSize.map((d, i) =>
  Math.max(d.height, shapeSize[i].height)
)

여기 textSize에는 텍스트 노드 shapeSize의 경계 상자와 사각형 또는 견본의 경계 상자가 있습니다. 처음에는 글꼴이 아직로드되지 않았을 때가 textSize.height16 픽셀이고 나중에는 23 픽셀입니다.

이런 일이 발생할 수 있다는 것을 알고 있다면 글꼴이로드 될 때까지 자바 스크립트를 지연 시킬 수 있습니다 .

범례가 단색 배경에 그려진 경우 직사각형 크기를 23px로 늘리고 배경 색상과 일치하는 두꺼운 획을 추가 할 수도 있습니다.

function drawMap1() {

  // Set the size and margins of the svg
  var margin = {
      top: 60,
      right: 10,
      bottom: 40,
      left: 10
    },
    width = 700 - margin.left - margin.right,
    height = 780 - margin.top - margin.bottom;

  d3.selectAll("#map1 > *").remove();

  // Create the svg element
  var svg = d3
    .select("#map1")
    .append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  // Set the colour scale
  var quantize = d3.scaleQuantize(); // domain and range are set as part of loading dataset

  quantize
    .domain([0.6, 1])
    .range(d3.quantize(d3.interpolate("rgb(230,0,126)", "rgb(45,170,225)"), 4));

  // Add a legend
  svg.append("g")
    .attr("class", "legendQuant")

  var legend = d3.legendColor()
    .shapeWidth(30)
    .shapeHeight(23)
    .orient("vertical")
    .shapePadding(2)
    .scale(quantize)
    .labelFormat(d3.format(".0%"));

  svg.select(".legendQuant")
    .call(legend);
}

function updateMap1DateSelector(value) {
  drawMap1()
}

drawMap1()
.d3-map svg {
  font-family: 'Open Sans';
}

.swatch {
  stroke: white;
  stroke-width: 8px;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8" />
  <link rel="icon" href="data:,">
  <link rel='stylesheet' href='styles.css' type='text/css' media='screen,projection' />
  <link rel="stylesheet" href="//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700&amp;lang=en" />
</head>

<body>
  <select id="date-selector-map1" class="dropdown-selector" onchange="updateMap1DateSelector(value)" style="float: right;">
    <option value='20201015'>15 October 2020</option>
    <option selected value='20201105'>5 November 2020</option>
  </select>
  <div id="map1" class="d3-map"></div>
  <script src="https://d3js.org/d3.v4.min.js"></script>
  <script src='https://cdnjs.cloudflare.com/ajax/libs/d3-legend/2.25.6/d3-legend.js'></script>
  <script src='legend.js'></script>
</body>