为什么减去边距只在设置 svg 宽度时将它们加回去

Why subtract margins only add them back in when setting svg width

在下面的 d3 折线图中,作者创建了一个具有属性 top、right、bottom 和 left 的 margin 对象。

var margin = {top: 20, right: 20, bottom: 60, left: 80}

然后按以下方式设置宽度变量。

width = 700 - margin.left - margin.right // 700 - 80 - 20 = 600
// So, width variable is 600

仅此而已,在设置 svg 的宽度时重新添加边距。

attr('width', width + margin.left + margin.right) // 600 + 80 + 20 = 700
// So, width attr of the svg is 700

为什么不直接设置宽度 700 而不必减去边距? svg 最终被设置为什么。这种模式的目的是什么我以前见过它并试图理解这样做的目的。谢谢。

完整代码如下。

var margin = {top: 20, right: 20, bottom: 60, left: 80},
     width = 700 - margin.left - margin.right,
     height = 700 - margin.top - margin.bottom;
var svg = d3.select("body") //create Svg element
   .append("svg")
   .attr('width', width + margin.right + margin.left)
   .attr('height', height + margin.top + margin.bottom)
   .style("border", "solid 1px red")
   .attr("transform","translate(100,0)"); // To align svg at the center in the output tab.
var data = [
    {  date:"2020/01/01 00:00:00", patients: 600 },
    {  date:"2020/02/01 00:00:00", patients: 500 },
    {  date:"2020/03/01 00:00:00", patients: 400 },
    {  date:"2020/04/01 00:00:00", patients: 500 },
    {  date:"2020/05/01 00:00:00", patients: 300 },
    {  date:"2020/06/01 00:00:00", patients: 100 },
    {  date:"2020/07/01 00:00:00", patients: 50  },
    {  date:"2020/08/01 00:00:00", patients: 500 },
    {  date:"2020/09/01 00:00:00", patients: 550 },
    {  date:"2020/10/01 00:00:00", patients: 550 },
        ];
data=data.map(d => ({
   date: new Date(d.date),
   patients: d.patients
   }))        
var xscale = d3.scaleTime()
                  .domain(d3.extent(data, d=>d.date))
                  .range([0,width]);
var yscale= d3.scaleLinear()                         // drawing  y scale
           .domain([0,600])
           .range([height,0])                   
var chart=svg.append('g')
   .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
   .attr('width', width)
   .attr('height', height)
chart.append('g')
   .call(d3.axisBottom(xscale).tickFormat(d3.timeFormat("%b")))
   .attr('transform', 'translate(0,' + height + ')')
chart.append('g')
   .call(d3.axisLeft(yscale))
svg.append("text")                                  // labelling x-axis
    .text("Month")          
    .attr("transform","translate(350,680)");
svg.append("text")                                 // labelling y-axis
    .text("Number of patients")
    .attr('transform', "translate(40,400) rotate(-90)");   
var generator = d3.line()
    .x(function (d) { return xscale(d.date); })
    .y(function (d) { return yscale(d.patients); });      
chart.append('path')
    .datum(data)
    .attr("d", generator)
    .attr("fill","none")
    .attr("stroke","blue");

这是 D3 的标准做法,至少可以追溯到版本 3。Mike Bostock 在他的示例中使用了这个约定。我一直觉得它很有用。您举了一个相当简单的例子,但是随着您的 d3 代码变得越来越复杂,您将需要比总 svg 宽度更频繁地引用该绘图宽度。