如何解决如何在d3中的每行中制作带有虚线段的多折线图
数据如下:
var data = [
{
name: 'USA',values: [
{ date: '2000',price: '100',isDashed:true },{ date: '2001',price: '110',{ date: '2002',price: '145',isDashed:false },...,{
name: 'Canada',price: '200',isDashed:true },price: '120',price: '33',isDashed:true }...
]
}
]
我正在关注此链接https://codesandbox.io/s/multi-line-chart-example-forked-hjix5?file=/src/index.js
问题是,当数据为true时,在我的数据中有一个isisshed标志,例如,我需要在图表中使用虚线段(例如)。
var data = [{
name: "USA",values: [{
date: "2000",price: "100",r: 1
},{
date: "2001",price: "110",{
date: "2002",price: "145"
},{
date: "2003",price: "241"
},{
date: "2004",price: "101"
},{
date: "2005",price: "90"
},{
date: "2006",price: "10"
},{
date: "2007",price: "35"
},{
date: "2008",price: "21"
},{
date: "2009",price: "201"
}
]
},{
name: "Canada",price: "200"
},price: "120"
},price: "33"
},price: "51"
},price: "190"
},price: "85"
},price: "221"
},price: "101"
}
]
},{
name: "Maxico",price: "50"
},price: "5"
},price: "71"
},price: "20"
},price: "9"
},price: "220"
},price: "235"
},price: "61"
},price: "10"
}
]
}
];
var width = 500;
var height = 300;
var margin = 50;
var duration = 250;
var lineOpacity = "0.25";
var lineOpacityHover = "0.85";
var otherLinesOpacityHover = "0.1";
var lineStroke = "1.5px";
var lineStrokeHover = "2.5px";
var circleOpacity = "0.85";
var circleOpacityOnLineHover = "0.25";
var circleRadius = 3;
var circleRadiusHover = 6;
/* Format Data */
var parseDate = d3.timeParse("%Y");
data.forEach(function(d) {
d.values.forEach(function(d) {
d.date = parseDate(d.date);
d.price = +d.price;
d.r = +d.r;
});
});
/* Scale */
var xScale = d3
.scaleTime()
.domain(d3.extent(data[0].values,(d) => d.date))
.range([0,width - margin]);
var yScale = d3
.scaleLinear()
.domain([0,d3.max(data[0].values,(d) => d.price)])
.range([height - margin,0]);
var color = d3.scaleOrdinal(d3.schemeCategory10);
/* Add SVG */
var svg = d3
.select("#chart")
.append("svg")
.attr("width",width + margin + "px")
.attr("height",height + margin + "px")
.append("g")
.attr("transform",`translate(${margin},${margin})`);
/* Add line into SVG */
var line = d3
.line()
.x((d) => xScale(d.date))
.y((d) => yScale(d.price));
let lines = svg.append("g").attr("class","lines");
lines
.selectAll(".line-group")
.data(data)
.enter()
.append("g")
.attr("class","line-group")
.on("mouseover",function(d,i) {
svg
.append("text")
.attr("class","title-text")
.style("fill",color(i))
.text(d.name)
.attr("text-anchor","middle")
.attr("x",(width - margin) / 2)
.attr("y",5)
.attr("r",5);
})
.on("mouseout",function(d) {
svg.select(".title-text").remove();
})
.append("path")
.attr("class","line")
.attr("d",(d) => line(d.values))
.style("stroke",(d,i) => color(i))
.style("opacity",lineOpacity)
.on("mouseover",function(d) {
d3.selectAll(".line").style("opacity",otherLinesOpacityHover);
d3.selectAll(".circle").style("opacity",circleOpacityOnLineHover);
d3.select(this)
.style("opacity",lineOpacityHover)
.style("stroke-width",lineStrokeHover)
.style("cursor","pointer");
})
.on("mouseout",lineOpacity);
d3.selectAll(".circle").style("opacity",circleOpacity);
d3.select(this)
.style("stroke-width",lineStroke)
.style("cursor","none");
});
/* Add circles in the line */
lines
.selectAll("circle-group")
.data(data)
.enter()
.append("g")
.style("fill",i) => color(i))
.selectAll("circle")
.data((d) => d.values)
.enter()
.append("g")
.attr("class","circle")
.on("mouseover",function(d) {
d3.select(this)
.style("cursor","pointer")
.append("text")
.attr("class","text")
.text(`${d.price}`)
.attr("x",(d) => xScale(d.date) + 5)
.attr("y",(d) => yScale(d.price) - 10);
})
.on("mouseout","none")
.transition()
.duration(duration)
.selectAll(".text")
.remove();
})
.append("circle")
.attr("cx",(d) => xScale(d.date))
.attr("cy",(d) => yScale(d.price))
.attr("r",circleRadius)
.style("opacity",circleOpacity)
.on("mouseover",function(d) {
d3.select(this)
.transition()
.duration(duration)
.attr("r",circleRadiusHover);
})
.on("mouseout",function(d) {
d3.select(this).transition().duration(duration).attr("r",circleRadius);
});
/* Add Axis into SVG */
var xAxis = d3.axisBottom(xScale).ticks(5);
var yAxis = d3.axisLeft(yScale).ticks(5);
svg
.append("g")
.attr("class","x axis")
.attr("transform",`translate(0,${height - margin})`)
.call(xAxis);
svg
.append("g")
.attr("class","y axis")
.call(yAxis)
.append("text")
.attr("y",15)
.attr("transform","rotate(-90)")
.attr("fill","#000")
.text("Total values");
.App {
font-family: sans-serif;
text-align: center;
}
svg {
font-family: Sans-Serif,Arial;
}
.line {
stroke-width: 2;
fill: none;
}
.axis path {
stroke: black;
}
.text {
font-size: 12px;
}
.title-text {
font-size: 12px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div class="App">
<div id="chart" />
</div>
解决方法
正如我在评论中说的那样,您似乎希望线段既是点缀又是较浅的颜色。最简单的解决方法是仅绘制两个单独的线段。较难的解决方案是将stroke-dasharray和线性渐变结合使用作为笔触,但这可能容易出错。
由于您似乎不太在意,因此以下代码显示了更简单的解决方法:
var data = [{
name: "USA",values: [{
date: "2000",price: "100",r: 1,isDashed: true
},{
date: "2001",price: "110",{
date: "2002",price: "145",{
date: "2003",price: "241",{
date: "2004",price: "101",{
date: "2005",price: "90",{
date: "2006",price: "10",{
date: "2007",price: "35"
},{
date: "2008",price: "21"
},{
date: "2009",price: "201"
}
]
},{
name: "Canada",price: "200"
},price: "120"
},price: "33"
},price: "51",price: "190",price: "120",price: "85"
},price: "221"
},price: "101"
}
]
},{
name: "Maxico",price: "50"
},price: "10"
},price: "5",price: "71"
},price: "20"
},price: "9"
},price: "220"
},price: "235"
},price: "61"
},price: "10"
}
]
}
];
var width = 500;
var height = 300;
var margin = 50;
var duration = 250;
var lineOpacity = "0.25";
var lineOpacityHover = "0.85";
var otherLinesOpacityHover = "0.1";
var lineStroke = "1.5px";
var lineStrokeHover = "2.5px";
var circleOpacity = "0.85";
var circleOpacityOnLineHover = "0.25";
var circleRadius = 3;
var circleRadiusHover = 6;
/* Format Data */
var parseDate = d3.timeParse("%Y");
data.forEach(function(d) {
d.values.forEach(function(d) {
d.date = parseDate(d.date);
d.price = +d.price;
d.r = +d.r;
});
});
/* Scale */
var xScale = d3
.scaleTime()
.domain(d3.extent(data[0].values,(d) => d.date))
.range([0,width - margin]);
var yScale = d3
.scaleLinear()
.domain([0,d3.max(data[0].values,(d) => d.price)])
.range([height - margin,0]);
var color = d3.scaleOrdinal(d3.schemeCategory10);
/* Add SVG */
var svg = d3
.select("#chart")
.append("svg")
.attr("width",width + margin + "px")
.attr("height",height + margin + "px")
.append("g")
.attr("transform",`translate(${margin},${margin})`);
/* Add line into SVG */
var line = d3
.line()
.defined(d => !isNaN(d.price)) // to make it deal with NaNs
.x((d) => xScale(d.date))
.y((d) => yScale(d.price));
let lines = svg.append("g").attr("class","lines");
let lineGroups = lines
.selectAll(".line-group")
.data(data)
.enter()
.append("g")
.attr("class","line-group")
.on("mouseover",function(d,i) {
svg
.append("text")
.attr("class","title-text")
.style("fill",color(i))
.text(d.name)
.attr("text-anchor","middle")
.attr("x",(width - margin) / 2)
.attr("y",5)
.attr("r",5);
})
.on("mouseout",function(d) {
svg.select(".title-text").remove();
});
function getDashedParts(values,lookingForDashed) {
const results = [];
let previousWasMatch = false;
// For every node we add,we need also the node just before,// so we can draw a line between them
values.forEach((v,i) => {
if(lookingForDashed === !!v.isDashed) {
if (!previousWasMatch && i > 0) {
results.push(values[i - 1]);
}
results.push(v);
previousWasMatch = true;
} else {
results.push({ date: v.date,price: NaN });
previousWasMatch = false;
}
});
// console.log(results);
return results;
}
lineGroups
.append("path")
.attr("class","line")
.attr("d",(d) => line(getDashedParts(d.values,false)));
lineGroups
.append("path")
.attr("class","line line-dashed")
.attr("d",true)));
lineGroups.each(function(d,i) {
const lineParts = d3.select(this).selectAll("path");
lineParts
.style("stroke",color(i))
.style("opacity",lineOpacity)
.on("mouseover",() => {
d3.selectAll(".line").style("opacity",otherLinesOpacityHover);
d3.selectAll(".circle").style("opacity",circleOpacityOnLineHover);
lineParts
.style("opacity",lineOpacityHover)
.style("stroke-width",lineStrokeHover)
.style("cursor","pointer");
})
.on("mouseout",lineOpacity);
d3.selectAll(".circle").style("opacity",circleOpacity);
lineParts
.style("stroke-width",lineStroke)
.style("cursor","none");
});
});
/* Add circles in the line */
lines
.selectAll("circle-group")
.data(data)
.enter()
.append("g")
.style("fill",(d,i) => color(i))
.selectAll("circle")
.data((d) => d.values)
.enter()
.append("g")
.attr("class","circle")
.on("mouseover",function(d) {
d3.select(this)
.style("cursor","pointer")
.append("text")
.attr("class","text")
.text(`${d.price}`)
.attr("x",(d) => xScale(d.date) + 5)
.attr("y",(d) => yScale(d.price) - 10);
})
.on("mouseout","none")
.transition()
.duration(duration)
.selectAll(".text")
.remove();
})
.append("circle")
.attr("cx",(d) => xScale(d.date))
.attr("cy",(d) => yScale(d.price))
.attr("r",circleRadius)
.style("opacity",circleOpacity)
.on("mouseover",function(d) {
d3.select(this)
.transition()
.duration(duration)
.attr("r",circleRadiusHover);
})
.on("mouseout",function(d) {
d3.select(this).transition().duration(duration).attr("r",circleRadius);
});
/* Add Axis into SVG */
var xAxis = d3.axisBottom(xScale).ticks(5);
var yAxis = d3.axisLeft(yScale).ticks(5);
svg
.append("g")
.attr("class","x axis")
.attr("transform",`translate(0,${height - margin})`)
.call(xAxis);
svg
.append("g")
.attr("class","y axis")
.call(yAxis)
.append("text")
.attr("y",15)
.attr("transform","rotate(-90)")
.attr("fill","#000")
.text("Total values");
.App {
font-family: sans-serif;
text-align: center;
}
svg {
font-family: Sans-Serif,Arial;
}
.line {
stroke-width: 2;
fill: none;
}
.line-dashed {
stroke-dasharray: 5,5;
}
.axis path {
stroke: black;
}
.text {
font-size: 12px;
}
.title-text {
font-size: 12px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div class="App">
<div id="chart" />
</div>
我所做的:
- 我在您的
isDefined()
的定义中添加了d3.line()
,以便可以处理NaN; - 我为每个数组过滤了isDashed true或false的值。当我找到一个匹配的元素而前一个不匹配时,我将两者都添加到了结果集中。这样,将在它们之间画一条线,并且不会出现间隙;
- 我稍微改变了鼠标悬停的行为,因此,如果将鼠标悬停在虚线和实线部分上,都将突出显示。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。