How to cancel the mouseover transition in d3.js - javascript

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

Related

Removing elements by class on scroll

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.

d3 SVG Transitions Not Always Ending At Required Value

I have a d3 visualisation on a web page that shows a collection of plot points on a chart.
It's all very simple really with just two axis and sometimes maybe 30 points to plot. As I render these plots I perform a transition on the r attributes of the SVG circles. The code that does this is:
g.append("svg:circle")
.attr("class", "plot-circle")
.attr("cx", xCo)
.attr("cy", yCo)
.attr("r", 0)
.transition()
.delay(function() { return delayInput * 100; })
.duration(plotExplosionDuration)
.ease(Math.sqrt)
.attr("r", 6);
All that occurs is that the circles are initially set to r=0 which means they aren't rendered at all. Then I start a transition on the appended circle to take this radius up to 6.
The problem is that it appears on some machines these transitions don't stop at r=6 and some plots end up being much bigger than the value set after the transition.
I simply cannot duplicate this on my main development machine (PC), my iPad nor my MacBook Pro which leads me to think it might be performance or machine load causing this?
has anyone got any ideas on how to ensure the transition stops at the defined final r value?

d3js SVG:title tooltip doesn't show up

d3js SVG:title tooltip doesn't show up
I have a graph which contains a lot of circles, now I would like to insert to each circle a tooltip but it doesn't work neither as title in circle nor as tooltip box. I cannot find my mistake:
var circleSmall = canvas.append("circle")
.attr("cx", 869)
.attr("cy", 693)
.attr("r", 10)
.attr("fill", "green")
.append("svg:title").text("Your tooltip info")
.on("mouseover", function(){return tooltip.style("visibility", "visible");})
http://jsfiddle.net/WLYUY/57/
The most important obstacle here was your rects. They were appended AFTER the circles and were preventing mouse events from reaching the circles. So, first thing to do is:
/* do this or append circles AFTER appending recs */
rect {
pointer-events: none;
}
I have added the mouseover and mouseout events necessary to show/hide the tooltip.
Complete FIDDLE here.
NOTE: For demonstration, I have painted the one circle receiving the events in large orange with an orange-red border (you can't miss it). This is just an example...normally you would apply the event listeners to all circles. And this brings me to a sanity check: you are currently appending shapes "manually" but I assume you will eventually do it based on data binding, one of the main points of D3.

AngularJS + d3js: Issues with resizing objects

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.

Transition flickering

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).

Categories

Resources