javascript - 在 D3.js V5 中,带箭头的路径向后方向未正确显示
问题描述
我试图实现如下图表,这个问题是使图表有效地呈现负值的延续:
我刚刚反转了差异绝对值和百分比文本计算的计算,并使标记开始而不是标记结束。但标记不像向下箭头那样正常。我面临的唯一问题与箭头方向有关。以下是相同的代码:
//negative value consideration code
const barData = [{
"Time": "2019",
"Value": 538865
},
{
"Time": "2020",
"Value": 645375
},
{
"Time": "2021",
"Value": -434535
},
{
"Time": "2022",
"Value": -534595
},
{
"Time": "2023",
"Value": -834545
},
{
"Time": "2024",
"Value": -534555
},
{
"Time": "2025",
"Value": 634565
},
{
"Time": "2026",
"Value": 33455
}
];
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: 50,
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)
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
//To add tooltip for bar
const 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
const yAxisMax = barData.reduce((max, item) => Math.max(max, item.Value), 0) * 1.5;
const yAxisMin = barData.reduce((min, item) => Math.min(min, item.Value), 0) * 1.5;
const yAxisRange = Math.max(yAxisMax, Math.abs(yAxisMin));
const yScale = d3.scaleLinear()
.domain([-yAxisRange, yAxisRange])
.range([height, 0]);
const yAxis = d3.axisLeft(yScale).ticks(4);
svg.append('g')
.call(yAxis);
const bars = svg.selectAll('g.bar')
.data(barData)
.enter()
.append('g')
.classed('bar', true)
.attr('transform', d => `translate(${xScale(d.Time) + xScale.bandwidth() / 2}, 0)`);
bars.append('rect')
.attr('x', -20)
.attr('width', 40)
.attr('y', d => Math.min(yScale(d.Value), height/2))
.attr('height', d => d.Value > 0 ?
(height/2 - yScale(d.Value)) :
(yScale(d.Value) - height/2)
)
.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");
}
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('alignment-baseline', d => d.Value > 0 ? 'baseline' : 'hanging')
.attr('y', d => yScale(d.Value))
.attr('dy', d => d.Value > 0 ? -5 : 5);
bars.filter((d, i) => i < barData.length - 1)
.append('path')
.attr('d', (d, i) => `M 5,${d.Value < 0 && barData[i + 1].Value < 0? Math.max(yScale(d.Value) + 20, height/2): Math.min(yScale(d.Value) - 20, height/2)} V ${d.Value < 0 && barData[i + 1].Value < 0 ? Math.max(yScale(d.Value) , yScale(barData[i + 1].Value) )+40 : Math.min(yScale(d.Value), yScale(barData[i + 1].Value)) - 40} H ${xScale.bandwidth() - 5} V ${d.Value < 0 && barData[i + 1].Value < 0 ? Math.max(yScale(barData[i + 1].Value) + 25, height/2 + 10): Math.min(yScale(barData[i + 1].Value) - 25, height/2 - 10)}`)
.style('stroke', 'gray')
.style('fill', 'none')
.attr('marker-start', 'url(#arrowhead)')
bars.filter((d, i) => i < barData.length - 1)
.append('rect')
.attr('x', 13)
.attr('y', (d, i) => d.Value < 0 && barData[i + 1].Value < 0 ? Math.max(yScale(d.Value), yScale(barData[i + 1].Value)) + 15 : Math.min(yScale(d.Value), yScale(barData[i + 1].Value)) - 55)
.attr('width', xScale.bandwidth() - 25)
.attr('height', 40)
.attr('rx', 20)
.style('fill', "white")
.style('stroke', 'gray');
bars.filter((d, i) => i < barData.length - 1)
.append('text')
.html((d, i) => `${d.Value > barData[i + 1].Value ? '+' : ''}${Math.round(((d.Value - barData[i + 1].Value) / Math.abs(barData[i + 1].Value))* 100)}%`)
.attr('x', xScale.bandwidth() / 2)
.attr('y', (d, i) => d.Value < 0 && barData[i + 1].Value < 0 ? Math.max(yScale(d.Value), yScale(barData[i + 1].Value)) + 44 : Math.min(yScale(d.Value), yScale(barData[i + 1].Value)) - 36)
.attr('text-anchor', 'middle')
.style('fill', 'black');
bars.filter((d, i) => i < barData.length - 1)
.append('text')
.html((d, i) => `${Math.round(d.Value - barData[i + 1].Value)}`)
.attr('x', xScale.bandwidth() / 2)
.attr('y', (d, i) => d.Value < 0 && barData[i + 1].Value < 0 ? Math.max(yScale(d.Value), yScale(barData[i + 1].Value)) + 34 : Math.min(yScale(d.Value), yScale(barData[i + 1].Value)) - 26)
.attr('text-anchor', 'middle')
.style('fill', 'black');
const xAxisG = svg.append('g')
.attr("transform", `translate(0,${height/2})`)
.call(xAxis);
xAxisG.selectAll('.tick').each(function() {
const tick = d3.select(this);
const text = tick.select('text').text();
const data = barData.find(d => d.Time === text);
if (data.Value < 0) {
tick.select('text').style('fill', 'white');
tick.select('line').style('stroke', 'white');
}
})
#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>
解决方案
我得到了答案,即代替 orient = auto 使用“auto-start-reverse”。
推荐阅读
- java - 使用 java 解析 USvideos.csv 文件
- javascript - FCM Web 转换 Android webview
- c - 不知道如何检查 C 中的输入参数
- javascript - 调用 Firebaseui start(...) 时的 preventScroll
- reactjs - Axios - 被 CORS 策略阻止:请求的资源上不存在“Access-Control-Allow-Origin”标头
- powershell - Powershell中变量定义的文件路径内的Out-File文本输入
- python - 获取数据框列中的类型列表,跳过 NaN 单元格
- python-3.x - 将答案四舍五入到小数点后两位
- python - 如何实现区分字符串长度和该字符串中的字符的函数
- blockchain - 在连接到 Rinkby 测试网络时,它给了我错误发生不匹配