D3.js: 无法使 Y 轴可滚动且 X 轴固定

D3.js: Cannot make Y axis scrollable and X axis fixed

我正在尝试重新创建 this example,其中 y 轴可滚动且 x 轴保持固定。我正在制作热图,我需要在垂直轴上显示 100 多个国家/地区,因此在页面中合理显示的唯一方法是使其可滚动。

我的代码是这样的:

const margin = { top: 20, right: 20, bottom: 30, left: 150 };
const width = 960 - margin.left - margin.right;
const height = 500 - margin.top - margin.bottom;


let chart = d3.select("#chart2")
    .append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .style("overflow-y", "scroll")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");


// Load the data
d3.csv("https://raw.githubusercontent.com/thedivtagguy/daily-data/master/dd_cropYieldsD3/cropYieldsD3/data/land_use.csv").then(function(data) {
    // console.log(data);
    const years = Array.from(new Set(data.map(d => d.year)));
    const countries = Array.from(new Set(data.map(d => d.entity)));

    // Sort countries based on change in land use in descending order
    const sortedCountries = countries.sort((a, b) => {
        const aChange = data.filter(d => d.entity === a).map(d => d.change).reduce((a, b) => a + b);
        const bChange = data.filter(d => d.entity === b).map(d => d.change).reduce((a, b) => a + b);
        return aChange - bChange;
    });



    const x = d3.scaleBand()
        .range([0, width])
        .domain(years)
        .padding(0.1);
    
    const y = d3.scaleBand()
        .range([height*6, 0])
        .domain(sortedCountries)
        .padding(0.1);

    
    chart.append("g")
        .attr("transform", "translate(0," + height + ")")
        // Only 10 years
        .call(d3.axisBottom(x).tickValues(years.filter((d, i) => !(i % 10))))
        .selectAll("text")
        .style("color", "black")
        .style("position", "fixed")
        .attr("transform", "translate(-10,10)rotate(-45)")
        .style("text-anchor", "end");

        chart.append("g")
        .call(d3.axisLeft(y))
        .selectAll("text")
        .style("color", "black")
        .attr("transform", "translate(-10,0)")
        .style("text-anchor", "end");    

    const colorScale = d3.scaleSequential()
        .domain([0, d3.max(data, d => d.change)])
        .interpolator(d3.interpolateInferno);


  // add the squares
  chart.selectAll()
    .data(data, function(d) {return d.year +':'+ d.entity;})
    .join("rect")
      .attr("x", function(d) { return x(d.year) })
      .attr("y", function(d) { return y(d.entity) })
      .attr("rx", 4)
      .attr("ry", 4)
      .attr("width", x.bandwidth() )
      .attr("height", y.bandwidth() )
      .style("fill", function(d) { return colorScale(d.change)
                    console.log(d.change);
    } )
      .style("stroke-width", 4)
      .style("stroke", "none")
      .style("opacity", 0.8)

});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js"></script>
<svg id="chart2" width="1000" height="800"></svg>

这给了我以下输出:

如您所见,y 轴被截断,而 x 轴未固定或未正确显示。根据上面链接的示例,我唯一能看到控制滚动的是 overflow-y,我已将其添加到我的 svg 此处:

let chart = d3.select("#chart2")
    .append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .style("overflow-y", "scroll")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

这不起作用。我做错了什么以及如何用我的代码复制固定的 x 轴和可滚动的 y 轴示例?

Live deploy here

当您仔细查看您发布的示例时,您会发现他们使用了两个 (!) SVG。一个用于图表,一个用于轴。从那开始已经解决了很大一部分问题。

接下来是 SVG 不可滚动。同样,查看示例会告诉您 overflow: scroll 实际上在 div 上,而不是在 SVG 上。

const margin = {
  top: 20,
  right: 20,
  bottom: 30,
  left: 150
};
const width = 960 - margin.left - margin.right;
const height = 500 - margin.top - margin.bottom;


let chart = d3.select("#chart2")
  .append("div")
  .classed("chart", true)
  .append("svg")
  .attr("width", width + margin.left + margin.right)
  .attr("height", height + margin.top + margin.bottom)
  .append("g")
  .style("overflow-y", "scroll")
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

// Make sure to create a separate SVG for the XAxis
let axis = d3.select("#chart2")
  .append("svg")
  .attr("width", width + margin.left + margin.right)
  .attr("height", 40)
  .append("g")
  .attr("transform", "translate(" + margin.left + ", 0)");


// Load the data
d3.csv("https://raw.githubusercontent.com/thedivtagguy/daily-data/master/dd_cropYieldsD3/cropYieldsD3/data/land_use.csv").then(function(data) {
  // console.log(data);
  const years = Array.from(new Set(data.map(d => d.year)));
  const countries = Array.from(new Set(data.map(d => d.entity)));

  // Sort countries based on change in land use in descending order
  const sortedCountries = countries.sort((a, b) => {
    const aChange = data.filter(d => d.entity === a).map(d => d.change).reduce((a, b) => a + b);
    const bChange = data.filter(d => d.entity === b).map(d => d.change).reduce((a, b) => a + b);
    return aChange - bChange;
  });

  const x = d3.scaleBand()
    .range([0, width])
    .domain(years)
    .padding(0.1);

  const y = d3.scaleBand()
    .range([height * 6, 0])
    .domain(sortedCountries)
    .padding(0.1);

  // Only 10 years 
  axis.call(d3
      .axisBottom(x)
      .tickValues(years.filter((d, i) => !(i % 10))))
    .selectAll("text")
    .style("color", "black")
    .style("position", "fixed")
    .attr("transform", "translate(-10,10)rotate(-45)")
    .style("text-anchor", "end");

  chart.append("g")
    .call(d3.axisLeft(y))
    .selectAll("text")
    .style("color", "black")
    .attr("transform", "translate(-10,0)")
    .style("text-anchor", "end");

  const colorScale = d3.scaleSequential()
    .domain([0, d3.max(data, d => d.change)])
    .interpolator(d3.interpolateInferno);


  // add the squares
  chart.selectAll()
    .data(data, function(d) {
      return d.year + ':' + d.entity;
    })
    .join("rect")
    .attr("x", function(d) {
      return x(d.year)
    })
    .attr("y", function(d) {
      return y(d.entity)
    })
    .attr("rx", 4)
    .attr("ry", 4)
    .attr("width", x.bandwidth())
    .attr("height", y.bandwidth())
    .style("fill", function(d) {
      return colorScale(d.change)
      console.log(d.change);
    })
    .style("stroke-width", 4)
    .style("stroke", "none")
    .style("opacity", 0.8)

});
#chart2 .chart {
  width: 960px;
  max-height: 470px;
  overflow-y: scroll;
  overflow-x: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js"></script>
<div id="chart2"></div>