d3.js — повернутая текстовая метка не совпадает точно с точками

#d3.js

Вопрос:

Просто попробуйте построить линейный график с d3.js,повернутая метка оси x и значение текста аннотации в каждой точке выглядят так, как будто они не совпадают в точности с точкой!

Как настроить его в соответствии с точкой?

 test()
function test() {
  var data = `tag,mean
boot_progress_start,10.882000000000001
boot_progress_preload_start,12.677
boot_progress_preload_end,15.962
boot_progress_system_run,16.441
boot_progress_pms_start,17.179
boot_progress_pms_system_scan_start,17.631
boot_progress_pms_scan_end,20.604
boot_progress_pms_ready,20.907
boot_progress_ams_ready,22.793000000000006
boot_progress_enable_screen,25.401
sf_stop_bootanim,25.427
`

  var dataset = d3.csvParse(data);
  dataset.forEach(d => {d.x = d.tag; d.y =  d.mean})

  var margin = {top: 50, right: 50, bottom: 50, left: 50}
  , width = window.innerWidth - margin.left - margin.right
  , height = window.innerHeight - margin.top - margin.bottom;

  var xScale = d3.scaleBand()
  .range([0, width])
  .domain(dataset.map(function(d) { return d.x; }));

  var yScale = d3.scaleLinear()
  .domain([0, d3.max(dataset, function(d){ return d.y; })])
  .range([height, 0]);

  var line = d3.line()
  .x(function(d, i) { return xScale(d.x); })
  .y(function(d) { return yScale(d.y); })
  .curve(d3.curveMonotoneX)

  var svg = d3.select("body").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   ")");

  svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0,"   height   ")")
    .call(d3.axisBottom(xScale))
    .selectAll("text")
    .attr("y", 0)
    .attr("x", 9)
    .attr("dy", "3em")
    .attr("transform", "rotate(-30)")
    .style("text-anchor", "end");

  svg.append("g")
    .attr("class", "y axis")
    .call(d3.axisLeft(yScale));

  svg.append("path")
    .datum(dataset)
    .attr("class", "line") 
    .attr("d", line) 
    .attr('stroke','black')
    .attr('stroke-width',2)
    .attr('fill','none')
 
  svg.selectAll(".dot")
    .data(dataset)
    .enter().append("circle")
    .attr("class", "dot")
    .attr("cx", function(d, i) { return xScale(d.x) })
    .attr("cy", function(d) { return yScale(d.y) })
    .attr("r", 5)
  
   var anno = svg.selectAll(null)
   .data(dataset).enter()
   .append("text")
  .text(function(d) {return d.y.toFixed(1);})
  .attr("x", function(d) { return xScale(d.x); })
  .attr("y", function(d) { return yScale(d.y) - 15; })
  .attr('text-anchor', 'middle')
  .attr("font-size",10)
  .attr("transform", function(d) {
    return "rotate(45 "   xScale(d.x)   ","   yScale(d.y)   ")"
  });

  var lines = svg.selectAll(null)
  .data(dataset).enter()
  .append("line")
  .attr("class", "stem-line")
  .attr("stroke", "green")
  .attr("stroke-width", 1)
  .attr("x1", function(d) { return xScale(d.x); } )
  .attr("x2", function(d) { return xScale(d.x); } )
  .attr("y1", function(d) { return yScale(0); } )
  .attr("y2", function(d) { return yScale(d.y); } );  
} 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script> 

Ответ №1:

Положение вашего текста определяется по крайней мере тремя группами параметров:

  1. его координаты, x координаты и y атрибуты
  2. его text-anchor нужно расположить слева/в центре/справа от ваших координат
  3. его базовый уровень см. в alignement-baseline разделе и dominant-baseline ссылка в документации Mozilla.

При этом вы позиционируете свой текст с .attr("x",9) помощью, поворачиваете его на -30 градусов .attr("transform", "rotate(-30)") и закрепляете его до конца .style("text-anchor", "end") . Я бы не стал поворачивать его, а закрепил бы его посередине:

 test()
