[英]D3 v4 chart with Bi directional chart
如何使用 D3 创建此图表? 任何帮助都将有助于对高图表进行全面尝试,但不能完全帮助更多点击事件在添加此向下钻取事件时不起作用,但在单击条形或 y 轴标签时不起作用。
const data = [
{ "name": 'IT', "value": 20, "negativeValue": -80 },
{ "name": 'Capital Invest', "value": 30, "negativeValue": -70 },
{ "name": 'Infrastructure', "value": 40, "negativeValue": -60 }
];
Highcharts.setOptions({
lang: {
drillUpText: `◁ Back to {series.description}`,
},
});
Highcharts.chart({
chart: {
type: 'bar',
renderTo: 'alignmentChart',
height: 530,
marginRight: 20,
backgroundColor: 'transparent',
events: {
drilldown(e: any) {
if (e.seriesOptions.fits) {
linesPositive = e.seriesOptions.line;
} else {
lineNegative = e.seriesOptions.line;
}
labels = !!e.seriesOptions && e.seriesOptions.data.map(a => a.name);
},
drillup(e: any) {
if (e.seriesOptions.fits) {
linesPositive = e.seriesOptions.line;
} else {
lineNegative = e.seriesOptions.line;
}
labels = !!e.seriesOptions && e.seriesOptions.data.map(a => a.name);
},
},
},
title: {
text: '',
},
colors: ['#f7a704', '#458dde'],
// tooltip: this.getTooltip(this),
xAxis: {
reversed: false,
tickPositions: Array.from(Array(this.multi.positive.length).keys()),
labels: {
useHTML: true,
formatter() {
return `<span title="${labels[this.value]}">${labels[this.value]}</span>`;
},
style: {
color: '#000000',
},
step: 1,
},
lineWidth: 0,
tickWidth: 0,
},
yAxis: {
title: {
text: null,
},
max: 100,
min: -100,
plotLines: [{
color: '#e5e5e5',
value: 0,
width: 1,
zIndex: 20,
}],
lineWidth: 1,
gridLineWidth: 0,
tickWidth: 1,
// offset: 100,
labels: {
y: 30,
align: 'center',
},
},
plotOptions: {
bar: {
pointWidth: 12,
},
series: {
stacking: 'normal',
dataLabels: {
enabled: true,
color: '#6b6b6b',
style: {
fontSize: '12px',
fontFamily: 'Proxima Nova'
},
formatter() {
return '';
},
inside: false,
},
},
},
series: [{
name: 'Fits Role',
description: 'Subfunctions',
data: this.multi.positive,
type: undefined
}, {
name: 'Not Fit Role',
description: 'Subfunctions',
data: this.multi.negative,
type: undefined
}],
drilldown: {
allowPointDrilldown: false,
activeAxisLabelStyle: {
fontSize: '12px',
fontWeight: 'bold',
color: '#007bc7',
textDecoration: 'none',
},
series: this.multi.drilldowns,
},
credits: {
enabled: false,
},
legend: {
enabled: false,
},
exporting: {
enabled: false,
},
});
与我分享的答案相比,我只需要进行很少的更改。 正如我在评论中所说,我为每个项目创建一个g
节点,并为每个项目绘制两个rect
。
然后我更新rect
s 以具有相同的数据形状( { name: string, value: number }
),无论它是正数还是负数。 这使我可以对两种类型做完全相同的事情。
// Now, the data can also be negative const data = [{ "name": 'IT', "value": 20, "negativeValue": -80 }, { "name": 'Capital Invest', "value": 30, "negativeValue": -70 }, { "name": 'Infrastructure', "value": 40, "negativeValue": -60 }]; const width = 600, height = 300, margin = { top: 20, left: 100, right: 40, bottom: 40 }; // Now, we don't use 0 as a minimum, but get it from the data using d3.extent const x = d3.scaleLinear() .domain([-100, 100]) .range([0, width]); const y = d3.scaleBand() .domain(data.map(d => d.name)) .range([height, 0]) .padding(0.1); const svg = d3.select('svg') .attr('width', width + margin.left + margin.right) .attr('height', height + margin.top + margin.bottom); const g = svg .append('g') .attr('transform', `translate(${margin.left} ${margin.right})`); // One group per data entry, each holding two bars const barGroups = g .selectAll('.barGroup') .data(data); barGroups.exit().remove(); const newBarGroups = barGroups.enter() .append('g') .attr('class', 'barGroup'); // Append one bar for the positive value, and one for the negative one newBarGroups .append('rect') .attr('class', 'positive') .attr('fill', 'darkgreen'); newBarGroups .append('rect') .attr('class', 'negative') .attr('fill', 'darkred'); const positiveBars = newBarGroups .merge(barGroups) .select('.positive') .datum(d => ({ name: d.name, value: d.value })); const negativeBars = newBarGroups .merge(barGroups) .select('.negative') .datum(d => ({ name: d.name, value: d.negativeValue })); newBarGroups.selectAll('rect') // If a bar is positive it starts at x = 0, and has positive width // If a bar is negative it starts at x < 0 and ends at x = 0 .attr('x', d => d.value > 0 ? x(0) : x(d.value)) .attr('y', d => y(d.name)) // If the bar is positive it ends at x = v, but that means it's x(v) - x(0) wide // If the bar is negative it ends at x = 0, but that means it's x(0) - x(v) wide .attr('width', d => d.value > 0 ? x(d.value) - x(0) : x(0) - x(d.value)) .attr('height', y.bandwidth()) // Let's color the bar based on whether the value is positive or negative g.append('g') .classed('x-axis', true) .attr('transform', `translate(0, ${height})`) .call(d3.axisBottom(x)) g.append('g') .classed('y-axis', true) .attr('transform', `translate(${x(0)}, 0)`) .call(d3.axisLeft(y))
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script> <svg></svg>
或者,您可以在不合并选择的情况下执行此操作,如下所示:
// Now, the data can also be negative const data = [{ "name": 'IT', "value": 20, "negativeValue": -80 }, { "name": 'Capital Invest', "value": 30, "negativeValue": -70 }, { "name": 'Infrastructure', "value": 40, "negativeValue": -60 }]; const width = 600, height = 300, margin = { top: 20, left: 100, right: 40, bottom: 40 }; // Now, we don't use 0 as a minimum, but get it from the data using d3.extent const x = d3.scaleLinear() .domain([-100, 100]) .range([0, width]); const y = d3.scaleBand() .domain(data.map(d => d.name)) .range([height, 0]) .padding(0.1); const svg = d3.select('svg') .attr('width', width + margin.left + margin.right) .attr('height', height + margin.top + margin.bottom); const g = svg .append('g') .attr('transform', `translate(${margin.left} ${margin.right})`); // One group per data entry, each holding two bars const positiveBars = g .selectAll('.positive') .data(data); positiveBars.exit().remove(); positiveBars.enter() .append('rect') .attr('class', 'positive') .attr('fill', 'darkgreen') .merge(positiveBars) .attr('x', x(0)) .attr('y', d => y(d.name)) // The bar is positive. It ends at x = v, but that means it's x(v) - x(0) wide .attr('width', d => x(d.value) - x(0)) .attr('height', y.bandwidth()); const negativeBars = g .selectAll('.negative') .data(data); negativeBars.exit().remove(); negativeBars.enter() .append('rect') .attr('class', 'negative') .attr('fill', 'darkred') .merge(negativeBars) .attr('x', d => x(d.negativeValue)) .attr('y', d => y(d.name)) // The bar is negative. It ends at x = 0, but that means it's x(0) - x(v) wide .attr('width', d => x(0) - x(d.negativeValue)) .attr('height', y.bandwidth()); g.append('g') .classed('x-axis', true) .attr('transform', `translate(0, ${height})`) .call(d3.axisBottom(x)) g.append('g') .classed('y-axis', true) .attr('transform', `translate(${x(0)}, 0)`) .call(d3.axisLeft(y))
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script> <svg></svg>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.