修复可滚动热图 d3 中的 x 轴位置

Fix x-axis position in scrollable Heatmap d3

我创建了一个可滚动的热图,我想在滚动 y 轴时固定 x 轴。我检查了这里的一些帖子并尝试使解决方案工作,例如固定位置,但没有成功。

编辑:我试着用这个例子来解决它 但是现在我的x轴描述消失了,x轴放在了底部。

var width = 500,
  height = 600,
  margintop = 50,
  marginbottom = 50,
  marginright = 10,
  marginleft = 50
d3.csv("https://raw.githubusercontent.com/Lea1216/d3/main/heatmap.csv", function(data) {

  var svg = d3.select("#my_dataviz")
              .append("div")
              .classed("chart",true)
              .append("svg")
                .attr("width",width +marginleft + marginright)
                .attr("height",height+margintop + marginbottom)
                .append("g")
                .attr("transform",
                      "translate(" + marginleft + "," + margintop + ")");

 var axis = d3.select("#my_dataviz")
                   .append("svg")
                   .attr("width", width + marginleft +marginright)
                   .attr("height",40)
                   .append("g")
                   .attr("transform", "translate(" + marginleft + ", 0)");


  var x_axis = d3.scaleBand()
    .range([0, width])
    .domain(data.map(function(d) {
      return d.group;
    }))
    .padding(0.01);

  axis.call(d3.axisTop(x_axis))
                .selectAll("text")
                .style("text-anchor", "end")
                .style("position","fixed")
                .attr("dx",15)
                .attr("dy",5)
                .attr("transform", "rotate(-65)");

  var y_axis = d3.scaleBand()
    .range([height, 0])
    .domain(data.map(function(d) {
      return d.activity;
    }))
    .padding(0.01);

  svg.append("g")
    .call(d3.axisLeft(y_axis))
    .attr("class", "y_axis")
    .selectAll("text")
    .on("click", function(d) {
      window.open(d.url, "_blank")
    });

  var myColor = d3.scaleLinear()
    .range(["white", "#C37B89"])
    .domain([1, 100])

  svg.selectAll()
    .data(data, function(d) {
      return d.group + ':' + d.activity;
    })
    .enter()
    .append("rect")
    .attr("x", function(d) {
      return x_axis(d.group)
    })
    .attr("y", function(d) {
      return y_axis(d.activity)
    })
    .attr("width", x_axis.bandwidth())
    .attr("height", y_axis.bandwidth())
    .style("fill", function(d) {
      return myColor(d.value)
    })
    .style("stroke-width", 1)
    .style("stroke", "none")

})
.rect {
  opacity: 0.8;
}

#my_dataviz {
  width: 600px;
  height: 500px;
  overflow-y: scroll;
  padding: 50px;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="my_dataviz"></div>

对此有几种可能的解决方案。第一个是在同一层上绘制两个 SVG 元素,并在 X 轴元素上使用 position: fixed。您可以看到块仍然显示在轴后面,但是您可以通过绘制 rect 轴 SVG 元素的总大小并给它 fill: white:

来解决这个问题

var width = 500,
  height = 600,
  margintop = 50,
  marginbottom = 50,
  marginright = 10,
  marginleft = 50
