如何左对齐y轴标签D3图表

时间:2020-10-20 14:38:34

标签: javascript typescript d3.js

如何在d3中左对齐y轴标签? 尝试使用.orient(left),但没有帮助。 尝试转换标签。 请帮助将标签向左对齐 例: 好 很好 非常 好1 很好1 非常1 好 很好 非常 好1 很好1 非常1 好 很好 非常 好1 很好1 非常1 好 很好 非常 好1 很好1 Very1

const data = [{name: 'Good', value: 1}, {name: 'Very Good', value: 2}, {name: 'Very', value: 1}, {name: 'Good1', value: 1}, {name: 'Very Good1', value: 2}, {name: 'Very1', value: 1}, {name: 'Good2', value: 1}, {name: 'Very Good2', value: 2}, {name: 'Very2', value: 1},{name: 'Good3', value: 1}, {name: 'Very Good3', value: 2}, {name: 'Very3', value: 1},{name: 'Good4', value: 1}, {name: 'Very Good5', value: 2}, {name: 'Very5', value: 1},{name: 'Good6', value: 1}, {name: 'Very Good6', value: 2}, {name: 'Very6', value: 1},{name: 'Good7', value: 1}, {name: 'Very Good7', value: 2}, {name: 'Very7', value: 1},{name: 'Good8', value: 1}, {name: 'Very Good8', value: 2}, {name: 'Very9', value: 1}];
// We want to center each rect around the value it's supposed to have.
// That means that we need to have a node width
let nodeWidth = 33;
let nodeHeight = 18;
if (this.viewBy !== 'Overlapping ' + this.jobOrSkill) {
  nodeWidth = 60;
  nodeHeight = 18;
}
let height = 1000;
const width = 620,
  paddingLeft = 0,
  paddingTop = 0,
  margin = {
    top: 20,
    left: 140,
    right: 40,
    bottom: 40
};
if (this.totalSkills && this.totalSkills.length < 10) {
  height = 250;
}
if (this.totalSkills && this.totalSkills.length > 10 && this.totalSkills.length < 20) {
  height = 400;
}
if (this.totalSkills && this.totalSkills.length > 20 && this.totalSkills.length < 30) {
  height = 600;
}
const innerWidth = width + (margin.left + margin.right);
const innerHeight = height + (margin.top + margin.bottom);
// We also need to make sure there is space for all nodes, even at the edges.
// One way to get this is by just extending the domain a little.
const domain = d3.extent(data.map(d => Math.round(d.value)));
const x = d3.scaleLinear()
  .domain([Math.round(domain[0]) - 1, Math.round(domain[1]) + 1])
  .range([0, width]);

const y = d3.scaleBand()
  .domain(data.map((d, i) => d.name && d.name.length > 20 ? d.name = d.name.slice(0, 20) + '...' : d.name = d.name))
  .range([height, 0])
  .padding(1);

const svg = d3.select('#comparisionChartData')
  .attr('width', width + margin.left)
  .attr('height', height + margin.top + margin.bottom);

const g = svg
  .append('g')
  .attr('transform', `translate(${margin.left} ${margin.right})`);

g.append('g')
  .classed('x-axis', true)
  .attr('transform', `translate(0, ${height})`)
  .call(d3.axisBottom(x));

d3.selectAll('.y-axis').selectAll('.tick')
  .append('svg:title')
  .data(data)
  .text(function (d) {
    return d.name;
  });

g.append('g')
  .classed('y-axis', true)
  .call(d3.axisLeft(y).tickPadding(10));
const bars = g.append('g')
  .selectAll('rect')
  .data(data)
  .attr('stroke', '#e5e5e5')
  .attr('class', 'line');

bars.exit().remove();

