I have a timeline graph that has datapoints indicating specific events. I have tried to build a zoom function into the graph but have reached a dead end. I can zoom in on the axis, but cannot reposition the datapoins into the new scale. I've been through many of the d3 bl.ocks but have not found a solution.
I have a minimal version in jsfiddle here: https://jsfiddle.net/q73ggw3e/2/
Main parts are:
the zoom:
var zoom = d3.zoom()
.scaleExtent([1, Infinity])
.translateExtent([[0, 0], [width, height]])
.extent([[0, 0], [width, height]])
.on("zoom", zoomed);
The datapoints:
svg.append("g")
.selectAll("logo")
.attr("class", "logo")
.data(data)
.enter()
.append("circle")
.attr("cy", 40)
.attr("cx", function(d) { return x(d);})
.attr("r", 10)
.style("fill", "#000000")
The zoom area:
svg.append("rect")
.attr("class", "zoom")
.attr("width", width)
.attr("height", height)
.call(zoom);
And the zoomed function:
function zoomed() {
svg.select(".axis--x")
.call(xAxis.scale(d3.event.transform.rescaleX(x)));
var new_x = d3.event.transform.rescaleX(x);
svg.selectAll(".logo")
.attr("cx", function(d) { return new_x(d);});
};
I should be binding the (.logo) class of circles to the zoom event too, but for some reason only the axis zooms...
Where am I going wrong? Or is there some completely other way of doing this...
If it matters, the datapoints will have drag n' drop, mouseover and click functions too. These work fine at the moment, but the zoom rect blocks the pointer events. But that is to be sorted after the zoom is working...
weird chart you make
var margin = {
top: 30,
right: 20,
bottom: 30,
left: 120
},
width = 800 - margin.left - margin.right,
height = 210 - margin.top - margin.bottom;
var svg = d3.select(".zoom")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var x = d3.scaleTime()
.rangeRound([0, width]);
var xAxis = d3.axisTop(x)
var zoom = d3.zoom()
.scaleExtent([1, Infinity])
.translateExtent([[0, 0], [width, height]])
.extent([[0, 0], [width, height]])
.on("zoom", zoomed);
var parseTime = d3.timeParse("%Y-%m-%d");
var lanes = ["A", "B", "C"]
var lanesLength = lanes.length
var a = ["2010-02-12", "2013-08-15", "2015-01-23", "2017-01-22"]
var data = [];
a.forEach(function (d){
data.push(parseTime(d));
});
var today = new Date();
x.domain([d3.min(data, function(d) { return d;}), today]);
y.domain([0, lanesLength]);
svg.append("g")
.attr("class", "axis axis--x")
.style("stroke-width", 2)
.attr("transform", "translate(0)")
.call(xAxis);
svg.append("g")
.selectAll(".lanetext")
.data(lanes)
.enter()
.append("text")
.text(function(d) { return d;})
.attr("x", -margin.right)
.attr("y", function(d, i) { return y(i + .5);})
.attr("dy", ".5ex")
.attr("text-anchor", "end")
.attr("class", "lanetext")
svg.append("g")
.attr("class", "logomu")
.selectAll("logomu")
.data(data)
.enter()
.append("circle")
.attr("cy", 40)
.attr("class", "logo")
.attr("cx", function(d) { return x(d);})
.attr("r", 10)
.style("fill", "#000000")
svg.append("rect")
.attr("class", "zoom")
.attr("width", width)
.attr("height", height)
.call(zoom);
function zoomed() {
svg.select(".axis--x")
.call(xAxis.scale(d3.event.transform.rescaleX(x)));
var new_x = d3.event.transform.rescaleX(x);
svg.selectAll(".logo")
.attr("cx", function(d) { return new_x(d);});
};
.axis line{
stroke: #000
}
.axis path{
stroke: #000
}
.axis text{
fill: #000
}
.lanetext {
fill: #000
}
.zoom {
cursor: move;
fill: none;
pointer-events: all;
}
.logo {
fill: #000;
}
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.3/d3.min.js"></script>
<body>
<div class="zoom">
</div>
</body>
I should be binding the (.logo) class of circles to the zoom event too, but for some reason only the axis zooms...
if there is a class name logo? inspec the html make sure you append it right
Where am I going wrong? Or is there some completely other way of doing this...
you didnt check what you draw. Yup just need experience