当所有值为0时零刻度垂直居中于轴

时间:2019-06-18 09:13:02

标签: d3.js

从d3版本4更新到版本5时,我注意到行为发生了变化。在v4中,当数据集包含y轴的所有零值时,“ 0”标记已正确对齐图表的底部

<head>
  <!-- load the d3.js library -->
  <script src="https://d3js.org/d3.v4.min.js"></script>
  <style>
    .line {
      fill: none;
      stroke: steelblue;
      stroke-width: 2px;
    }
  </style>
</head>

<script>
  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 n = 21;
  // An array of objects of length N. Each object has key -> value pair, the key being "y" and the value is a random number
  // var dataset = d3.range(n).map(function(d) { return {"y": d3.randomUniform(1)() } })
  var dataset = d3.range(n).map(function(d) {
    return {
      "y": 0
    }
  })

  var xScale = d3.scaleLinear()
    .domain([0, n - 1])
    .range([0, width]);

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

  var line = d3.line()
    .x(function(d, i) {
      return xScale(i);
    })
    .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));

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

  svg.append("path")
    .datum(dataset)
    .attr("class", "line")
    .attr("d", line);
</script>

对于我的用例,这是预期的行为。

在v5中,在相同条件下,“ 0”与y轴的中心对齐。

<head>
  <!-- load the d3.js library -->    	
  <script src="https://d3js.org/d3.v5.min.js"></script>
  <style>
    .line {
      fill: none;
      stroke: steelblue;
      stroke-width: 2px;
    }
  </style>
</head>

<script>
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 n = 21;
// An array of objects of length N. Each object has key -> value pair, the key being "y" and the value is a random number
// var dataset = d3.range(n).map(function(d) { return {"y": d3.randomUniform(1)() } })
var dataset = d3.range(n).map(function(d) { return {"y": 0 } })

var xScale = d3.scaleLinear()
    .domain([0, n-1])
    .range([0, width]);

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

var line = d3.line()
    .x(function(d, i) { return xScale(i); }) 
    .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));

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

svg.append("path")
    .datum(dataset)
    .attr("class", "line")
    .attr("d", line);
</script>

两个示例之间的唯一区别是所加载的d3版本。

有什么办法可以保持d3当前版本(v5)在v4中表现出的相同行为?

3 个答案:

答案 0 :(得分:3)

这与D3 v4和v5之间的区别不是。实际上,此更改是D3 v5.8中引入的。

在这里看看,这是使用D3 v5.7的代码:

<head>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.js"></script>
  <style>
    .line {
      fill: none;
      stroke: steelblue;
      stroke-width: 2px;
    }

  </style>
</head>

<script>
  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 n = 21;
  // An array of objects of length N. Each object has key -> value pair, the key being "y" and the value is a random number
  // var dataset = d3.range(n).map(function(d) { return {"y": d3.randomUniform(1)() } })
  var dataset = d3.range(n).map(function(d) {
    return {
      "y": 0
    }
  })

  var xScale = d3.scaleLinear()
    .domain([0, n - 1])
    .range([0, width]);

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

  var line = d3.line()
    .x(function(d, i) {
      return xScale(i);
    })
    .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));

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

  svg.append("path")
    .datum(dataset)
    .attr("class", "line")
    .attr("d", line);

</script>

使用D3 v5.8现在使用相同的代码:

<head>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.8.0/d3.js"></script>
  <style>
    .line {
      fill: none;
      stroke: steelblue;
      stroke-width: 2px;
    }

  </style>
</head>

<script>
  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 n = 21;
  // An array of objects of length N. Each object has key -> value pair, the key being "y" and the value is a random number
  // var dataset = d3.range(n).map(function(d) { return {"y": d3.randomUniform(1)() } })
  var dataset = d3.range(n).map(function(d) {
    return {
      "y": 0
    }
  })

  var xScale = d3.scaleLinear()
    .domain([0, n - 1])
    .range([0, width]);

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

  var line = d3.line()
    .x(function(d, i) {
      return xScale(i);
    })
    .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));

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

  svg.append("path")
    .datum(dataset)
    .attr("class", "line")
    .attr("d", line);

</script>

有关说明,请参见D3 v5.8的release notes

  

D3-Scale

     

对于折叠的域,请使用域或范围的中点,而不要使用开始。 (重点是我的)

因此,除非返回D3 v5.7或更低版​​本,否则恐怕您无能为力。

事实上,D3 v5.8与v5.7如此不同(并且不向后兼容,例如,请参见新的scale构造函数或新的join方法),以我的拙见被命名为D3 v6.0。可以说,从v5.7到v5.8的差异要大于从v4到v5的差异。

答案 1 :(得分:0)

在yScale域的定义中,只有一个值(零)。因此,该值正确显示在轴的中间。

您可以通过调整域来解决此问题,但轴上会出现更多滴答声。

只有一个示例(您可以添加任何其他数字):

var yScale = d3.scaleLinear()
    .domain([0, d3.max(dataset, d => d.y)+1])
    .range([height, 0]);

答案 2 :(得分:0)

在 d3 v7 中遇到了同样的问题 - 解决方法是设置一个有效范围,如 previously answered 并有条件地显示刻度线和标签。

const height = *HEIGHT OF YOUR CHART*
const dataMax = Math.max(...*YOUR DATA*)
const dataMin = Math.min(...*YOUR DATA*)
const dataMinMaxZero = dataMax === 0 && dataMin === 0 
const scaleMax = dataMinMaxZero ? 1 : dataMax;

const yScale = d3.scaleLinear()
  .domain([dataMin, scaleMax])
  .range([height, 0]);

const yAxis = d3.axisLeft(yScale)

d3.select("#yaxis").call(yAxis);
if (dataMinMaxZero) {
  d3.select("#yaxis").selectAll(".tick").style("opacity", 0);
} else {
  d3.select("#yaxis").selectAll(".tick").style("opacity", 1);
}