// All the same until here
bars.enter()
  .append('rect')
  // width has become a constant
  .attr('width', nodeWidth)
  // Now, transform each node so it centers around the value it's supposed to have
  .attr('transform', `translate(${-nodeWidth / 2} -8)`)
  // .merge(bars)
  // `x` denotes the placement directly again
  .attr('x', d => x(Math.round(d.value)))
  .attr('y', d => y(d.name))
  .attr('height', nodeHeight)
  .attr('fill', d => Math.round(d.value) > 0 && this.viewBy === 'Overlapping ' + this.jobOrSkill ? '#648fff' :
    this.viewBy === 'Overlapping ' + this.jobOrSkill && Math.round(d.value) === 0 ? '#9a16ca' :
    this.viewBy === 'Overlapping ' + this.jobOrSkill && Math.round(d.value) < 0 ? '#dc267f' :
    this.viewBy === 'Unique to Organization' ? '#016970' : this.viewBy === 'Absent to Organization' ? '#757575' : '#648fff');

// Now one more thing, we want to add labels to each node.
// `<rect>` can't have children, we we add them to the plot seperately
// using the same `data` as for the bars
const labels = g.append('g')
  .selectAll('text')
  .data(data);

labels.exit().remove();

labels.enter()
  .append('text')
  .attr('fill', 'white')
  .attr('text-anchor', 'middle') // center-align the text
  // .merge(bars)
  // `x` denotes the placement directly
  .attr('x', d => x(Math.round(d.value)))
  // Add half a bar's height to target the center of each node
  .attr('y', d => y(d.name) + y.bandwidth() / 4)
  // Actually fill in the text this.viewBy === 'Overlapping ' + this.jobOrSkill ? '+' + Math.round(d.value) :
  .text(d => Math.round(d.value) > 0 && this.viewBy === 'Overlapping ' + this.jobOrSkill ? Math.round(d.value)
  : this.viewBy === 'Unique to Organization' ? 'Unique' : this.viewBy === 'Absent to Organization' ? 'Absent' : Math.round(d.value))
  .attr('title', d => y(d.name))
  .attr('transform', `translate(0, 0)`)
  .attr('dy', 5);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg id="comparisionChartData"></svg>

1 个答案:

答案 0 :(得分:0)

我能够结合使用text-anchor: starttransform(-60, 0)来获得它:

const data = [{
  name: 'Good',
  value: 1
}, {
  name: 'Very Good',
  value: 2
}, {
  name: 'Very',
  value: 1
}, {
  name: 'Good1',
  value: 1
}, {
  name: 'Very Good1',
  value: 2
}, {
  name: 'Very1',
  value: 1
}, {
  name: 'Good2',
  value: 1
}, {
  name: 'Very Good2',
  value: 2
}, {
  name: 'Very2',
  value: 1
}, {
  name: 'Good3',
  value: 1
}, {
  name: 'Very Good3',
  value: 2
}, {
  name: 'Very3',
  value: 1
}, {
  name: 'Good4',
  value: 1
}, {
  name: 'Very Good5',
  value: 2
}, {
  name: 'Very5',
  value: 1
}, {
  name: 'Good6',
  value: 1
}, {
  name: 'Very Good6',
  value: 2
}, {
  name: 'Very6',
  value: 1
}, {
  name: 'Good7',
  value: 1
}, {
  name: 'Very Good7',
  value: 2
}, {
  name: 'Very7',
  value: 1
}, {
  name: 'Good8',
  value: 1
}, {
  name: 'Very Good8',
  value: 2
}, {
  name: 'Very9',
  value: 1
}];
// We want to center each rect around the value it's supposed to have.
// That means that we need to have a node width
let nodeWidth = 33;
let nodeHeight = 18;
if (this.viewBy !== 'Overlapping ' + this.jobOrSkill) {
  nodeWidth = 60;
  nodeHeight = 18;
}
let height = 1000;
const width = 620,
  paddingLeft = 0,
  paddingTop = 0,
  margin = {
    top: 20,
    left: 140,
    right: 40,
    bottom: 40
  };
