I want to turn NVD3 charts into PDF documents. Those charts are normally displayed in browser (I can't make a separate instance of each chart for print and for display), I got it all working using PhantomJS, but I have a problem that I can't seem to find a good solution to.
All NVD3 models use transitions, but only some of those transitions are affected by transitionDuration option. Because of those transitions, I now have to use a timeout before "capturing" the screen in PhantomJS to make a PDF, otherwise resulting document pictures those charts mid-transition. Obviously I'd rather not have to wait.
PhantomJS uses print media type to render PDFs, so it's very easy to disable any CSS3 animations (using media query), but I can't find any way of turning D3 transitions off (in other words - forcing a default transition duration of 0). I can detect print media type in JavaScript, but can't find a good way of globally turning off animations in D3/NVD3... That's all I've got and it doesn't really do much:
var chart = nv.models.multiBarChart()
.tooltipContent(tooltip)
.stacked(true)
.showControls(false);
var duration = 1000; // default duration
if(window.matchMedia) {
if(window.matchMedia('print').matches) {
duration = 1; // duration for print
}
}
chart.transitionDuration(duration);
As of NVD3 1.7.1 you can use the duration option:
chart.duration(0);
I cannot think of another solution than modify the nvd3 source. If you replace all the ocurrences of
transitionDuration = 250
for
transitionDuration = 0
in nv.d3.js it should work.
Related
The company I'm working for is selling micro computers that can manage and monitor diffrent IO devices.
They are using ajax for the web IO stuff and I created a new graphic for a Voltmeter that contains 41 states from 0 Volt to 20 with 0,5 stepping.
My first question would be, if that is being called a sprite because there are so many images?
The code I wrote to load the images was already much shorter than the company's because I made a function to generate the img links via a counter.
var i = 1;
function counter()
{
var img = "http://"adress"/"+i.toString()+".png";
if (i == 40)
{
i=0;
}
i++;
document.getElementById('picture').src = img;
}
now the next step I was asked was doing that with a svg.
I could do the same thing with a svg of course, but I´ve read about being able to animate svg.
The first big question is:
Should I make a svg file that contains all 41 images as code or should I just do one image and animate the needle by creating an own pivot for it?
Note that the animation states would be hand in hand with a javascript code that "GET"s hex values via xmlHTTP which define the states of the device.
So i wanna turn the needle to 3 volts on the svg if I rotate the knob at the device.
I don't ask for a full solution but some hints if this would even be possible and what i need to read about.
Here is the img I am talking about as an example
Voltmeter
https://www.deviantart.com/blue-lovag/art/Voltmeter-759876423
An SVG with 41 groups in it might be a big file. If you create an SVG containing just one image, where the needle is a <g> group with an ID defined on it, you can refer to that group from JavaScript and have the needle rotate -- even with a smooth animation.
You might define a CSS class for each state the needle can be in, with the rotation in it:
.pos20 {
transform: rotate(45deg);
}
There are some gotchas with Internet Explorer support for this, so you may have to set an attribute on the group directly:
<g transform="rotate(45deg)">...</g>
Please refer to this article on CSS-Tricks for details on SVG transformations.
PROLOGUE
This is my first time posting to stackoverflow and i'm a noob with dc.js. Apologies in advance for etiquette transgressions (feedback welcome on this too)
PROBLEM
I have defined a barchart and it displays perfectly, but brushOn(true) is not letting me filter the data. In the past, this seemed to work perfectly with a crosshair appearing as soon as i hovered over the bargraph. Now it is not. Any idea why?! or what i can do to fix it? I'm on day 3 of trying to figure out what is happening. The help is MUCH appreciated!
PREREQS:
https://d3js.org/d3.v5.min.js
crossfilter.min.js
https://unpkg.com/dc#3.0.4/dc.js
CODE FOR BARCHART
I have defined a barchart as follows:
filterDim = cross.dimension(function(d){return d3.timeWeek(d.date);});
var filterGroup = filterDim.group().reduceSum(function(d){
if(d.isTrue){return 1;}
else {return 0;} });
height=400;
if(width == 0){
width = $(dom_id).parent().innerWidth();
}
var hitsbarChart = dc.barChart(dom_id);
hitsbarChart
.width(width).height(height)
.dimension(sentDimension)
.group(allGroups[0].data,allGroups[0].name)
.xUnits(d3.timeWeeks);
hitsbarChart
.x(d3.scaleTime())
.valueAccessor(function(d){return d.value;})
.keyAccessor(function(d){return d.key;})
.round(d3.timeWeek.round)
.yAxis().ticks(d3.format('.3s'));
function calc_domain(chart) {
var min = d3.min(chart.group().all(), function(kv) { return kv.key; }),
max = d3.max(chart.group().all(), function(kv) { return kv.key; });
max = d3.timeMonth.offset(max, 1);
chart.x().domain([min, max]);
}
hitsbarChart.on('preRender', calc_domain);
hitsbarChart.on('preRedraw', calc_domain);
hitsbarChart.brushOn(true);
dc.renderAll();
RESEARCH
I found this example which demonstrates something different but outputs a graph with time-series as the x-axis and working brush to select a range of dates.
Also, there this bug with work-around but the work around did not work. I can't imagine that time-series data works more like an ordinal scale than a numerical scale.
It's likely that you have some CSS inadvertently affecting your chart when it was supposed to be control some other part of the page.
This could happen either because you used a generic name which is also used by dc.js or d3.js, or because a style sheet from another library does. All of dc.js's style rules are carefully scoped so that they shouldn't affect anyone else, but many common words are used for class names, so interference the other way is common.
The brushing behavior comes from d3, so I'd try looking at d3's g.brush rect.overlay in the inspector of your developer tools. You should be able to bring it up by right-clicking the background of the chart and selecting Inspect.
If it has something like
pointer-events: none;
or
display: none;
applied to it, find out what applied that (hopefully CSS you control) and try to make the rules more specific.
Of course it's also possible for JavaScript from another library to cause such troubles, but interference from CSS is much more common.
I am a little bit struggling on how to use Vis.js to only show the final result of a stabilized graph, without any stabilization animation or interaction possibilities.
Is there someone that could push me to the correct direction?
If I disable physics completely, the graph shows all nodes one overlaying the other naturally.
Thank you already!
EDIT
Here is the code that I have for the options:
Stabilization stabilization = new Stabilization();
stabilization.setFit(true);
BarnesHut barnesHut = new BarnesHut();
barnesHut.setGravitationalConstant(-23000);
barnesHut.setCentralGravity(0);
barnesHut.setSpringLength(0);
barnesHut.setSpringConstant(0.5f);
barnesHut.setDamping(1);
barnesHut.setAvoidOverlap(1);
Physics physics = new Physics();
physics.setEnabled(true);
physics.setBarnesHut(barnesHut);
physics.setSolver(Physics.Solver.barnesHut);
Smooth smooth = new Smooth();
smooth.setEnabled(false);
smooth.setType(Smooth.Type.continuous);
smooth.setRoundness(0);
Edges edges = new Edges();
edges.setSmooth(smooth);
Interaction interaction = new Interaction();
interaction.setDragNodes(false);
Options options = new Options();
options.setPhysics(physics);
options.setEdges(edges);
options.setInteraction(interaction);
Please be aware that this code is used for a wrapper around vis.js, although the options should reflect the vis.js options.
So it is ok if the answer does not contain any Java code but the actual vis.js hints, I will map it to the wrapper implementation myself.
Seems like the animation is disabled by default, by having the stabilize option set to true.
If you still see an animation, then try to increase the iterations option, which is by default set to 1000.
options.setStabilizationIterations(2000);
I have created a simple demo using the latest version of Vis.js (4.19). Bear in mind that the VisJs-Addon uses an older verion of Vis.js (3.11).
I am building a Highcharts visualisation where there is a line series, and an area series that hugs it (as the error of that series).
My problem is that when I add the arearange, the zoom animation no longer works. The zoom animation works just fine without the arearange series and, strangely enough, the zoom animation works fine if there are eight data points in the area range, but not if there are nine or above.
I have created a codepen to demonstrate this: http://codepen.io/samirelanduk/pen/eWgdZN
The two highcharts files I am pulling in are:
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="http://code.highcharts.com/stock/highcharts-more.js"></script>
Does anybody know why this is? Is it a bug (known or unknown) within Highcharts, or have I done something wrong? Or is there a very good reason for why it does this?
If the number of the points is higher than 99, then the animation for zooming is disabled, unless you force it by setting chart.animation to true. I cannot find whether it is documented, sadly, but it can be seen in the function responsible for zooming.
In chart.prototype.zoom:
// Redraw
if (hasZoomed) {
chart.redraw(
pick(chart.options.chart.animation, event && event.animation, chart.pointCount < 100) // animation
);
}
example: http://codepen.io/anon/pen/bWgBMw
I have a dynamically generated svg image that I am using Ariutta's svg-pan-zoom plugin with. When I double click an svg image, I set pan.x = centerOfScreenX, and pan.y = centerOfScreenY to center the image in the middle of the screen. ie:
$('.svg').dblclick(function(){
zoom.pan({'x':centerOfScreenX, 'y':centerOfScreenY });
});
Currently this causes the image to just suddenly move to the center of the screen. Is there a way I can animate this change in pan position so that the image doubleclicked moves along a path to the center of the screen instead?
Bumbu suggested two solution paths (see answers below), and I have taken a stab at the first. My attempt did not work however, and I do not know why.
// centerOfScreenX and centerOfScreenY are the correct values that pan.x and
// pan.y should have to center the svg in the middle of the screen
// xInterval and yInterval break the distance between the current pan
// position and the desired pan position into 10 steps
var xInterval = (centerOfScreenX - pan.x)/10;
var yInterval = (centerOfScreenY - pan.y)/10;
while( pan.x !== centerOfScreenX && pan.y !== centerOfScreenY ){
if(pan.x !== centerOfScreenX){
pan({'x': pan.x + xInterval })
}
if(pan.y !== centerofScreenY){
pan({'y': pan.y + yInterval })
}
}
When I try to run this code, the window freezes and I can no longer interact with my app, unless i close the window and reload it. My guess is that I am somehow triggering an infinite loop.
Currently there is no solution to do animation in an easy way.
There is a similar question (about animating zoom). The answer from there (adjusted to this one) is:
Currently such functionality is not supported. You could do it in 2 ways:
Use a twin library (or write you own function) and just call pan in small iterations multiple times. This may be slow but it is what many libraries do when implementing animation (eg. jQuery).
Use SVG animateTransform element. It seems to be the right way. But it needs some work to get it done.
You can actually try to implement second solution by listening to zoom
events, canceling them and adding animateTransform manually to the SVG.
When your animation is done, call zoom again but this time don't
cancel it (necessary to update library inner state).
There is an ongoing discussion about next version of library that would be more extensible. This would allow to write plugins. Animation is one of the candidates. But it will take some time (few months) to do this.
If you'll be able to find a temporary solution - share it here or on github and we'll be happy to update the library or integrate it in next version.
Edit
I added a simple example how this kind of animation can be implemented.
You can find it in demo/simple-animation.html
I used a simple interval there. A more advanced version should take into account how much time passed since last interval call and send the right amount for pan. But even like this it works very well.
The library internally uses requestAnimationFrame so you can call panBy even every millisecond and it shouldn't block the browser.