如何添加过滤器以创建从一个条到另一个条的单个路径(不在循环中) [英] How to add filter to create single path from one bar to another bar (not in a loop)
问题描述
我要求条形图最多包含 4 个条形图,我正在尝试在条形图上添加一个过滤器,我可以从 4 个条形图中选择一个特定的条形并添加一个路径来显示数据.
>示例
不同的路径:
Bar1 -->酒吧3
Bar2 -->酒吧4
Bar3 -->酒吧2
Bar4 -->酒吧1
请帮助我理解如何选择任何特定的栏并设置到另一个栏的路径,如上例所示?我为相同的路径创建了不同的路径代码,但无法获得如何让目标栏创建正确的路径.
下面是我的代码:
var barData = [{"时间": "Bar1",价值":5388},{"时间": "Bar2",价值":6453},{"时间": "Bar3",价值":3345},{"时间": "Bar4",价值":5345}];const container = d3.select('#graph');const divWidth = parseInt(container.style('width'));const divHeight = parseInt(container.style('height'));//考虑这个宽度和高度对于 div "graphID" 是动态的,因为我正在尝试响应式设计const margin = {top: 30, right: 50, bottom: 50, left: 50};const width = divWidth - margin.left - margin.right;const height = divHeight - margin.top - margin.bottom;//在可视化节点即圆顶节点中添加svgconst svg = container.append("svg").attr("宽度", divWidth).attr("高度", divHeight);const svgG = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);//为条形添加工具提示var tooltip = d3.select("body").append("div").attr("class", "toolTip");const defs = svg.append("defs");const 标记 = defs.append("标记").attr("id","箭头").attr("markerWidth","10").attr("markerHeight","7").attr("refX","0").attr("refY","3.5").attr("东方","自动")constpolygon = marker.append("polygon").attr("填充","灰色").attr("点数","0 0, 10 3.5, 0 7")const xScale = d3.scaleBand().domain(barData.map(d => d.Time)).range([0, width+margin.right]);const xAxis = d3.axisBottom(xScale);//为x轴为svg添加g属性svgG.append('g').attr("transform", `translate(0,${height})`).call(xAxis);const yAxisMax = barData.reduce((max, item) => Math.max(max, item.Value), 0) * 1.5;const yScale = d3.scaleLinear().domain([0, yAxisMax]).range([高度, 0]);const yAxis = d3.axisLeft(yScale).ticks(4);svgG.append('g').call(yAxis);const bar = svgG.selectAll('g.bar').data(barData).进入().append('g').classed('bar', true).attr('transform', d => `translate(${xScale(d.Time) + xScale.bandwidth()/2}, 0)`);/*const staticColor = "steelblue",highlightColor = "橙色";var sheet = document.createElement('style')sheet.innerHTML = ".bar {fill: "+staticColor+"} .highlight {fill:"+highlightColor+"}";document.body.appendChild(sheet);*/bar.append('rect').attr('x', -20).attr('宽度', 40).attr('y', d => yScale(d.Value)).attr('height', d => height - yScale(d.Value) ).attr('填充', '蓝色')//.attr("class", "bar").on("mousemove", onMouseOver).on("mouseout", onMouseOut);函数 onMouseOver(d,i){工具提示.style("left", d3.event.pageX - 50 + "px").style("top", d3.event.pageY - 70 + "px").style("display", "inline-block").html("年份:" + (d.Time) + "
" + "值:" + (d.Value));d3.select(this).attr('fill', "#eec42d");//d3.select(this).attr('class', 'highlight');//this.setState({ fillColour: 'green' });}函数 onMouseOut(d,i){tooltip.style("display", "none");d3.select(this).attr('fill', "blue");//d3.select(this).attr('class', 'bar');//this.setState({ fillColour: 'blue' });}bar.append('文本').text(d => d.Value).attr('文本锚', '中间').attr('y', d => yScale(d.Value)).attr('dy', -5);//从Bar1到Bar3的路径bar.filter((d, i) => i == 0 ).append('路径').attr('d', (d, i) => `M 5,${yScale(d.Value) - 20} V ${Math.min(yScale(d.Value), yScale(barData[i+)2].Value)) - 60} H ${xScale.bandwidth() - 5} V ${yScale(barData[i+2].Value) - 25}`).style('描边', '灰色').style('填充', '无').attr('marker-end', 'url(#arrowhead)')//从Bar2到Bar4的路径bar.filter((d, i) => i == 1 ).append('路径').attr('d', (d, i) => `M 5,${yScale(d.Value) - 20} V ${Math.min(yScale(d.Value), yScale(barData[i+)2].Value)) - 60} H ${xScale.bandwidth() - 5} V ${yScale(barData[i+2].Value) - 25}`).style('描边', '灰色').style('填充', '无').attr('marker-end', 'url(#arrowhead)')//从Bar3到Bar2的路径bar.filter((d, i) => i == 2 ).append('路径').attr('d', (d, i) => `M 5,${yScale(d.Value) - 20} V ${Math.min(yScale(d.Value), yScale(barData[i-1].Value)) - 60} H ${xScale.bandwidth() - 5} V ${yScale(barData[i-1].Value) - 25}`).style('描边', '灰色').style('填充', '无').attr('marker-end', 'url(#arrowhead)')//从Bar3到Bar2的路径bar.filter((d, i) => i == 3 ).append('路径').attr('d', (d, i) => `M 5,${yScale(d.Value) - 20} V ${Math.min(yScale(d.Value), yScale(barData[0]).Value)) - 60} H ${xScale.bandwidth() - 5} V ${yScale(barData[0].Value) - 25}`).style('描边', '灰色').style('填充', '无').attr('marker-end', 'url(#arrowhead)')/*bar.filter((d, i) => i Math.min(yScale(d.Value), yScale(barData[i + 1].Value)) - 70).attr('width', xScale.bandwidth() - 30).attr('身高', 20).attr('rx', 10).style('填充', '白色').style('描边', '灰色');bar.filter((d, i) => i `${barData[i + 1].Value > d.Value ? '+' : ''}${Math.round((barData[i + 1].Value/d.Value * 100) - 100)}%`).attr('x', xScale.bandwidth()/2).attr('y', (d, i) => Math.min(yScale(d.Value), yScale(barData[i + 1].Value)) - 56).attr('文本锚', '中间').style('填充', '黑色');*/
#graph {宽度:600px;高度:400px;}文本 {字体大小:12px;字体系列:Ubuntu";}.工具提示{位置:绝对;显示:无;最小宽度:80px;高度:自动;背景:无重复滚动 0 0 #ffffff;边框:1px 实心 #6F257F;填充:5px;文本对齐:左;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script><div id="图形">
下面是一个带有 targets
的例子:
var barData = [{"时间": "Bar1",价值":5388,目标":2},{"时间": "Bar2",价值":6453,目标":0,},{"时间": "Bar3",价值":3345,目标":3,},{"时间": "Bar4",价值":5345,目标":1,}];const container = d3.select('#graph');const divWidth = parseInt(container.style('width'));const divHeight = parseInt(container.style('height'));const margin = {top: 30, right: 50, bottom: 50, left: 50};const width = divWidth - margin.left - margin.right;const height = divHeight - margin.top - margin.bottom;//在可视化节点即圆顶节点中添加svgconst svg = container.append("svg").attr("宽度", divWidth).attr("高度", divHeight);const svgG = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);//为条形添加工具提示var tooltip = d3.select("body").append("div").attr("class", "toolTip");const defs = svg.append("defs");const 标记 = defs.append("标记").attr("id","箭头").attr("markerWidth","10").attr("markerHeight","7").attr("refX","0").attr("refY","3.5").attr("东方","自动")constpolygon = marker.append("polygon").attr("填充","灰色").attr("点数","0 0, 10 3.5, 0 7")const xScale = d3.scaleBand().domain(barData.map(d => d.Time)).range([0, width+margin.right]);const xAxis = d3.axisBottom(xScale);//为x轴为svg添加g属性svgG.append('g').attr("transform", `translate(0,${height})`).call(xAxis);const yAxisMax = barData.reduce((max, item) => Math.max(max, item.Value), 0) * 1.5;const yScale = d3.scaleLinear().domain([0, yAxisMax]).range([高度, 0]);const yAxis = d3.axisLeft(yScale).ticks(4);svgG.append('g').call(yAxis);const bar = svgG.selectAll('g.bar').data(barData).进入().append('g').classed('bar', true).attr('transform', d => `translate(${xScale(d.Time) + xScale.bandwidth()/2}, 0)`);/*const staticColor = "steelblue",highlightColor = "橙色";var sheet = document.createElement('style')sheet.innerHTML = ".bar {fill: "+staticColor+"} .highlight {fill:"+highlightColor+"}";document.body.appendChild(sheet);*/bar.append('rect').attr('x', -20).attr('宽度', 40).attr('y', d => yScale(d.Value)).attr('height', d => height - yScale(d.Value) ).attr('填充', '蓝色').on("mousemove", onMouseOver).on("mouseout", onMouseOut);函数 onMouseOver(d,i){工具提示.style("left", d3.event.pageX - 50 + "px").style("top", d3.event.pageY - 70 + "px").style("display", "inline-block").html("年份:" + (d.Time) + "
" + "值:" + (d.Value));d3.select(this).attr('fill', "#eec42d");//d3.select(this).attr('class', 'highlight');//this.setState({ fillColour: 'green' });}函数 onMouseOut(d,i){tooltip.style("display", "none");d3.select(this).attr('fill', "blue");}bar.append('文本').text(d => d.Value).attr('文本锚', '中间').attr('y', d => yScale(d.Value)).attr('dy', -5);const topPosition = i =>yScale(9000) + i * 15;const pathBetweenBars = (d, i) =>{const delta = d.Target - i;const targetValue = barData[d.Target].Value;const targetX = delta * xScale.bandwidth() - 5;const sourceY = yScale(d.Value);const targetY = yScale(targetValue);const topY = topPosition(i);返回`M 5,${sourceY - 20} V ${topY} H ${targetX} V ${targetY - 25}`;};const LABEL_WIDTH = 50;const midPosition = (d, i) =>{const delta = d.Target - i;返回增量 * xScale.bandwidth()/2;}bar.filter(d => d.Target != null).append('路径').attr('d', (d, i) => pathBetweenBars(d, i)).style('描边', '灰色').style('填充', '无').attr('marker-end', 'url(#arrowhead)')bar.filter((d) => d.Target != null).append('rect').attr('x', (d, i) => midPosition(d, i) - LABEL_WIDTH/2).attr('y', (d, i) => topPosition(i) - 10).attr('宽度', LABEL_WIDTH).attr('身高', 20).attr('rx', 10).style('填充', '白色').style('描边', '灰色');bar.filter((d, i) => d.Target != null).append('文本').text((d, i) => `${barData[d.Target].Value > d.Value ? '+' : ''}${Math.round((barData[d.Target].Value/d.Value * 100) - 100)}%`).attr('x', (d, i) => midPosition(d, i)).attr('y', (d, i) => topPosition(i) + 3).attr('文本锚', '中间').style('填充', '黑色');
#graph {宽度:600px;高度:400px;}文本 {字体大小:12px;字体系列:Ubuntu";}.工具提示{位置:绝对;显示:无;最小宽度:80px;高度:自动;背景:无重复滚动 0 0 #ffffff;边框:1px 实心 #6F257F;填充:5px;文本对齐:左;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script><div id="图形">
I am have a requirement where the bar chart will containing max 4 bars and I am trying to add a filter on a bar chart where I can pick a particular bar from 4 bars and add a path to display the data.
Example
Different Paths:
Bar1 --> Bar3
Bar2 --> Bar4
Bar3 --> Bar2
Bar4 --> Bar1
Please help me to understand how can I pick any specific bar and set a path to another bar like above example? I have created different path code for the same but not able to get how I can get the target bar to create the proper path.
Below is my code:
var barData = [{
"Time": "Bar1",
"Value": 5388
},
{
"Time": "Bar2",
"Value": 6453
},
{
"Time": "Bar3",
"Value": 3345
},
{
"Time": "Bar4",
"Value": 5345
}];
const container = d3.select('#graph');
const divWidth = parseInt(container.style('width'));
const divHeight = parseInt(container.style('height'));
// Consider this width and Height are dynamic for div "graphID" because I am trying to responsive design
const margin = {top: 30, right: 50, bottom: 50, left: 50};
const width = divWidth - margin.left - margin.right;
const height = divHeight - margin.top - margin.bottom;
//To add svg in the visualization node i.e Dome node
const svg = container.append("svg")
.attr("width", divWidth)
.attr("height", divHeight);
const svgG = svg.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
//To add tooltip for bar
var tooltip = d3.select("body").append("div").attr("class", "toolTip");
const defs = svg.append("defs");
const marker = defs.append("marker")
.attr("id","arrowhead")
.attr("markerWidth","10")
.attr("markerHeight","7")
.attr("refX","0")
.attr("refY","3.5")
.attr("orient","auto")
const polygon = marker.append("polygon")
.attr("fill","gray")
.attr("points","0 0, 10 3.5, 0 7")
const xScale = d3.scaleBand()
.domain(barData.map(d => d.Time))
.range([0, width+margin.right]);
const xAxis = d3.axisBottom(xScale);
//Adding g attribute to svg for x axis
svgG.append('g')
.attr("transform", `translate(0,${height})`)
.call(xAxis);
const yAxisMax = barData.reduce((max, item) => Math.max(max, item.Value), 0) * 1.5;
const yScale = d3.scaleLinear()
.domain([0, yAxisMax])
.range([height, 0]);
const yAxis = d3.axisLeft(yScale).ticks(4);
svgG.append('g')
.call(yAxis);
const bars = svgG.selectAll('g.bar')
.data(barData)
.enter()
.append('g')
.classed('bar', true)
.attr('transform', d => `translate(${xScale(d.Time) + xScale.bandwidth() / 2}, 0)`);
/*
const staticColor = "steelblue",
highlightColor = "orange";
var sheet = document.createElement('style')
sheet.innerHTML = ".bar {fill: "+staticColor+"} .highlight {fill:"+highlightColor+"}";
document.body.appendChild(sheet);
*/
bars.append('rect')
.attr('x', -20)
.attr('width', 40)
.attr('y', d => yScale(d.Value))
.attr('height', d => height - yScale(d.Value) )
.attr('fill', 'blue')
//.attr("class", "bar")
.on("mousemove", onMouseOver)
.on("mouseout", onMouseOut);
function onMouseOver(d,i)
{
tooltip
.style("left", d3.event.pageX - 50 + "px")
.style("top", d3.event.pageY - 70 + "px")
.style("display", "inline-block")
.html("Year: " + (d.Time) + "<br>" + "Value: " + (d.Value));
d3.select(this).attr('fill', "#eec42d");
//d3.select(this).attr('class', 'highlight');
//this.setState({ fillColour: 'green' });
}
function onMouseOut(d,i)
{
tooltip.style("display", "none");
d3.select(this).attr('fill', "blue");
//d3.select(this).attr('class', 'bar');
//this.setState({ fillColour: 'blue' });
}
bars.append('text')
.text(d => d.Value)
.attr('text-anchor', 'middle')
.attr('y', d => yScale(d.Value))
.attr('dy', -5)
;
//path from Bar1 to Bar3
bars.filter((d, i) => i == 0 )
.append('path')
.attr('d', (d, i) => `M 5,${yScale(d.Value) - 20} V ${Math.min(yScale(d.Value), yScale(barData[i+2].Value)) - 60} H ${xScale.bandwidth() - 5} V ${yScale(barData[i+2].Value) - 25}`)
.style('stroke', 'gray')
.style('fill', 'none')
.attr('marker-end', 'url(#arrowhead)')
//path from Bar2 to Bar4
bars.filter((d, i) => i == 1 )
.append('path')
.attr('d', (d, i) => `M 5,${yScale(d.Value) - 20} V ${Math.min(yScale(d.Value), yScale(barData[i+2].Value)) - 60} H ${xScale.bandwidth() - 5} V ${yScale(barData[i+2].Value) - 25}`)
.style('stroke', 'gray')
.style('fill', 'none')
.attr('marker-end', 'url(#arrowhead)')
//path from Bar3 to Bar2
bars.filter((d, i) => i == 2 )
.append('path')
.attr('d', (d, i) => `M 5,${yScale(d.Value) - 20} V ${Math.min(yScale(d.Value), yScale(barData[i-1].Value)) - 60} H ${xScale.bandwidth() - 5} V ${yScale(barData[i-1].Value) - 25}`)
.style('stroke', 'gray')
.style('fill', 'none')
.attr('marker-end', 'url(#arrowhead)')
//path from Bar3 to Bar2
bars.filter((d, i) => i == 3 )
.append('path')
.attr('d', (d, i) => `M 5,${yScale(d.Value) - 20} V ${Math.min(yScale(d.Value), yScale(barData[0].Value)) - 60} H ${xScale.bandwidth() - 5} V ${yScale(barData[0].Value) - 25}`)
.style('stroke', 'gray')
.style('fill', 'none')
.attr('marker-end', 'url(#arrowhead)')
/*
bars.filter((d, i) => i < barData.length - 1)
.append('rect')
.attr('x', 15)
.attr('y', (d, i) => Math.min(yScale(d.Value), yScale(barData[i + 1].Value)) - 70)
.attr('width', xScale.bandwidth() - 30)
.attr('height', 20)
.attr('rx', 10)
.style('fill', 'white')
.style('stroke', 'gray');
bars.filter((d, i) => i < barData.length - 1)
.append('text')
.text((d, i) => `${barData[i + 1].Value > d.Value ? '+' : ''}${Math.round((barData[i + 1].Value / d.Value * 100) - 100)}%`)
.attr('x', xScale.bandwidth() / 2)
.attr('y', (d, i) => Math.min(yScale(d.Value), yScale(barData[i + 1].Value)) - 56)
.attr('text-anchor', 'middle')
.style('fill', 'black');
*/
#graph {
width: 600px;
height: 400px;
}
text {
font-size: 12px;
font-family: "Ubuntu";
}
.toolTip {
position: absolute;
display: none;
min-width: 80px;
height: auto;
background: none repeat scroll 0 0 #ffffff;
border: 1px solid #6F257F;
padding: 5px;
text-align: left;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="graph">
</div>
Here is an example with targets
:
var barData = [{
"Time": "Bar1",
"Value": 5388,
"Target": 2
},
{
"Time": "Bar2",
"Value": 6453,
"Target": 0,
},
{
"Time": "Bar3",
"Value": 3345,
"Target": 3,
},
{
"Time": "Bar4",
"Value": 5345,
"Target": 1,
}];
const container = d3.select('#graph');
const divWidth = parseInt(container.style('width'));
const divHeight = parseInt(container.style('height'));
const margin = {top: 30, right: 50, bottom: 50, left: 50};
const width = divWidth - margin.left - margin.right;
const height = divHeight - margin.top - margin.bottom;
//To add svg in the visualization node i.e Dome node
const svg = container.append("svg")
.attr("width", divWidth)
.attr("height", divHeight);
const svgG = svg.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
//To add tooltip for bar
var tooltip = d3.select("body").append("div").attr("class", "toolTip");
const defs = svg.append("defs");
const marker = defs.append("marker")
.attr("id","arrowhead")
.attr("markerWidth","10")
.attr("markerHeight","7")
.attr("refX","0")
.attr("refY","3.5")
.attr("orient","auto")
const polygon = marker.append("polygon")
.attr("fill","gray")
.attr("points","0 0, 10 3.5, 0 7")
const xScale = d3.scaleBand()
.domain(barData.map(d => d.Time))
.range([0, width+margin.right]);
const xAxis = d3.axisBottom(xScale);
//Adding g attribute to svg for x axis
svgG.append('g')
.attr("transform", `translate(0,${height})`)
.call(xAxis);
const yAxisMax = barData.reduce((max, item) => Math.max(max, item.Value), 0) * 1.5;
const yScale = d3.scaleLinear()
.domain([0, yAxisMax])
.range([height, 0]);
const yAxis = d3.axisLeft(yScale).ticks(4);
svgG.append('g')
.call(yAxis);
const bars = svgG.selectAll('g.bar')
.data(barData)
.enter()
.append('g')
.classed('bar', true)
.attr('transform', d => `translate(${xScale(d.Time) + xScale.bandwidth() / 2}, 0)`);
/*
const staticColor = "steelblue",
highlightColor = "orange";
var sheet = document.createElement('style')
sheet.innerHTML = ".bar {fill: "+staticColor+"} .highlight {fill:"+highlightColor+"}";
document.body.appendChild(sheet);
*/
bars.append('rect')
.attr('x', -20)
.attr('width', 40)
.attr('y', d => yScale(d.Value))
.attr('height', d => height - yScale(d.Value) )
.attr('fill', 'blue')
.on("mousemove", onMouseOver)
.on("mouseout", onMouseOut);
function onMouseOver(d,i)
{
tooltip
.style("left", d3.event.pageX - 50 + "px")
.style("top", d3.event.pageY - 70 + "px")
.style("display", "inline-block")
.html("Year: " + (d.Time) + "<br>" + "Value: " + (d.Value));
d3.select(this).attr('fill', "#eec42d");
//d3.select(this).attr('class', 'highlight');
//this.setState({ fillColour: 'green' });
}
function onMouseOut(d,i)
{
tooltip.style("display", "none");
d3.select(this).attr('fill', "blue");
}
bars.append('text')
.text(d => d.Value)
.attr('text-anchor', 'middle')
.attr('y', d => yScale(d.Value))
.attr('dy', -5)
;
const topPosition = i => yScale(9000) + i * 15;
const pathBetweenBars = (d, i) => {
const delta = d.Target - i;
const targetValue = barData[d.Target].Value;
const targetX = delta * xScale.bandwidth() - 5;
const sourceY = yScale(d.Value);
const targetY = yScale(targetValue);
const topY = topPosition(i);
return `M 5,${sourceY - 20} V ${topY} H ${targetX} V ${targetY - 25}`;
};
const LABEL_WIDTH = 50;
const midPosition = (d, i) => {
const delta = d.Target - i;
return delta * xScale.bandwidth() / 2;
}
bars.filter(d => d.Target != null)
.append('path')
.attr('d', (d, i) => pathBetweenBars(d, i))
.style('stroke', 'gray')
.style('fill', 'none')
.attr('marker-end', 'url(#arrowhead)')
bars.filter((d) => d.Target != null)
.append('rect')
.attr('x', (d, i) => midPosition(d, i) - LABEL_WIDTH / 2)
.attr('y', (d, i) => topPosition(i) - 10)
.attr('width', LABEL_WIDTH)
.attr('height', 20)
.attr('rx', 10)
.style('fill', 'white')
.style('stroke', 'gray');
bars.filter((d, i) => d.Target != null)
.append('text')
.text((d, i) => `${barData[d.Target].Value > d.Value ? '+' : ''}${Math.round((barData[d.Target].Value / d.Value * 100) - 100)}%`)
.attr('x', (d, i) => midPosition(d, i))
.attr('y', (d, i) => topPosition(i) + 3)
.attr('text-anchor', 'middle')
.style('fill', 'black');
#graph {
width: 600px;
height: 400px;
}
text {
font-size: 12px;
font-family: "Ubuntu";
}
.toolTip {
position: absolute;
display: none;
min-width: 80px;
height: auto;
background: none repeat scroll 0 0 #ffffff;
border: 1px solid #6F257F;
padding: 5px;
text-align: left;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="graph">
</div>
这篇关于如何添加过滤器以创建从一个条到另一个条的单个路径(不在循环中)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!