How to avoid flicker when redrawing svg? - javascript

I have a couple of End points for eg: rule1, rule2. I need to switch between these two end points using button click event and display the data using d3js charts. When I'm switching between these two, I noticed a flicker which is uncomfortable to the eye. For every button click event I'm emptying the content in the respective divs using $("id").html("") . After the above command, I'm calling the functions which redraws the entire chart. I don't understand why there is a flicker on the screen.
$("button").on("click",function(){
$("#stackhorizon").html(''); //chart-1
$("#horizontalbar").html(''); //chart-2
$("#atom").html(''); //chart-3
var id = $(this).attr('id'); // id holds the url
url = id;
drawDashBoard(url);
});
drawDashBoard re-draws all the charts. Sometimes there's flicker and sometimes there is smooth transition.

The reason for that flickering is probably either the delay until new data has been loaded or your chart creation takes a considerable time.
In both cases it would be beneficial to leave the old chart visible as long as possible before replacing it with the new one.
So option one would be: Draw the chart in a separate <div> element that is not visible to the user. Once you're finished drawing replace the old chart with the new one.
The other option would be to wait with deleting at least until the data is available. So in your drawDashBoard() you will at some point have something like d3.json(). Put the removal of the old chart in the callback of that data-retrieving call.

Related

Question on Highcharts SVG element create / destroy using Highcharts.SVGRenderer

I've read various examples on how to use the Highcharts.SVGRenderer to create custom SVG text etc.
However, I'm wondering what is the correct practices to create and clean them up?
Currently I make them at the events.redraw hook (because I want to update the SVG text based on the dynamic data), however it looks like the redraw hook is being called at various occasions multiple times (it looks like it'd redraw on add series and data being added) and then the SVG does not get cleaned up... so I ended up having multiple copies of the same SVG elements in the chart. Any subsequence redraw would obviously keep adding more SVGElements into the chart.
Questions:
Do i need to keep track of all the generated SVGElements myself and call destroy() manually every time I redraw them?
Is there any internal trackers in Highcharts that tracks the created SVG using SVGRenderer? (so that I could maybe use the internal ref to call destroy myself)
If I am not supposed to draw them in redraw event, what is the recommended hook to draw my SVG elements where I need dynamic update on those generated SVG elements.
Do I absolutely need to keep track of my own references on the SVG elements? Seems a bit too much of the overhead/hassle to do that.
Yes, you need to keep track of all the generated SVG elements, which you want to update, but better than destroying them is to update their attributes.
No, there is no such trackers.
You can draw the elements in the load event and redraw them when you need to (for example when you change your data).
No, you have to keep track only those elements that you need update. Maybe some of your problems will be resolved by by annotations? https://www.highcharts.com/docs/advanced-chart-features/annotations-module
events: {
render: function() {
var chart = this,
series = chart.series[0],
x = series.points[2].plotX,
y = series.points[2].plotY - 50;
if (!chart.customElement) {
chart.customElement = chart.renderer.text().add();
}
chart.customElement.attr({
text: 'some text ' + Math.random(),
x,
y
});
}
}
Live demo: http://jsfiddle.net/BlackLabel/t250crx1/
API Reference: https://api.highcharts.com/class-reference/Highcharts.SVGElement#attr

hide one of the axes of a parallel-coordinates plot

i'm representing some data with the parallel coordinates library (based on d3.js) ( https://github.com/syntagmatic/parallel-coordinates#parallel-coordinates )
main functional part of the code is the following:
var parcoords = d3.parcoords()("#example") //#example is the div for the drowing
.data(eingabe) // eingabe is the var, which contains the data
.render()
.reorderable()
.shadows()
.brushMode("1D-axes")
so far it works fine :)
but now i want to hide one specific axis of the parallel coordinates; i only don't want to show it.
I want that the axis is completeley removed, so that it takes no place and the lines don't be affected of the values of this dimension, but i don´t want to do that by manipulating the data. i only want to manipulate the plot (thats the reason i called it hide).
i've searched on the api description but didn't find something. I searched in the internet but didn't find anything, too. But i think i remember that i've seen a peace of code, where the first axis was hidden, but i can't find it again.
Can anybody tell me, how i can hide an axis or where i can find the solution?
thank you, greetings
Jones
You're missing a .hideAxis(array) method call, where the array is a list of keys of the exact column names in your dataset that you want to hide. This method does exactly what you said you wanted:
"...hide one specific axis of the parallel coordinates; i only don't want to show it."
"I want that the axis is completley [sic] removed, so that it takes no place"
"the lines dont [sic] be affected of the values of this dimension, but i don´t want to do that by manupulating [sic] the data. i only want to manipulate the plot."
To implement this method, your code would need to be
var parcoords = d3.parcoords()("#example") //#example is the div for the drowing
.data(eingabe) // eingabe is the var, which contains the data
.hideAxis(["col1", "col2"])
.render()
.reorderable()
.shadows()
.brushMode("1D-axes")
Note: .hideAxis can take in a blank array if you don't want to hide any axis, i.e. .hideAxis([])
You can implement this method in some sort of update/redraw function if you want to allow the user to specify which axis(es) to remove; else, removing it completely via the DOM is impossible. This, performing the axis(es) hide via code, is your only option to implement what you want.
To see this method in action, take a look at the API documentation, specifically, their "example1" graph and code at http://syntagmatic.github.io/parallel-coordinates/index.html#example1
Assuming you just want to hide the axes (i.e. make it invisible) and not remove it completely
d3.selectAll("#example0 > svg > g > g.dimension:nth-child(3)").attr("opacity", "0");
Assuming example0 is the id of the wrapper around your svg element. You could also do the same thing directly on your svg element if you have it.
The above would still leave you able to interact with the filter of the hidden axis. To make it non-interactable, use .attr("display", "none");
Don't have a fiddle, but you should be able to copy paste the above line and run it from the console at http://syntagmatic.github.io/parallel-coordinates/ and see the effect on the chart in the Bundling section.
Here is the effect - before and after respectively

