I have a JavaFX Webview which renders a graph (using D3). I want to implement the 'zoom on mouse scroll' on this graph in the JavaFX application (when the mouse is hovered on top of the webview). I was able to do it successfully on a web-browser using the following d3 js code
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.call(d3.behavior.zoom().on("zoom", zoomHandler))
.append('g');
function zoomHandler() {
svg.attr("transform",
"translate(" + d3.event.translate + ")"
+ " scale(" + d3.event.scale + ")");
}
but if I do something similar inside the JavaFX application's WebView, the graph is rendered initially in the WebView but on scroll, just disappears and only reappears if I bring back the mouse scroll to the initial position (when the application was started)
The following code is how I use WebView to render the graph
final URL urlLoadMainGraph = getClass().getResource("html/MainGraph.html");
weMainGraph.load(urlLoadMainGraph.toExternalForm());
Is there something wrong on what is being done ?
Thanks
I cannot provide a solution, but i have the same problem.
The problem is, that you have not specified any scale.
JavaFX is just jumping from the minimum to the maximum scale of the zoom with one "tip" on the mousewheel :(. if you specify it in a certain range, it could work out for you. Use this for example to zoom a little bit outside.
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height).call(d3.behavior.zoom().scaleExtent([0.75, 1]);
best so far... hoping for anwsers!
(PS: i couldnt comment... thats why this kind of answer.
There seems to be a bug in your zoomhandler code example. You might want to start with a handler like the following and go on from there:
function zoomHandler() {
alert('zoom');
}
Here is a more detailed d3 zoom example:
http://bl.ocks.org/mbostock/3892919
And here is a JavaFx wrapper for D3 that also contains a working zoom example:
https://github.com/stefaneidelloth/javafx-d3
https://github.com/stefaneidelloth/javafx-d3/blob/master/javafx-d3-demo/src/main/java/com/github/javafxd3/demo/client/democases/behaviors/ZoomDemo.java
Related
I have a heatmap designed on d3js. I have added two filters for sorting the data. When I'm changing the filters I see the updated data in the console and the chart refreshes. But after each refresh, the earlier visualized chart still remains.
I have tried .remove() and .exit from d3. These didn't seem to work.
Here's the link for my code in codesandbox: https://codesandbox.io/s/5x6qkjw6kp
Quick Answer
The way you have your code setup, you are calling the whole heatmap creation function every time a filter changes, so you're reading data, axes, everything again. Based on that, you could easily clear all elements on the chart and have them recreated from scratch, rather than using the true enter, exit, update pattern, by putting this line before you define your chart.
d3.select(".chart").selectAll("*").remove();
So your code will now look like
//Setting chart width and adjusting for margins
d3.select(".chart").selectAll("*").remove();
const chart = d3
.select(".chart")
.attr("width", width + margins.right + margins.left)
.attr("height", height + margins.top + margins.bottom)
.append("g")
.attr(
"transform",
"translate(" + margins.left + "," + margins.top + ")"
);
Enter Exit Update
This section has been heavily edited, but looking through the code, quite a lot of changes would have to be made to adapt it to make it use Enter/Exit/Update type processes. Think about putting the main creation steps at the start of the chart creation outside of the function, as these only need to run once at load (titles, axes, things like that). Clicking new elements would then not recreate the whole chart, but just alter the dataset used to create the rect objects.
This works by removing all elements which are children of the main chart group before the chart is recreated.
Hope that makes sense, let me know if anything is unclear and I'll tidy it up!
I have a page that includes a dropdown menu to choose one of several bar charts to display. The page improperly reloads when the dropdown selection is changed. I have narrowed the culprit to this line:
$("svg").remove();
When I comment out that line, the HTML changes without the page reloading. But I need that line (or something similar), because I want the previous chart to go away when the new chart is selected.
I've also tried
d3.select("svg").remove();
but the same thing happens.
I've added
event.preventDefault();
but that doesn't help either.
I've made a jsfiddle to show my relevant HTML and JS/d3.
My page is here if seeing the whole thing will help. Note how the page reloads when the dropdown selection is changed.
(Update Jan. 3: I have followed the suggestions in the comments/answers below, but nothing has helped. I'm still having this problem.)
Nope, your page is not reloading. ( If it does reload you will see a spin icon in most of the browsers title). Like #Mansov told in the comment It is the margin that changes when you .remove() and add the svg.
I was having this same problem. Whenever I refreshed my chart, I was doing a .remove() to the entire svg reference, and then calling my build() function again.
In effect, what was happening was the <div> containing the SVG was collapsing to 0 height, causing the page to scroll back up to fit all the remaining content, and then when I rebuilt the chart the page would remain scrolled at the top.
So what I did was make a "frame" for the chart svg to sit on top of:
//===================================================== Initialize build
function initializeBuild() {
bilSVG = this.d3.select(rawSvg)
.append("svg");
calculateMargins();
bilSVG
.attr("width", width)
.attr("height", height)
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height)
.style("opacity", .0)
;
}
and then created a group that held the chart:
//===================================================== build
function build() {
chartGroup = bilSVG
.append("g")
.attr("class","bil chartgroup")
.attr("transform", function(d) {
return "translate(" + width / 2 + "," + width / 2 + ")";
})
.attr("x", width / 2)
.attr("y", width / 2)
;
}
Then when I needed to rebuild the chart, I deleted the group, and not the whole SVG:
//===================================================== rebuild
function rebuild() {
chartGroup = bilSVG.select(".chartgroup");
chartGroup.remove();
build();
}
Hope this helps!
Using the code pattern here, I want to programmatically achieve semantic zooming (in or out). I'm unsure how to get the proper x, y, and scaleExtent terms that are augmented via the zoom:
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.call(d3.behavior.zoom().x(x).y(y).scaleExtent([1, 8]).on("zoom", zoom));
See JSfiddle here. The zoomIn and zoomOut functions should simulate the mousewheel events centered in the SVG.
Here's a solution based on the answer below: http://jsbin.com/dociwuvacu/1
I believe I successfully updated your fiddle, but in case not, here is the type of code you would need:
function zoomIn() {
zoomer.scale(zoomer.scale()+.1);
zoomer.event(svg);
}
function zoomOut() {
zoomer.scale(zoomer.scale()-.1);
zoomer.event(svg);
}
I've generated a D3 visualization (a force directed graph) that requires zooming and panning. I've got 2 problems however when it comes to zooming, and I can't find any decent examples on how I might overcome these problems:
The first problem is I've followed all the examples I can find about zooming, which involves adding groupings and adding a rectangle to ensure that the entire area is zoomeable. If I style the rectangle a slightly opaque blue then I get SVG that looks like this when I zoom out:
The problem with this is that I can zoom in/out absolutely fine while I've got my mouse over the blue rectangle area. The problem is I want this to be fully opaque, which means that when I zoom right out, it's very easy to place the cursor outside of this box and then you're unable to zoom in. Is there a way I can make the SVG itself zoomeable or pick up on these events?
This is how I go about generating the various layers and the zoomed function:
function zoomed() {
group2.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
svg = d3.select(target)
.attr("pointer-events", "all")
.append("svg");
group = svg.append('svg:g')
.call(d3.behavior.zoom().on('zoom', zoomed))
.on("dblclick.zoom", null);
group2 = group.append("g");
rect = group2.append('svg:rect')
.style("opacity", 0.3)
.attr('width', width)
.attr('height', height);
The second problem I have is that I'm trying to automatically size my text based on this http://bl.ocks.org/mbostock/1846692 example. When I've tried this however I seem to be getting text that renders really poorly. It seems to suffer from:
Being difficult to read
Not appearing contained within the circle
Being so small the entire thing compresses (image 2)
var texts = planets.append("text")
.text(function(d) { return d.name; })
.style("font-size", "24px") // initial guess
.style("font-size", function(d) {
return Math.min( 2 * d.size, (2 * d.size - 8) / this.getComputedTextLength() * 24) + "px";
})
.attr("dx", function(d) { return -d.size; })
.attr("dy", ".35em")
.style("fill", "white");
I thought that SVG would just handle this, I understand that some of the font-sizes can come out small, but if you zoom in should that not all sort itself out?
I've got a JSFiddle http://jsfiddle.net/IPWright83/vo7Lpefs/22/ to demonstrate.
I've not yet managed to work out a resolution to my first issue (regarding the zooming box) however I did manage to track down the text rendering issue.
This was actually because the each circle/node had a stroke property to provide the white border. This was also applying to the text element, and when the font was very small the stroke was much larger than the overall fill of the text. Removing the stroke from the text elements ensured that they rendered even when very small.
I have probably a relativly simple question.
Here is my simple diagram enter link description here
And I would love to implement zooming in and out functionality, using the behaviour descibed here
enter link description here. But I cant make it work, maybe You would spot the problem.
Thanks for Your time.
The whole zooming in functionality seems to be handled with this assignment and function:
svg = d3.select("body").append("svg").attr("width", width).attr("height", height)
.call(d3.behavior.zoom().x(x).y(y).scaleExtent([1, 8]).on("zoom", zoom));
function zoom() {
svg.clearRect(0, 0, width, height);
}
The code you have tried for zooming is used for canvas. You can use transform attribute for zooming in svg.
You can put the whole graph in a (group) element and apply transform attribute to element.
svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
.call(d3.behavior.zoom().x(x).y(y).scaleExtent([1, 8]).on("zoom", zoom))
.append("g");
function zoom() {
svg.attr("transform","translate("+ d3.event.translate+") scale("+d3.event.scale+")");
}
Here is the fiddle