我正在尝试使用d3.js v5向折线图添加缩放功能。为了显示曲线上的实际鼠标位置,有一个工具提示,带有y值(SVG元素),两条虚线和一个在交界点处的圆。工具提示元素在放大时不得更改其大小。虚线和圆圈在“焦点”组中,该组对d3.event.transform的缩放事件有反应。这些元素的行为是正确的,放大后它们不会更改其大小并正确翻译。 SVG元素位于单独的组“ focustooltip”中,该组未绑定到放大事件。放大文本时,请勿调整大小(这是正确的行为),但文本的翻译不正确(放大时,文本会远离结点,但不应如此)。
查看图像以可视化行为:
未缩放:每个元素都在正确的位置: unzoomed
缩放:文本翻译错误。 zoomed
这是我的实际代码:
<!DOCTYPE html>
<meta charset="utf-8">
<html>
<head>
<script type = "text/javascript" src = "https://d3js.org/d3.v5.min.js"></script>
<style>
.axis path,.axis line
{
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
path.line
{
fill: none;
stroke: rgb(52,101,164);
stroke-width: 2px;
vector-effect: non-scaling-stroke; /* line height won't scale */
pointer-events: none;
}
.overlay
{
fill: none;
pointer-events: all;
}
.focus circle
{
fill: #F1F3F3;
stroke: #6F257F;
stroke-width: 2px;
}
text.y2
{
fill: rgb(239,41,41);
}
line.x
{
stroke: blue;
stroke-width: 2px;
stroke-dasharray: 3,3;
vector-effect: non-scaling-stroke; /* line height won't scale */
pointer-events: none;
}
line.y
{
stroke: blue;
stroke-width: 2px;
stroke-dasharray: 3,3;
vector-effect: non-scaling-stroke; /* line height won't scale */
pointer-events: none;
}
</style>
</head>
<body>
<div id="example"></div>
<div id="example_2"></div>
<script>
function lineChart()
{
// set the dimensions and margins of the graph
var margin = {top: 40,right: 20,bottom: 60,left: 50},width = 960 - margin.left - margin.right,height = 500 - margin.top - margin.bottom;
// set the ranges
var x = d3.scaleTime().range([0,width]);
var y = d3.scaleLinear().range([height,0]);
var formatDate = d3.timeFormat("%d-%b"),bisectDate = d3.bisector(function(d) { return d.year; }).left;
// define the line
var valueline = d3.line()
.x(function(d) { return x(d.year); })
.y(function(d) { return y(d.population); });
function chart(selection)
{
selection.each(function(data)
{
var actScale=1.0;
var zoom = d3.zoom()
.scaleExtent([1,Infinity])
.translateExtent([[0,0],[width,height]])
.extent([[0,height]])
.on("zoom",zoomed);
// append the svg obgect to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select(this)
.append("svg")
.attr("width",width + margin.left + margin.right)
.attr("height",height + margin.top + margin.bottom)
.call(zoom)
.append("g")
.attr("transform","translate(" + margin.left + "," + margin.top + ")");
// Scale the range of the data
x.domain(d3.extent(data,function(d) { return d.year; }));
y.domain([0,d3.max(data,function(d) { return d.population; })]);
var xAxis = d3.axisBottom(x);
var yAxis = d3.axisLeft(y);
svg.append("defs").append("clipPath")
.attr("id","clip")
.append("rect")
.attr("width",width)
.attr("height",height);
var clipping = svg.append("g")
.attr("clip-path","url(#clip)");
var path = clipping.append("path")
.data([data])
.attr("class","line")
.attr("d",valueline);
var focus = clipping.append("g")
.style("display","none");
var focustooltip = clipping.append("g")
.style("display","none");
// append the x line
focus.append("line")
.attr("class","x")
.style("stroke","blue")
.style("stroke-dasharray","3,3")
// .style("stroke-width","2px")
.style("opacity",0.5)
.attr("y1",0)
.attr("y2",height);
// append the y line
focus.append("line")
.attr("class","y")
.style("stroke",3")
.style("opacity",0.5)
.attr("x1",width)
.attr("x2",width);
focustooltip.append("text")
.attr("class","y2")
.attr("dx",8)
.attr("dy","-.3em");
// append the circle at the intersection
var circle = focus.append("circle")
.attr("class","y")
.style("fill","none")
.style("stroke","blue")
.attr("r",4);
// append the rectangle to capture mouse
var rect = clipping.append("rect")
.attr("width",height)
.style("fill","none")
.style("pointer-events","all")
.on("mouseover",function() { focus.style("display",null); focustooltip.style("display",null);})
.on("mouseout","none"); focustooltip.style("display",null);})
.on("mousemove",mousemove);
function mousemove()
{
var x0 = x.invert(d3.mouse(this)[0]),i = bisectDate(data,x0,1),d0 = data[i - 1],d1 = data[i],d = x0 - d0.year > d1.year - x0 ? d1 : d0;
focus.select("circle.y")
.attr("transform","translate(" + x(d.year) + "," + y(d.population) + ")");
focus.select(".x")
.attr("transform"," + y(d.population) + ")")
.attr("y2",height - y(d.population));
focus.select(".y")
.attr("transform","translate(" + width * -1 + "," + y(d.population) + 1 + ")")
.attr("x2",width + width);
focustooltip.select("text.y2")
.attr("transform"," + y(d.population) + ")")
.text(d.population);
}
var gX = svg.append("g")
.attr("class","axis axis--x")
.attr("transform","translate(0," + height + ")")
.call(xAxis);
var gY = svg.append("g")
.attr("class","axis axis--y")
.call(yAxis);
//Create title
var title = svg.append("text")
.attr("x",(width / 2))
.attr("y",0 - (margin.top / 2))
.attr("text-anchor","middle")
.style("font-size","16px")
.text("x vs y");
// text label for the x axis
svg.append("text")
.attr("transform","translate(" + (width/2) + "," + (height + margin.top) + ")")
.style("text-anchor","middle")
.text("x-axis");
// text label for the y axis
svg.append("text")
.attr("transform","rotate(-90)")
.attr("y",0 - margin.left)
.attr("x",0 - (height / 2))
.attr("dy","1em")
.style("text-anchor","middle")
// .text(yaxis_lable);
.text("y-axis");
function zoomed()
{
path.attr("transform",d3.event.transform);
focus.attr("transform",d3.event.transform);
// focustooltip.attr("transform",0)scale(1)");
focus.select("circle").attr("r",4 / d3.event.transform.k);
focus.select("circle").attr("stroke-width",2 / d3.event.transform.k);
rect.attr("transform",d3.event.transform);
gX.call(xAxis.scale(d3.event.transform.rescaleX(x)));
gY.call(yAxis.scale(d3.event.transform.rescaleY(y)));
}
});
}
return chart;
}
var chart = lineChart();
// Get the data
d3.csv("data_graph.csv")
.then(function(data)
{
// format the data
data.forEach(function(d)
{
d.year = d.year;
d.population = +d.population;
})
d3.select("#example")
.datum(data)
.call(chart);
});
// Get the data
d3.csv("data_graph.csv")
.then(function(data)
{
// format the data
data.forEach(function(d)
{
d.year = d.year;
d.population = +d.population;
})
d3.select("#example_2")
.datum(data)
.call(chart);
});
</script>
</body>
</html>