issues with Raphaël and removing objects within svg

if you open the code pen there is a fire button. it will launch a bunch of ellipses and then when it hits it will cause a burst. if you look the ellipses ,which there are two sets of, they are still there. I have tried using the below
d3.selectAll("ellipse").remove()
$("ellipse").remove()
$("ellipse").each(function(){this.remove()})
http://codepen.io/daniel667/pen/QwMWrm
the code pen above will help show what im talking about the second fire button to the far right is what ive been trying to use to kill the ellipses so I don't wait for the animation the functions at the very bottom.
I would create a Raphael set, or an array and store the elemets in that, so you can reference them later to remove. If they will be used repeatedly, it may be worth not removing them, but just hiding them rather than recreating each time.
var mySet;
...
mySet = paper.set();
mySet.push( circi );
....
function throwss() {
mySet.forEach( function( el ) { el.remove(); });
}
Example: codepen
For speed, you may also want to look into Velocity.js, also be aware for animation filters can be quite resource heavy.

images created dynamically do not appear on screen! -> Javascript

I'm trying to do something simple to practice my Javascript (which I learned some recently) and I'm trying to do a game on it (pacman to be precise).
I am trying to build that game board on the browser by creating images dynamically. I've done an array like this:
var images= new Array(25);
for(i=0;i<25;i++)
images[i]= new Array(25);
And, for the game board I used a matrix done with 0 and 1's with 25x25 size (not going to post it here cause is too big and would make my text hard to read) called board.
For the images that I am using right now I have something like this:
var image_empty = new Image();
image_empty.src="Images/empty.jpg";
var image_wall = new Image();
image_wall.src="Images/wall.jpg";
For the initialization function I have something like this:
function drawField()
{
for(i=0;i<board.length;i++)
{
for(j=0;j<board[i].length;j++)
{
if(board[i][j] == 0)
draw(i,j,image_empty);
else if(board[i][j] == 1)
draw(i,j,image_wall);
}
}
}
And for drawing the images themselves I am using this:
function draw(x,y,img)
{
images[x][y] = new Image(22,22);
images[x][y].src = img.src;
images[x][y].style.position = 'absolute';
images[x][y].style.left = 40+x*22;
images[x][y].style.top = 40+y*22;
}
Every time I run this code nothing appears on the screen. I've tried several times use a load of things but nothing happens. I am saving the pictures (at least I think I am) and still nothing.
Can someone give me some pointers of what could be wrong?
PS: Some people pointed me out using the appendChild method would solve the problem, still, since pacman will be moving around I can't use it to store my images (and I was planning to use the draw function to draw anything).
And btw nor Web Developer plugin or firebug point out errors (the code is correct from their perspective).
Creating an Image in the method you describe doesn't actually display the image. Even putting attributes and styling to make it appear a certain way doesn't add it to the DOM. The advice about append child is correct. For example, if you had:
<div id="main"></div>
and you called
document.getElementById("main").appendChild(images[x][y]);
this would insert the image inside the div. You could do this repeatedly to generate the equivalent of...
<div id="main">
<img src... />
<img src... />
...and so on
</div>
Then, your CSS styling and positioning would work.
There's nothing wrong with your script, but Firebug does display a rendered version of the DOM. As you run the script, you will actually see the HTML tab of Firebug changing with the images you've added to the page.
Also, keep in mind that the DOM must complete loading before you are able to run this. You can accomplish this by doing a simple:
<body onload="drawImages()">
UPDATE: Once you've actually added the 25x25 images, the array still references the elements - they're just now part of the DOM. So, you can change their source via:
images[x][y].src = "newImage.jpg";
If you, for some reason, wanted to remove an image from the board, leaving a gap, you can remove it from the DOM
document.getElementById("main").removeChild(images[x][y]);
or just hide it via CSS.

Updating target href problem

I'm working on a little image gallery, where you'd roll over a small thumbnail, the larger image would display over it. Clicking on the image would load a full size version in an overlay.
http://shopcoobie.server303.com/shop/
The issue is with the larger image, the rollovers are working fine, but the script I have updates the href which points to the full size image only seems to work the first time I click the image.
$$('.thumbs img').each(function(s) {
$(s).observe('mouseover', function(e) {
var el = e.target;
var thumb = $(el).src;
var large = $(el).alt;
$(el).up(3).down(1).href = large;
$(el).up(3).down(2).src = thumb;
console.log((el).up(3).down(1).href);
console.log(thumb);
});
});
The console outputs the proper image reference, so it seems this should work (and partially does), Im just not sure why it only works once.
Thanks
Rich
I think the lightview plugin builds up its list of images first, then you change the src dynamically, and it doesn't update. So try adding a call to Lightview.updateViews(); at the end of the mouseover handler above.
From Lightview:
Lightview.updateViews(): Force a reset of all Lightview
elements on the page. Most of the time
you won't need to do this since
Lightview will pick up on newly
inserted elements automatically. After
updating existing elements it might be
required to call this function.
So yours is a case where it doesn't automatically pick up the change.

Categories