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>
Related
Hi is it possible to make a clone of a selection clone in D3 v5?
I am essentially using selection clone to make and animate circles for simulating bacteria division.
I can call d3.select.clone multiple times but that only makes duplicate clones of the first circle that I call clone on. I want to be able to make clones of the clones as well.
var svg = d3.select('svg')
.style("width", '800px')
.style("height", '600px');
setInterval(function() {
svg.append("circle")
.attr("cx", 30)
.attr("cy", 50)
.attr("r", 15)
.attr("id", "test_circle0")
.style("fill", "red")
d3.select("#test_circle0").clone(true).transition().attr("transform", "translate(25)").duration(1000);
d3.select("#test_circle0").clone(true).transition().attr("transform", "translate(-25)").duration(3000);
}, 1000);
You need to put the creation of the initial circle outside the interval. Then you can clone it inside.
To do things to the clones themselves, you can change select to selectAll
var svg = d3.select('svg')
.style("width", '800px')
.style("height", '600px');
svg.append("circle")
.attr("cx", 300)
.attr("cy", 50)
.attr("r", 15)
.attr("class", "test-circle")
setInterval(function() {
d3.select(".test-circle").clone(true).transition().attr("transform", "translate(25)").duration(1000);
d3.selectAll(".test-circle").clone(true).transition().attr("transform", "translate(-25)").duration(3000);
}, 1000);
.test-circle {
fill: red
}
<div class="chart"></div>
<script src="https://d3js.org/d3.v5.min.js" charset="utf-8"></script>
<svg></svg>
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;});
If I try to store the following enter selection, I get an error when I try to access it. I don't have a problem if I remove the transition. Why? Are there other restrictions on storing selections? Here is an example:
// this works
var enterSel = d3.select("svg")
.selectAll("circle")
.data([100, 200, 300])
.enter()
.append("circle")
.attr("cx", d => d)
.attr("cy", "100")
.attr("fill", "red")
.attr("r", "0")
.transition()
.duration(2000)
.attr("r", "50");
The above appends and transitions three circles to red, as expected, but the enterSel variable cannot be used for further modifications:
// this doesn't work
enterSel.attr("fill", "green");
Uncaught Error: transition not found d3.v4.min.js:2
at zn (d3.v4.min.js:2)
at Cn (d3.v4.min.js:2)
at SVGCircleElement.<anonymous> (d3.v4.min.js:2)
at qn.each (d3.v4.min.js:2)
at qn.tween (d3.v4.min.js:2)
at qn.attrTween (d3.v4.min.js:2)
at qn.attr (d3.v4.min.js:2)
at <anonymous>:1:10
I can get around this by doing the transition separately, as follows, but I really want to understand why this is necessary.
// this works
var enterSel = d3.select("svg")
.selectAll("circle")
.data([100, 200, 300])
.enter()
.append("circle")
.attr("cx", d => d)
.attr("cy", "100")
.attr("fill", "red")
.attr("r", "0");
enterSel.transition()
.duration(2000)
.attr("r", "50");
enterSel.attr("fill", "green");
I'll post an answer for the future. A d3 selection returns a selection with the d3.selection.prototype. A transition on the other hand returns a transition with the d3.transition.prototype.
var enterSel = d3.select("svg")
.selectAll("circle")
.data([100, 200, 300])
.enter()
.append("circle")
.attr("cx", d => d)
.attr("cy", "100")
.attr("fill", "red")
.attr("r", "0")
.transition()
.duration(2000)
.attr("r", "50");
enterSel.attr("fill", "green");
Does not work because enterSel is now a transition and has different properties than a selection.
var enterSel = d3.select("svg")
.selectAll("circle")
.data([100, 200, 300])
.enter()
.append("circle")
.attr("cx", d => d)
.attr("cy", "100")
.attr("fill", "red")
.attr("r", "0");
enterSel.transition()
.duration(2000)
.attr("r", "50");
enterSel.attr("fill", "green");
This one works because enterSel is always a selection, which uses the selection prototype. The transition is sectioned away in the second call, but enterSel is always the selection of all the circles.
Hopefully this helps clear things up!
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.
I need to place circles in the sunburst arcs, close to the outer radius either in the middle or in the sides as in the mock-up in the B Unit slice.
Please use this sunburst example to play around: jsfiddle
var circles = g.append("circle")
.attr("cx",
function(d) {
return arc.centroid(d)[0];
})
.attr("cy",
function(d) {
return arc.centroid(d)[1];
})
.attr("r", 8)
.attr("stroke", "black")
.attr("stroke-width", 2)
.style("fill", "blue");