if (this.totalSkills && this.totalSkills.length < 10) {
  height = 250;
}
if (this.totalSkills && this.totalSkills.length > 10 && this.totalSkills.length < 20) {
  height = 400;
}
if (this.totalSkills && this.totalSkills.length > 20 && this.totalSkills.length < 30) {
  height = 600;
}
const innerWidth = width + (margin.left + margin.right);
const innerHeight = height + (margin.top + margin.bottom);
// We also need to make sure there is space for all nodes, even at the edges.
// One way to get this is by just extending the domain a little.
const domain = d3.extent(data.map(d => Math.round(d.value)));
const x = d3.scaleLinear()
  .domain([Math.round(domain[0]) - 1, Math.round(domain[1]) + 1])
  .range([0, width]);

const y = d3.scaleBand()
  .domain(data.map((d, i) => d.name && d.name.length > 20 ? d.name = d.name.slice(0, 20) + '...' : d.name = d.name))
  .range([height, 0])
  .padding(1);

const svg = d3.select('#comparisionChartData')
  .attr('width', width + margin.left)
  .attr('height', height + margin.top + margin.bottom);

const g = svg
  .append('g')
  .attr('transform', `translate(${margin.left} ${margin.right})`);

g.append('g')
  .classed('x-axis', true)
  .attr('transform', `translate(0, ${height})`)
  .call(d3.axisBottom(x));

g.append('g')
  .classed('y-axis', true)
  .call(d3.axisLeft(y).tickPadding(10))
  .selectAll("text")
  .attr("transform", "translate(-60, 0)")
  .attr("text-anchor", "start")
  .append("title")
  .text(function(d) {
    return d;
  });

const bars = g.append('g')
  .selectAll('rect')
  .data(data)
  .attr('stroke', '#e5e5e5')
  .attr('class', 'line');

bars.exit().remove();

// All the same until here
bars.enter()
  .append('rect')
  // width has become a constant
  .attr('width', nodeWidth)
  // Now, transform each node so it centers around the value it's supposed to have
  .attr('transform', `translate(${-nodeWidth / 2} -8)`)
  // .merge(bars)
  // `x` denotes the placement directly again
  .attr('x', d => x(Math.round(d.value)))
  .attr('y', d => y(d.name))
  .attr('height', nodeHeight)
  .attr('fill', d => Math.round(d.value) > 0 && this.viewBy === 'Overlapping ' + this.jobOrSkill ? '#648fff' :
    this.viewBy === 'Overlapping ' + this.jobOrSkill && Math.round(d.value) === 0 ? '#9a16ca' :
    this.viewBy === 'Overlapping ' + this.jobOrSkill && Math.round(d.value) < 0 ? '#dc267f' :
    this.viewBy === 'Unique to Organization' ? '#016970' : this.viewBy === 'Absent to Organization' ? '#757575' : '#648fff');

// Now one more thing, we want to add labels to each node.
// `<rect>` can't have children, we we add them to the plot seperately
// using the same `data` as for the bars
const labels = g.append('g')
  .selectAll('text')
  .data(data);

labels.exit().remove();

labels.enter()
  .append('text')
  .attr('fill', 'white')
  .attr('text-anchor', 'middle') // center-align the text
  // .merge(bars)
  // `x` denotes the placement directly
  .attr('x', d => x(Math.round(d.value)))
  // Add half a bar's height to target the center of each node
  .attr('y', d => y(d.name) + y.bandwidth() / 4)
  // Actually fill in the text this.viewBy === 'Overlapping ' + this.jobOrSkill ? '+' + Math.round(d.value) :
  .text(d => Math.round(d.value) > 0 && this.viewBy === 'Overlapping ' + this.jobOrSkill ? Math.round(d.value) :
    this.viewBy === 'Unique to Organization' ? 'Unique' : this.viewBy === 'Absent to Organization' ? 'Absent' : Math.round(d.value))
  .attr('title', d => y(d.name))
  .attr('transform', `translate(0, 0)`)
  .attr('dy', 5);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg id="comparisionChartData"></svg>