我有以下代码,它显示可以处理正值和负值的条形图。我现在想将条形标签旋转 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>
我会这样做
首先我将添加一个新类到标签
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";
});