D3.JS旋转条形图条形标签

问题描述 投票:0回答:1

我有以下代码,它显示可以处理正值和负值的条形图。我现在想将条形标签旋转 90 度,但当我尝试时,它们不会在其位置上旋转,而是似乎围绕 0,0

旋转

我尝试过以下方法:

.attr("transform", "rotate(-90)");

但这会将我所有的标签移出屏幕。

有办法做到这一点吗?

感谢您提前提供的任何帮助。

let payloadResponse = {
    "text": "",
    "chart": "bar",
    "dims": [{
      "dim0": "Year Month"
    }],
    "metrics": [{
      "metric0": "Rev"
    }],
    "totals": [{
      "metric0": "0",
    }],
    "data": [{
      "dim0": "2022-09",
      "metric0": "159487698",
    }, {
      "dim0": "2022-10",
      "metric0": "68008958",
    }, {
      "dim0": "2022-11",
      "metric0": "16152370",
    }, {
      "dim0": "2022-12",
      "metric0": "-9865752",
    }, {
      "dim0": "2023-01",
      "metric0": "46135223",
    }, {
      "dim0": "2023-02",
      "metric0": "11003565",
    }, {
      "dim0": "2023-03",
      "metric0": "-31017278",
    }, {
      "dim0": "2023-04",
      "metric0": "-37912892",
    }, {
      "dim0": "2023-05",
      "metric0": "96426636",
    }, {
      "dim0": "2023-06",
      "metric0": "-34053237",
    }, {
      "dim0": "2023-07",
      "metric0": "-32393213",
    }, {
      "dim0": "2023-08",
      "metric0": "24596177",
    }]
  }
  
    let options = {};
  let barWidth;
  let labelLengths = [];

  const response = _.cloneDeep(payloadResponse);
  const d3ChartData = [];
  const horizontal = false
  
  
  //console.log("response","",response)

  response.data.forEach(m => {
    m.metric0 = parseFloat(m.metric0);
  });


  const total = _.sumBy(response.data, 'metric0')
  const { percentValues } = options;

  //console.log("percentValues","",percentValues)
  
  const metrics = [];
  response.metrics.forEach(m => {
    metrics.push({ key: Object.keys(m)[0], name: Object.values(m)[0] });
  });
  //console.log("metrics","",metrics)
  
    const dimensions = [];
  response.dims.forEach(d => {
    dimensions.push({ key: Object.keys(d)[0], name: Object.values(d)[0] });
  });
  //console.log("dimensions","",dimensions)
  
    response.data.forEach(row => {
    const newobject = {};
    newobject["category"] = row[dimensions[0].key];
    newobject["values"] = row[metrics[0].key];//Y axis metrics
    metrics.forEach((s, i) => {
      row[metrics[i].key] = percentValues ? percentage(row[metrics[i].key] / total) : row[metrics[i].key]
      newobject[s.name] = row[metrics[i].key]
    })
    d3ChartData.push(newobject)
  })
  
  
  const groups = d3.map(d3ChartData, function (d) { return d["category"] }).keys();
  const valueGroups = d3.map(d3ChartData, function (d) { return d["values"] }).keys();//Y axis group
  subgroups = d3.map(metrics, function (d) { return d["name"] }).keys();
  subgroups = _.sortBy(subgroups);

  const newdataset = d3ChartData;
  
  
  
    const minarray = [];
  metrics.forEach((s, i) => {
    minarray.push((Math.min.apply(Math, response.data.map(function (o) {
      return Math.round(o[metrics[i].key]);
    }))))
  })

  let min = Math.min.apply(Math, minarray)

  const maxarray = [];
  metrics.forEach((s, i) => {
    maxarray.push((Math.max.apply(Math, response.data.map(function (o) {
      return Math.round(o[metrics[i].key]);
    }))))
  })
  
  
  let max = Math.max.apply(Math, maxarray)
  max = max * 1.1;


  let longestLabel = Math.max.apply(Math, maxarray).toString().length;
  if(longestLabel < 5)
    {
      longestLabel = 5;
    }
    
    
//START spacing for axis tick labels
let chartBottom = Math.max(...(groups.map(el => el.length)));

let longestValue = Math.max(...(valueGroups.map(el => Math.trunc(el).toString().length)));

let ySpacing = longestValue*10 >= 60 ? longestValue*10 == 70 ? longestValue*( (10 + Math.ceil(longestValue/3))+3) : longestValue*( (10 + Math.ceil(longestValue/3) )) : 80;


let margin = { top: 10, right: 20, bottom: horizontal ? ySpacing<150 ? ySpacing : 130 : chartBottom < 14 ? 100 : (chartBottom * 8), left: !horizontal ? ySpacing  : chartBottom < 14 ? 100 : (chartBottom * 8)},
width = 340,
height = 310;
   
   
        
        
var svg = d3.select("#chart")
  .attr("width", width + margin.left + margin.right)
  .attr("height", height + margin.top + margin.bottom)
  .append("g")
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
      

    
let sizeDimensions = {
    margin: margin,
    height: height,
    width: width
  }




//console.log(d3.min(valueGroups))

//console.log(d3.max(valueGroups))
//console.log(valueGroups)



