D3: use transition .attr('x') to horizontally slide <text> elements - d3.js

I want to
display some text one after another
after each of the text is displayed, I would like to slide them horizontally from the left side of the page to the right side of the page: so I would like to change the texts' x= 10 to x= 500.
I am able to make step 1) but not 2).
Here is my script:
var textall =["one", "two", "three"]
var number = -1
d3.select('svg')
.transition()
.duration(0)
.delay(0)
.on("start", function addtext() {
number+=1;
if (number < textall.length){
d3.select('svg').append('g').append("text")
.attr("class", "one")
.attr('x', 10)
.attr('y', function(d,i) {return (number+1)*30; })
.text(textall[number])
.attr("font-family", "sans-serif")
.attr("font-size", "20px")
.attr("fill", "black")
.transition()
.duration(0)
.delay(2000)
.on("start", addtext);
}
<!-- }; -->
});
d3.selectAll(".one")
.transition()
.duration(1000)
.delay(6000)
.attr('x', 90)
On jsfiddle

Updated Fiddle I have updated your fiddle, not with the end result but with enough that you can fill in the exact values. Basically I added and .on("end") function that moves the text after they have been added. Further tweaking can happen to improve the aesthetic.

Related

Transition trigger to not work till another transition is complete

I have 2 circles that move right when clicked. Transition takes 10 sec.
What I want
Requirement 1: If circle 1 is in transition, clicking circle 2 should not trigger transition
Requirement 2: If circle 1 is in transition, clicking circle 2 should stop circle 1 at its current position and start transition for circle 2
here is my code
let svg = d3.select("body").append("svg").attr("width", 1800).attr("height", 1800)
svg.selectAll("circles")
.data([100,200])
.enter()
.append("circle")
.attr("class","zubi")
.attr("cx",50)
.attr("cy",d=>d)
.attr("r",30)
.on("click",function(){
d3.select(this)
.transition()
.duration(10000)
.attr("transform","translate(800)")
})
One way to block any transitions is to just remove the handler when the transition is started, and put it back when it's done:
let svg = d3.select("body").append("svg").attr("width", 600).attr("height", 300);
function myClickFunction(d, i) {
circles.on("click", null);
d3.select(this)
.transition()
.duration(5000)
.attr("transform", "translate(300)")
.on("end", () => circles.on("click", myClickFunction));
}
let circles = svg.selectAll("circles")
.data([100, 200])
.enter()
.append("circle")
.attr("class", "zubi")
.attr("cx", 50)
.attr("cy", d => d)
.attr("r", 30)
.on("click", myClickFunction)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
Other possibilities are to add/remove classes, or to get the active transition using d3.active.
You can use d3.selection.interrupt to cancel any currently running transitions on a selection. I gave the transition a name so you can more fine-grained control. You can have multiple transitions run side by side if you want - if no name is given all transitions are interrupted.
let svg = d3.select("body").append("svg").attr("width", 600).attr("height", 300)
let circles = svg.selectAll("circles")
.data([100, 200])
.enter()
.append("circle")
.attr("class", "zubi")
.attr("cx", 50)
.attr("cy", d => d)
.attr("r", 30)
.on("click", function(d, i) {
// Cancel any running transitions
circles.interrupt("circle-transform");
d3.select(this)
.transition("circle-transform")
.duration(5000)
.attr("transform", "translate(300)")
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

Unable to remove d3 transition despite calling interrupt on selection

I have a transition running on some circles in d3. The transition brings the circles into view, and updates a text display with their time stamp:
var component = this;
select(this.node).select("#circles").selectAll(".pin")
.data(this.props.data)
.enter()
.append("circle", ".pin")
.attr("r", 5/component.state.zoomScale)
.style("fill", "#ff0000")
.style("opacity", "0.0")
.transition()
.on("start", function(d, i) {
if (i % component.props.multiplier == 0) {
select("#timer").text(d.time);
}
})
.style("opacity", "1.0")
.delay(function(d, i) {return d.delay/component.props.multiplier;});
If my component receives new data, I want to stop any running transitions on the circles, clear the text and remove the circles:
var circles = select(this.node).select("#circles").selectAll(".pin");
if (!circles.empty()) {
circles.interrupt();
select("#timer").text(""); //This is my time display that I want to clear
circles.remove();
While the circles are removed fine, the text reappears after being removed suggesting the transition was never actually stopped. How do I correctly stop the transition running on my circles? I am using d3.js v4 within ReactJS.
you have to set the class separate and not with the append call.
Your selection to interrupt does select nothing, there is no circle with that class.
var component = this;
select(this.node).select("#circles").selectAll(".pin")
.data(this.props.data)
.enter()
.append("circle")
.attr("class", "pin")
.attr("r", 5/component.state.zoomScale)
.style("fill", "#ff0000")
.style("opacity", "0.0")
.transition()
.on("start", function(d, i) {
if (i % component.props.multiplier == 0) {
select("#timer").text(d.time);
}
})
.style("opacity", "1.0")
.delay(function(d, i) {return d.delay/component.props.multiplier;});

d3.js: Labels are not respecting their place (simple bar chart)

I'm working through Scott Murray's exercise from Chapter 9 of his book - specifically, using update selection to introduce new data points. He demonstrates it with a simple bar chart, and then leaves updating the labels as an exercise. As a disclaimer, I'm almost a complete d3 noob.
So I've managed to get the labels transitioning onto the chart as each new data point arrives. But, for reasons mysterious, they don't center themselves in each bar. Weirdly, the old, original data points' bar labels do - only the new ones don't work. Here's what it looks like (new data is sliding in from the right):
And here's my code:
var labels = svg.selectAll("text")
.data(dataset);
labels.enter()
.append("text")
.text(function(d) {
return d;
})
.attr("x", w)
.attr("y", function(d) {
return h - yScale(d) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white");
labels.transition()
.duration(500)
.text(function(d) {
return d;
})
.attr("x", function(d, i) {
return xScale(i) + xScale.rangeBand() / 2 ;
})
.attr("y", function(d) {
return h - yScale(d) + 14;
});
My instinct is that it's something going wrong in the third chunk - after labels.transition(), and specifically, something going on with my xScale.rangeBand() / 2 business. But any insight would be very welcome!
The labels are correctly placed. You should set the attribute text-anchor to middle to get the labels horizontally centered.
labels.enter()
.append("text")
.attr("text-anchor", "middle")
.text(function(d) {
return d;
})
.attr("x", w)
.attr("y", function(d) {
return h - yScale(d) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white");
Happy learning!

d3.js Add labels in Chord diagram

I am new to this kind of chord visualization. I am working on a sample http://bl.ocks.org/mbostock/4062006:
I would like to add "black", "blonde","brown","red" as labels in the respective chord. How is this possible.
I tried something like this but is not working:
var textLabel = d3.scale.ordinal().range(['Black','Blonde','Brown','Red']);
svg.append("g").selectAll("path")
.data(chord.groups)
.enter()
.append("path")
.style("fill", function(d) { return fill(d.index); })
.style("stroke", function(d) { return fill(d.index); })
.attr("d", d3.svg.arc().innerRadius(innerRadius).outerRadius(outerRadius))
svg.append("g").append("svg:text")
.attr("x", 6)
.attr("dy", 15)
.append("svg:textPath")
.text(function(d,i) { return textLabel(i+1); })
.on("mouseover", fade(.1))
.on("mouseout", fade(1));
You have forgotten to use the enter selection and also do not link the svg:textPath to an actual path in the SGV. To do this the path you want the text to follow needs to have been given an id attribute and you need to reference this in the textPath with an href attribute.
So you want to add the id attribute to the path you want the text to follow
.attr("id", function(d, i){return "group-" + i;})
You then want to do something like this to add textPath elements
svg.append("g").selectAll("text")
.data(chord.groups)
.enter()
.append("sgv:text")
.attr("x", 6)
.attr("dy", 15)
.append("svg:textPath")
.attr("xlink:href", function(d, i){return "#group-" + i;})
.text(function(d,i) {return textLabel(i+1);});
Finally with the colour black you will probably want to invert the text so you don't end up with black on black text.
.filter(function(d, i){return i === 0 ? true : false;})
.attr("style", "fill:white;");

circle radius not updating on transition in d3

I am having trouble with a transtion in d3. This jsfiddle illustrates the problem: http://jsfiddle.net/3bzsE/
When the page loads, dataset01 is used to create a circle for each person in the array. d.name is used as the key.
The blue rectangles below the chart are buttons that update the data on click.
Here is the update funciton:
function updateVis(data) {
var points = svg.selectAll('.nameinfo')
.data(data, function(d) { return d.name;});
var pointsEnter = points
.enter()
.append('g')
.attr('class', 'nameinfo');
pointsEnter
.append('circle')
.attr('cx', function(d) { return 10 + d.position * 100; })
.attr('cy', width/2)
.attr('r', 0)
.style('fill', function(d) { return z(d.position); })
.style('fill-opacity', 0.5)
.style('stroke', '#232323')
.append("title")
.text(function(d) { return d.name; });
pointsEnter
.append('text')
.attr('x', function(d) { return 10 + d.position * 100; })
.attr('y', width/2)
.attr("dy", "0.35em")
.style("text-anchor", "middle")
.style("font-size", "11px")
.text(function(d, i) { return d.name; });
pointsUpdate = points
.selectAll('circle')
.transition().duration(500)
.attr('r', function(d){ return d.value/2;});
var pointsExit = points.exit().transition().duration(500).remove();
pointsExit
.selectAll('circle')
.attr('r', 0);
}
The enter and exits are behaving as expected, but circle radius is not changing for names that are present in the entering and exiting datasets.
An example using the values for Jim:
clicking on button three with button one active:
Joe, Janet and Julie exit
Jane and John enter
But, the radius of Jim does not change (it should shrink because d.value changes from 130 to 50)
Clicking on two with three active causes Jim to exit. Clicking on three from two causes Jim to enter with the proper radius from dataset03.
The same behavior can be see with the other names. In all cases enters and exits work, but if two datasets have an element with the same name, radius is not updated on transition
You might have to specifically select the circles for your transition rather than trying to do it on the outer group element. So instead of this:
pointsUpdate = points
.selectAll('circle')
.transition().duration(500)
.attr('r', function(d){ return d.value/2;});
Do this:
svg.selectAll('.nameinfo circle')
.data(data, function(d) { return d.name;})
.transition().duration(500)
.attr('r', function(d){ return d.value/2;});
UPDATE: Below is another way that fits better with the overall D3 philosophy of reusing the existing data/selection:
points
.select('circle').transition().duration(500)
.attr('r', function(d){ return d.value/2;});

Resources