I see a strange flickering effect after a transition. It is unusual mainly because I do not set the opacity in any way (I want the color to remain the same). Any ideas why this happens?
To have an idea about how the code looks like, here is an example.
var theBars = this.vis.selectAll(".bar" + source.id).data(this.columns);
theBars.enter().insert("svg:rect")
//some attributes
.style("fill", sourceColor)
//some other attributes
theBars.transition()
//.duration(.01)
.attr("y", function(d) {
return this.settings.base - this.getStackedBarHeight(d, source.id);
}.bind(this))
.attr("height", function(d) {
return this.getBarHeight(d.counters[source.id]);
}.bind(this));
As it can be seen only one line sets the color.
I initially tought I made some mistakes at binding, but after checking some posts here and on Google Groups, I discovered that this flickering usually appears when you have transitions that also change the opacity of the object. Unfortunately I don't change any opacity, I just make a transition. This effect appears in all major browsers when executing that transition (theBars.transition).
I try to select a bar from a stacked bar and modify its height.
Best regards!
To fix this I added 2 things:
in the init phase - I added all the bars but with all counters set
on 0;
in the draw phase - I added this code:
var theBars = this.vis.selectAll("#bar_"+index+"_"+currentIndex);
this.settings.sources.each(function(pair) {
theBars
.style("fill", source.color)
.attr("height", this.getBarHeight(source.id)
.attr("y", this.settings.size.baseLine - this.getStackedBarHeight(counters, source.id))
}, this);
The flickering caused by the transitions is gone since we have no transition here. There are still situations in which we need to do a transition, for example when we have several bars with the same word, but there I resolved the flickering by doing it really quick (a .duration(.1) or even less).
Related
Note: I asked this question about interrupting transitions during a scroll, but am trying a different technique now that is resulting in a similar issue that doesn't get resolved with the accepted (and working) answer.
This time, rather than initializing all the graphs with 0 opacity and having a separate function to change the opacity that gets called on each step, I'd like to use selection.remove() in each drawing function. I want to do this so that out-of-view graphs don't get in the way of any mouseover interactions that I might want on the current graph.
For example, I have functions that clear the existing graphs and then draw the current one with some transition:
var makeCircle0 = function() {
d3.selectAll(".nancygraphs").interrupt().remove()
g.append("circle")
.attr("cx", 50)
.attr("cy", 100)
.attr("r", 20)
.attr("fill", "red")
.attr("id", "red")
.attr("opacity", 0)
.transition()
.duration(1000)
.attr("opacity", 1)
.attr("class", "nancygraphs")
}
These functions are put in a list
var activateFunctions = [];
activateFunctions[0] = makeCircle0;
activateFunctions[1] = makeCircle1;
activateFunctions[2] = makeCircle2;
activateFunctions[3] = makeCircle3;
And depending on the step, the function gets called to draw the correct graph
function handleStepEnter(response) {
console.log(response)
step.classed('is-active', function(d, i) {
return i === response.index;
})
figure.select('p').text(response.index);
figure.call(activateFunctions[response.index]) // HERE!
}
Here is a jsfiddle to illustrate. Basically, if you scroll back-and-forth quickly then old graphs don't get cleared and you'll notice several graphics in view simultaneously. Why isn't d3.selectAll(".nancygraphs").interrupt().remove() doing the job?
Three observations regarding your approach:
First, according to the d3 manual on
transitions:
remove: remove the selected elements when the transition ends.
The remove will not interrupt already running transitions - it only removes when all transitions have stopped. More specifically it seems to act when __transition__.count of an element reaches 0. You could consider using a non-d3 remove implementation here, e. g. jQuery.
Second, from the same manual:
Interrupting a transition on an element has no effect on any transitions on any descendant elements. (...) you must therefore interrupt the descendants: selection.selectAll("*")
You should call interrupt on both by doing d3.selectAll(".nancygraphs").interrupt().selectAll("*").interrupt().
Third, it is never a good idea to directly couple mouse or scroll input to your logic (when you directly couple input events to e. g. attaching a transition, you might be doing so many thousands of times): did you use a debounce function? The lodash implementation is highly recommended.
After trying these modifications I would assume your current problem is solved. If it is not, a further way of debugging would be to log / overwrite the __transition__.count attribute of your elements.
I am moving a rectangle from point a to point i in a picture, i want to mark a stop delay of 5s for each point (there are 8 points). the transitions work fine in the code below (the delay works only for point b).The problem is that i can't add more delays for my other transitions.
Is there any way to do it ?
Thank you all in advance.
function TRANSITION(access,dur=10000,Delay=5000,b=390.5,c=523,d=632.5,e=810.8,f=942.5,g=1063,h=1196,i=1334.5)
{
access.transition().duration(dur).attr('x',b)
.transition().delay(Delay).duration(dur).attr('x',c)
.transition().duration(dur).attr('x',d)
.transition().duration(dur).attr('x',e)
.transition().duration(dur).attr('x',f)
.transition().duration(dur).attr('x',g)
.transition().duration(dur).attr('x',h)
.transition().duration(dur).attr('x',i)
}
You could add a transition that changes no attributes but still has a duration:
.transition()
.duration(dur)
.attr('x',d)
.transition() // don't transition anything
.duration(5000) // but take five seconds doing it
.transition()
.duration(dur)
.attr('x',e)
I've only tested this in version 4, so it is possible that this might not work in version 3. Alternatively, you could add the .attr line in the delaying transition if you were to leave some attribute the same.
Here is the structure of my HTML
svg
g id=invisibleG
g
circle
g
circle
g
circle
So I want something like this on hover of any particular circle
svg
g id=invisibleG
g
circle --> radius is increased on hover.....decreased on hoverout
text
g
circle
g
circle
here is the code
.on("mouseover",function(){
var r=d3.select(this).attr("r");
d3.select(this).style('fill','tan')
.style('fill-opacity', '1')
.transition()
.duration(1000)
.attr("r",50);
d3.select(this).attr("stroke","blue")
.attr("stroke-width",4);
})
.on("mouseout",function(){
var r=d3.select(this).attr("prevRadius");
d3.select(this).attr("r",r)
.attr("stroke-width",0)
.style('fill-opacity','0');
});
Now the problem is that when I hover over a circle and immediately hover out of it the transition which is started in mouseover doesn't stop immediately.It completes its transition and the size of radius is increased despite being the fact that mouseout event should be called.And whatever the transition was going should stop.
Please let me know the problem and its solution .
You just need to use transitions in both cases. From the documentation:
If a newer transition runs on a given element, it implicitly cancels any older transitions, including any that were scheduled but not yet run. This allows new transitions, such as those in response to a new user event, to supersede older transitions even if those older transitions are staged or have staggered delays.
So your code would need to be something like this.
.on("mouseover", function() {
this.prevRadius = d3.select(this).attr("r");
d3.select(this)
.style('fill','tan')
.style('fill-opacity', '1')
.transition()
.duration(1000)
.attr("r",50)
d3.select(this)
.attr("stroke","blue")
.attr("stroke-width",4);
}).on("mouseout", function() {
d3.select(this)
.transition()
.attr("r", this.prevRadius)
.attr("stroke-width",0)
.style('fill-opacity','0');
});
Demo here.
If your d3 version is mature enough (3.3+), they seemed to have added selection.interrupt
So you could perhaps try:
.on("mouseout",function(){
d3.select(this).interrupt();
// if interrupt isn't supported in your version use below
//d3.select(this).transition().duration(0);
})
Otherwise, newer transitions on the same selection will cancel the old active transitions. So you could run a new transition on mouseout where you transition to the reset values again. If you want to just freeze the transitions, just run a dummy transition to cancel the old one.
FURTHER INSIGHTS
If your goal is to stop the only transition of the r (radius) dead in its tracks see this fiddle which uses interrupt.
If your goal is to reset the r value or the non-transition changes you've made during the mouseover, see this fiddle
I'm attempting to integrate AngularJS with d3 for dragging and resizing. I've managed to create a rect object that is draggable in an SVG element, and resizable using resize handles. The resize handles work as they should, but resizing is choppy when I try to resize in the north or east direction. I created the following Plunk as a demo of the issue: http://plnkr.co/tG19vpyyw0OHMetLOu2U. (I've simplified it to show the issue I've run into, so there's only one resize handle.)
Dragging works as it should, and resizing in the west and south directions works as well (not shown in the demo).
Figured I'd ask the community and see if anyone had run into this before. Thank you all.
The problem is that you're modifying the rect element itself and the enclosing g element. There's a very short delay between setting the size of the rect and the position of the g simply because this has to be done with two separate commands. During this delay, the cursor position relative the the drag rectangle changes, firing a new drag event with values that correspond to the inconsistent intermediate state. This is fixed immediately afterwards (as soon as the attributes of both elements have been adjusted) and a new drag event is fired that fixes the inconsistency, but it is noticeable as a flicker.
The easiest way to fix this is to change both size and position for the rect and nothing for the g element. This means adjusting the position of the drag rectangle as well and makes the code less nice, but avoids the timing/inconsistency problem.
So myrect becomes
var myRect = d3.select(document.createElementNS("http://www.w3.org/2000/svg", "rect"))
.attr("data-ng-width", "{{square.w}}")
.attr("data-ng-height", "{{square.h}}")
.attr("stroke", "yellow")
.attr("stroke-width", 3)
.attr("fill-opacity", 0)
.attr("data-ng-x", "{{square.x}}")
.attr("data-ng-y", "{{square.y}}");
and resizer
var resizer = myGroup.append("rect")
.attr("width", 5)
.attr("height", 5)
.attr("stroke", "blue")
.attr("stroke-width", 1)
.attr("fill-opacity", 0)
.attr("cursor", "nw-resize")
.attr("x", "{{square.x-2.5}}")
.attr("y", "{{square.y-2.5}}")
.call(nresize);
I've updated your code with this solution here.
I'm fairly sure if I can find an example showing what I'm trying to do, I can reverse engineer/reimplement it. Has anyone seen an example showing a smooth/animated transition between a linear and log scale in D3JS?
I have both scales working independently, but I have to reload the page to change the scale.
My Google skills have failed me!
Thanks so much.
Here's a proof of concept jsfiddle. You simply reselect the data points and redraw them with the new scale. For the axis labels, the transition is even simpler -- you just need to call the axis function again. Relevant excerpt below.
// change to log scale...
yScale = d3.scale.log().domain([1, 100]).range([dim-padding,padding]);
svg.selectAll("circle").data(data)
.transition().delay(1000).duration(1000)
.attr("cx", function(d) { return xScale(d); })
.attr("cy", function(d) { return yScale(d); });
svg.selectAll(".y")
.transition().delay(1000).duration(1000)
.call(yAxis.scale(yScale));
You might need to play around with how the labels are generated to make it look "nice", but in principle d3 will take care of the entire transition.