function test() {
  var data = `tag,mean
boot_progress_start,10.882000000000001
boot_progress_preload_start,12.677
boot_progress_preload_end,15.962
boot_progress_system_run,16.441
boot_progress_pms_start,17.179
boot_progress_pms_system_scan_start,17.631
boot_progress_pms_scan_end,20.604
boot_progress_pms_ready,20.907
boot_progress_ams_ready,22.793000000000006
boot_progress_enable_screen,25.401
sf_stop_bootanim,25.427
`

  var dataset = d3.csvParse(data);
  dataset.forEach(d => {d.x = d.tag; d.y =  d.mean})

  var margin = {top: 50, right: 50, bottom: 50, left: 50}
  , width = window.innerWidth - margin.left - margin.right
  , height = window.innerHeight - margin.top - margin.bottom;

  var xScale = d3.scaleBand()
  .range([0, width])
  .domain(dataset.map(function(d) { return d.x; }));

  var yScale = d3.scaleLinear()
  .domain([0, d3.max(dataset, function(d){ return d.y; })])
  .range([height, 0]);

  var line = d3.line()
  .x(function(d, i) { return xScale(d.x); })
  .y(function(d) { return yScale(d.y); })
  .curve(d3.curveMonotoneX)

  var svg = d3.select("body").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   ")");

  svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0,"   height   ")")
    .call(d3.axisBottom(xScale))
    .selectAll("text")
    .attr("y", 0)
    .attr("x", 9)
    .attr("dy", "3em")
    // changing the rotation to 0 or remove it
    .attr("transform", "rotate(0)")
    // anchoring the text at the center
    .style("text-anchor", "middle");

  svg.append("g")
    .attr("class", "y axis")
    .call(d3.axisLeft(yScale));

  svg.append("path")
    .datum(dataset)
    .attr("class", "line") 
    .attr("d", line) 
    .attr('stroke','black')
    .attr('stroke-width',2)
    .attr('fill','none')
 
  svg.selectAll(".dot")
    .data(dataset)
    .enter().append("circle")
    .attr("class", "dot")
    .attr("cx", function(d, i) { return xScale(d.x) })
    .attr("cy", function(d) { return yScale(d.y) })
    .attr("r", 5)
  
   var anno = svg.selectAll(null)
   .data(dataset).enter()
   .append("text")
  .text(function(d) {return d.y.toFixed(1);})
  .attr("x", function(d) { return xScale(d.x); })
  .attr("y", function(d) { return yScale(d.y) - 15; })
  .attr('text-anchor', 'middle')
  .attr("font-size",10)
  .attr("transform", function(d) {
    return "rotate(45 "   xScale(d.x)   ","   yScale(d.y)   ")"
  });

  var lines = svg.selectAll(null)
  .data(dataset).enter()
  .append("line")
  .attr("class", "stem-line")
  .attr("stroke", "green")
  .attr("stroke-width", 1)
  .attr("x1", function(d) { return xScale(d.x); } )
  .attr("x2", function(d) { return xScale(d.x); } )
  .attr("y1", function(d) { return yScale(0); } )
  .attr("y2", function(d) { return yScale(d.y); } );  
}
 

Дополнительная заметка о ротации

Как вы, возможно, знаете, поскольку вы делаете это для anno, вы можете выбрать точку для поворота объекта. Если вы хотите, чтобы он вращался сам по себе, вам придется указать собственные координаты объекта:

 .attr("transform", function(d) {
    return "rotate(45 "   xScale(d.x)   " "   yScale(d.y)   ")"
  });
 

Комментарии:

1. лучший способ ответить на этот вопрос-скопировать фрагмент кода в свой ответ и наглядно продемонстрировать, как он решает проблему

2. Спасибо за ваше предложение, я в этом довольно новичок! Просто обновил ответ, надеюсь, это поможет