I've made zooming and brushing working together. The only problem is, when I've set the particular period on X axis via brushing and then trying to use zoom (on mouse drag or mouse wheel), it resets previous selected scales, so zoom doesn't store x axis domain that was set via brushing before.
zoomRight = d3.behavior.zoom()
.x(xScale)
.y(yRightScale)
.scaleExtent([1,20])
zoomed = ->
zoomRight.scale(zoom.scale()).translate(zoom.translate())
canvas.select("._x._axis").call xAxis
canvas.select(".axisLeft").call yLeftAxis
canvas.select(".axisRight").call yRightAxis
canvas.select(".y.grid").call make_y_axis().tickSize(-width, 0, 0).tickFormat("")
canvas.select(".line1").attr("d", line1(data))
canvas.select(".line2").attr("d", line2(data))
brush.extent(xScale.domain())
canvas.select(".brush").call(brush)
zoom = d3.behavior.zoom()
.x(xScale)
.y(yLeftScale)
.scaleExtent([1,20]) # 20x times zoom
.on("zoom", zoomed)
Full code is here fiddle. How can I force zoom to remember the previous brushing selection(position)?
Solved the problem by adding the following lines on brush:
zoom.x(xScale)
zoom.translate()
Working example is here.
Related
I am trying to draw a vertical line marker in my graph in D3. It is modeled off of this example: https://bl.ocks.org/mbostock/34f08d5e11952a80609169b7917d4172
My issue is that after I draw my line, it doesn't move as I zoom/scroll the graph. An example is shown below:
Currently, I have it calculated as a d3.area().
this.pastDateArea = d3.area()
.x(function(d) { return this.x(this.props.pastDate.toDate()) }.bind(this))
.y0(0)
.y1(function(d) { return this.height }.bind(this))
It is appended as
var pastDateData = [{x:this.props.pastDate.toDate(), y:150}]
this.focus.append("path")
.datum(pastDateData)
.attr("class", "area")
.attr("d", this.pastDateArea)
and zoomed/brushed using
//zoom
var t = d3.event.transform;
this.x.domain(t.rescaleX(this.x2).domain());
//brush
this.svg.select(".zoom").call(this.zoom.transform, d3.zoomIdentity
.scale(this.width / (s[1] - s[0]))
.translate(-s[0], 0));
I know there are similar questions to this one (namely, Draw a vertical line representing the current date in d3 gantt chart) but none of them include the zooming/panning features I have in my graph.
Please let me know if you need more information and thanks!
The issue is that you are not updating the vertical bar with each zoom event. Using the code of the example you show, several things are done when the chart is zoomed, including as you note:
x.domain(t.rescaleX(x2).domain()); // update x scale
focus.select(".area").attr("d", area); // redraw chart area
While you do give the new area the class of area, d3.select will only pick the first matching element. So, on zoom, only one .area element is updated (the first encountered, generally the first appended). But, replacing this with d3.selectAll(".area") will not generate the intended results as the area function referenced (.attr("d",area) ) is only used for the first area (that of the graph, not of the vertical bar).
A solution is to select each area (the chart and the bar) independently and update the area with their respective area generators. To do so, append the vertical bar with a unique class name, or an id and use that to select it later. Then when updating the graph on zoom or brush you can use:
x.domain(s.map(x2.invert, x2)); // update x scale
focus.select(".area").attr("d", area); // redraw chart area
focus.select(".bar").attr("d", pastDateArea);// redraw vertical bar
Remember that this needs to be done for both zoom and brush. Also, in the given example, a clip path is assigned in the css for .area, so you need to keep that in mind as well.
Here's a modified example.
I am using d3 axis to show the bar graph and i have added zoom interaction to it.
I am adding zoom interaction like this,
d3.zoom(xScale).on("zoom", jQuery.proxy(function(){
//Do somthing on zooming.
},this));
},this));
On Zooming the axis i have to get the at what level the axis is zoomed,
suppose consider i have time axis on zooming it will show the labels in day level, after zooming again it will show the axis in hour level. So how to get whether it is zoomed in day level or hour level after zooming?
Assuming you are targeting d3 version 4, the best way to handle this is with the zoom functionality's rescaleX. Say we have the following zoom handler where x is the scale tied to the x-axis:
function zoomed() {
var newScale = d3.event.transform.rescaleX(x));
console.log(newScale.domain());
}
Would log the bounds (min/max) of your zoomed axis.
I have setup this fiddle to try to figure out why the scale that I pass to d3.behavior.zoom() is being ignored. I am setting the scale to 10 but the effective scale is 1. Then if I try to pan the grid the scale jumps to 10. Same thing happens when I try to zoom the grid. The scale jumps to 10 and then the additional scale is applied. What am I doing wrong? How can I work this out in order to have an effective initial scale value?
You're initializing a var scale = 10 to represent that initial zoom, and you're applying that to zoomer via .scale(10). So zoomer knows the initial scale, but nothing in your code is doing the work of applying the initial scale to the d3 selection vis. The first time the scale is actually applied is once the user interacts, which in turn calls function zoom() {...}, where the scale is finally applied.
The time/place to apply the initial scale, is when you create vis:
//create a group that will hold all the content to be zoomed
var vis = graph.append("svg:g")
.attr("class", "plotting-area")
.attr("transform", "scale(" + scale + ")");// <-- THIS
Here's an updated fiddle
I'm working on a basic linear chart with pan functionality.
I managed to limit the extent to which the chart elements can be dragged by limiting the d3.event.translate values:
var tx = Math.max(0, d3.event.translate[0]),
ty = Math.min(0, d3.event.translate[1]);
All I need now is to limit the x and y axes accordingly. See the example:
http://jsfiddle.net/Q2SWV/
The bars on the chart are limited by 0 when dragging down or to the left. The x and y axes aren't. Any ideas on how to fix the axis problem?
You're very close, but you're missing the final step of updating the zoom behavior with your updated translation coordinates. This will fix your problem since both axes are updated using the zoom. Add the following right after determining tx and ty:
zoom.translate([tx, ty]);
This will apply the limits to your axes. See updated fiddle here: http://jsfiddle.net/mdml/nZD3E/.
In d3 zoom behavior, for linear scale, how can I gradually increase the zoom. I use it like
var zoom = d3.behavior.zoom()
.x(x)
.y(y)
.scaleExtent([1, 10])
.on("zoom", zoomed);
If I understand correctly, the chart will zoom up to 10x. Is it like, each double click will zoom it 1x times, till it reaches 10x? How can I make it even finer step like, 1, 1.2x, 1.4x,..
Thanks.
The zoom level increases as a geometric series. That is, each time you click, it doubles -- 1, 2, 4, etc. The default interaction doesn't allow you to have finer steps, but you can easily do this in your handler code -- there's nothing requiring you to zoom in exactly the same steps as the zoom behaviour's scale value. That is, for a zoom level of 2 you could set the zoom factor to 1.2, for 4 to 1.4 etc.
For finer control in general, you may also want to consider having another input element on the page (e.g. an input box for the zoom level or a slider).