const minValue = d3.min(newdataset, d => d.values);
const maxValue = d3.max(newdataset, d => d.values);


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

    
const y = d3.scaleLinear()
    .domain([Math.min(minValue, 0), Math.max(maxValue, 0)]) // Adjust the domain
    .range([height, 0]);  
  

  
  
 
 svg.append("g")
    .call(d3.axisLeft(y).tickSizeOuter(0))
    .selectAll("text")
    .attr("class", "axis-text")
    //.attr("transform", function (d) {return !horizontal ? "rotate(0)" : "rotate(-30)"})
    .attr("text-anchor", "end"); 
    
    


  // Another scale for subgroup position?
let xSubgroup = d3.scaleBand()
    .domain(subgroups)
    .range([0, (x.bandwidth())])
    .padding([0.05])

  // color palette = one color per subgroup
let color = d3.scaleOrdinal()
    .domain(subgroups)
    .range(['green', 'pink'])

    
    
    //console.log("newdataset","",newdataset)
    //console.log("subgroups","",subgroups)
    
      
 //draw bars 
 svg.append("g")
  .selectAll("g")
  .data(newdataset)
  .enter()
  .append("g")
  .attr("transform", function (d) { return "translate(" + x(d.category) + ",0)"; })
  .selectAll("rect")
  .data(function (d) {
    return subgroups.map(function (key) {
      return { key: key, value: d[key], category: d.category };
    });
  })
  .enter()
  .append("rect")
  .attr("x", function (d) { return xSubgroup(d.key); })
  .attr("width", xSubgroup.bandwidth())
  .attr("y", function (d) {
    return y(0); // Start with a height of 0
  })
  .attr("height", 0) // Initial height is 0
  .attr("fill", function (d) {
    //return color(d.key);
    return d.value >= 0 ? "#349ce4" : "#e41814"; // Set fill color based on value
  });
  

//Bar Labels
 svg.append("g")
  .selectAll("g")
  .data(newdataset)
  .enter()
  .append("g")
  .attr("transform", function (d) { return "translate(" + x(d.category) + ",0)"; })
  .selectAll("text")
  .data(function (d) {
    return subgroups.map(function (key) {
      return { key: key, value: d[key], category: d.category };
    });
  })
  .enter()
  .append("text")
  .attr("x", function (d) { return xSubgroup(d.key) + xSubgroup.bandwidth() / 2; })
  .attr("y", function (d) {
    return d.value >= 0 ? y(d.value) - 5 : y(d.value) + 5; // Adjust the y position for labels
  })
  .attr("dy", function (d) {
    return d.value >= 0 ? "-0.35em" : "1.2em"; // Adjust vertical alignment for labels
  })
  .text(function (d) { return d.category; })
  .attr("text-anchor", "middle")
  .style("font-size", "10px") // Adjust font size as needed
   //.attr("transform", "rotate(-90)"); Tried to rotate but moves labels off screen
  
  
 // X-Axis
  svg.append("g")
  .attr("transform", `translate(0, ${y(0)})`) // Position the x-axis at y=0
  .call(d3.axisBottom(x).tickSizeOuter(0).tickValues([]))
  .selectAll("text")
  .each(function(d){ labelLengths.push(this.getBBox().width)})
  .attr("transform", function (d) {return "rotate(90)"})
  .attr("y", function (d) {return percentValues ? 8 : longestValue < 2 ? 8 : -4 })
  .attr("x", function (d) {return percentValues ? 0 : longestValue < 2 ? 0 : -7 })
  .attr("class", "axis-text x-axis-text")
  .attr("text-anchor", function (d) {return percentValues ? "middle" : longestValue < 2 ? "middle" : "end"});  
  





//Anmation
svg.selectAll("g")
  .selectAll("rect")
  .transition()
  .duration(1000)
  .ease(d3.easePoly)
  .attr("y", d => y(Math.max(0, d.value))) // Animate to the correct y position
  .attr("height", d => Math.abs(y(0) - y(d.value))) // Animate to the correct height
  /* .attr("fill", d => d.value >= 0 ? "steelblue" : "red") */ // Set fill color based on value
  .delay(function (d, i) { return i * 30; });
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
<script src="lodash.js"></script>
<svg width="960" height="500" id="chart"></svg>

javascript d3.js
1个回答
0
投票

我会这样做

首先我将添加一个新类到标签

svg
    .append("g")
    .selectAll("g")
    .data(newdataset)
    .enter()
    .append("g")
    .attr("transform", function (d) {
        return "translate(" + x(d.category) + ",0)";
    })
    .selectAll("text")
    .data(function (d) {
        return subgroups.map(function (key) {
            return { key: key, value: d[key], category: d.category };
        });
    })
    .enter()
    .append("text")
    .attr("class", "bar-label") // Add a class for the labels
    .attr("x", function (d) {
        return xSubgroup(d.key) + xSubgroup.bandwidth() / 2;
    })
    .attr("y", function (d) {
        return d.value >= 0 ? y(d.value) - 5 : y(d.value) + 5;
    })
    .attr("dy", function (d) {
        return d.value >= 0 ? "-0.35em" : "1.2em";
    })
    .text(function (d) {
        return d.category;
    })
    .attr("text-anchor", "middle")
    .style("font-size", "10px");

然后应用旋转

svg
    .selectAll(".bar-label")
    .attr("transform", "rotate(-90)") // Rotate labels 90 degrees
    .attr("x", function (d) {
        return xSubgroup(d.key) + xSubgroup.bandwidth() / 2;
    })
    .attr("y", function (d) {
        return d.value >= 0 ? y(d.value) - 5 : y(d.value) + 5;
    })
    .attr("dy", function (d) {
        return d.value >= 0 ? "-0.35em" : "1.2em";
    });
© www.soinside.com 2019 - 2024. All rights reserved.