d3.csv("https://raw.githubusercontent.com/Lea1216/d3/main/heatmap.csv", function(data) {

  // Add the axis *before* adding the SVG, because the order matters in HTML
  var axis = d3.select("#my_dataviz")
    .append("svg")
    .attr("width", width + marginleft + marginright)
    // Add 2 so you have a little bit of room left for the black bar
    // i.e. margin top has to be less than total height!
    .attr("height", margintop + 2)
    .style("position", "fixed") // this makes the axis fixed
    .append("g")
    .attr("transform", "translate(" + marginleft + ", " + margintop + ")");

  var svg = d3.select("#my_dataviz")
    .append("svg")
    .attr("width", width + marginleft + marginright)
    .attr("height", height + margintop + marginbottom)
    .append("g")
    .attr("transform", "translate(" + marginleft + ", " + margintop + ")");

  var x_axis = d3.scaleBand()
    .range([0, width])
    .domain(data.map(function(d) {
      return d.group;
    }))
    .padding(0.01);

  axis.call(d3.axisTop(x_axis))
    .selectAll("text")
    .style("text-anchor", "end")
    .style("position", "fixed")
    .attr("dx", 15)
    .attr("dy", 5)
    .attr("transform", "rotate(-65)");

  var y_axis = d3.scaleBand()
    .range([height, 0])
    .domain(data.map(function(d) {
      return d.activity;
    }))
    .padding(0.01);

  svg.append("g")
    .call(d3.axisLeft(y_axis))
    .attr("class", "y_axis")
    .selectAll("text")
    .on("click", function(d) {
      window.open(d.url, "_blank")
    });

  var myColor = d3.scaleLinear()
    .range(["white", "#C37B89"])
    .domain([1, 100])

  svg.selectAll()
    .data(data, function(d) {
      return d.group + ':' + d.activity;
    })
    .enter()
    .append("rect")
    .attr("x", function(d) {
      return x_axis(d.group)
    })
    .attr("y", function(d) {
      return y_axis(d.activity)
    })
    .attr("width", x_axis.bandwidth())
    .attr("height", y_axis.bandwidth())
    .style("fill", function(d) {
      return myColor(d.value)
    })
    .style("stroke-width", 1)
    .style("stroke", "none")

})
.rect {
  opacity: 0.8;
}

#my_dataviz {
  border: solid 1px red;
  width: 600px;
  height: 300px;
  overflow-y: scroll;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="my_dataviz"></div>

第二个选项是将另一个 SVG 放在 div 中并滚动 div 而不是 SVG。然后,您确实需要使用 CSS 做更多事情,并且需要将一些 CSS 规则从 #my_dataviz 元素移动到 .chart 元素:

var width = 500,
  height = 600,
  margintop = 50,
  marginbottom = 50,
  marginright = 10,
  marginleft = 50
d3.csv("https://raw.githubusercontent.com/Lea1216/d3/main/heatmap.csv", function(data) {

  // Add the axis *before* adding the SVG, because the order matters in HTML
  var axis = d3.select("#my_dataviz")
    .append("svg")
    .attr("width", width + marginleft + marginright)
    // Add 2 so you have a little bit of room left for the black bar
    // i.e. margin top has to be less than total height!
    .attr("height", margintop + 1)
    .append("g")
    .attr("transform", "translate(" + marginleft + ", " + margintop + ")");

  var svg = d3.select("#my_dataviz")
    .append("div")
    // Note the CSS rules for .chart
    .attr("class", "chart")
    .append("svg")
    .attr("width", width + marginleft + marginright)
    // No margin-top required here, because the other element already took care of it
    .attr("height", height + marginbottom)
    .append("g")
    // Same, no margin-top
    .attr("transform", "translate(" + marginleft + ", 0)");

  var x_axis = d3.scaleBand()
    .range([0, width])
    .domain(data.map(function(d) {
      return d.group;
    }))
    .padding(0.01);

  axis.call(d3.axisTop(x_axis))
    .selectAll("text")
    .style("text-anchor", "end")
    .style("position", "fixed")
    .attr("dx", 15)
    .attr("dy", 5)
    .attr("transform", "rotate(-65)");

  var y_axis = d3.scaleBand()
    .range([height, 0])
    .domain(data.map(function(d) {
      return d.activity;
    }))
    .padding(0.01);

  svg.append("g")
    .call(d3.axisLeft(y_axis))
    .attr("class", "y_axis")
    .selectAll("text")
    .on("click", function(d) {
      window.open(d.url, "_blank")
    });

  var myColor = d3.scaleLinear()
    .range(["white", "#C37B89"])
    .domain([1, 100])

  svg.selectAll()
    .data(data, function(d) {
      return d.group + ':' + d.activity;
    })
    .enter()
    .append("rect")
    .attr("x", function(d) {
      return x_axis(d.group)
    })
    .attr("y", function(d) {
      return y_axis(d.activity)
    })
    .attr("width", x_axis.bandwidth())
    .attr("height", y_axis.bandwidth())
    .style("fill", function(d) {
      return myColor(d.value)
    })
    .style("stroke-width", 1)
    .style("stroke", "none")

})
.rect {
  opacity: 0.8;
}

#my_dataviz {
  width: 600px;
  border: solid 1px red;
}

/* To make sure there is no space between the DIV and the SVG */
#my_dataviz > * {
  display: block;
}

.chart {
  overflow-y: scroll;
  max-height: 300px;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="my_dataviz"></div>