如何有条件地设置属性名称



我试图根据我的数据中的一列动态设置属性名称,但它不工作。

根据data中的"isReverse"列,设置路径箭头属性名为marker-endmarker-start。我创建了一个函数,并在.attr()函数中调用该函数,但它抛出脚本错误。

这个要求基本上是根据isReverse列值反转箭头方向。并且从一个条到目标条的差值计算值也将反转。

function chkArrowDir(d){
return d.isReverse == "false" ? 'marker-end' : 'marker-start';
}
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)')
.attr(d => chkArrowDir(d), 'url(#arrowhead)')

下面是完整的代码:

var barData = [{
"Time": "Bar1",
"Value": 5388,
"Target": 1,
"isReverse": "true"
},
{
"Time": "Bar2",
"Value": 6453,
"Target": 3,
"isReverse": "false"
},
{
"Time": "Bar3",
"Value": 3345,
"Target": -1,
"isReverse": "false"
},
{
"Time": "Bar4",
"Value": 5345,
"Target": 0,
"isReverse": "false"
}];
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) => {
if(d.Target != -1)
{
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}`;
}
else
return `M 5,${-100} V ${-100} H ${-1000} V ${-0}`;
};         
const LABEL_WIDTH = 50;
const midPosition = (d, i) => {
if(d.Target != -1)
{
const delta = d.Target - i;
return delta * xScale.bandwidth() / 2;
}
else
{
return -1000;
}
}
function chkArrowDir(d)
{
return d.isReverse == "false" ? 'marker-end' : 'marker-start';
}
bars.filter(d => d.Target != null)
.append('path')
.attr('d', (d, i) => pathBetweenBars(d, i))
.style('stroke', 'gray')
.style('fill', 'none')
.attr(d => chkArrowDir(d), '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) => `${d.Target != -1 ? barData[d.Target].Value > d.Value ? '+' : '':""}${d.Target != -1 ? 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>

在D3中,attr方法…

选择。attr(名称(价值))

…不能接收一个函数作为第一个参数,这里称为name。如果您查看源代码,您将看到它必须是一个字符串。实际上,即使您传递另一个原语,例如数字,它也会通过以下方式转换为字符串:

var prefix = name += ""

一个可能的解决方案是使用null作为值。在内部,使用null将调用element.removeAttribute(),因此在实践中,您只在值非空时设置该属性。

因此,它应该是:
.attr("marker-end", d => d.isReverse === "false" ? "url(#arrowhead)" : null)
.attr("marker-start", d => d.isReverse === "false" ? null : "url(#arrowhead)")

修改后的代码如下:

var barData = [{
"Time": "Bar1",
"Value": 5388,
"Target": 1,
"isReverse": "true"
},
{
"Time": "Bar2",
"Value": 6453,
"Target": 3,
"isReverse": "false"
},
{
"Time": "Bar3",
"Value": 3345,
"Target": -1,
"isReverse": "false"
},
{
"Time": "Bar4",
"Value": 5345,
"Target": 0,
"isReverse": "false"
}];
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) => {
if(d.Target != -1)
{
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}`;
}
else
return `M 5,${-100} V ${-100} H ${-1000} V ${-0}`;
};         
const LABEL_WIDTH = 50;
const midPosition = (d, i) => {
if(d.Target != -1)
{
const delta = d.Target - i;
return delta * xScale.bandwidth() / 2;
}
else
{
return -1000;
}
}
function chkArrowDir(d)
{
return d.isReverse == "false" ? 'marker-end' : 'marker-start';
}
bars.filter(d => d.Target != null)
.append('path')
.attr('d', (d, i) => pathBetweenBars(d, i))
.style('stroke', 'gray')
.style('fill', 'none')
.attr("marker-end", d => d.isReverse === "false" ? "url(#arrowhead)" : null)
.attr("marker-start", d => d.isReverse === "false" ? null : "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) => `${d.Target != -1 ? barData[d.Target].Value > d.Value ? '+' : '':""}${d.Target != -1 ? 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>

最新更新