I have a bunch of square grids being constructed using two while loops. I am attempting to make them fade in individually, one at a time using the .transition function. However, I am noticing that all are transitioning in together at once. I realize D3 is asynchronous, but I am wondering if there is any way to override this.
See snippet below and link for fiddle: https://jsfiddle.net/nxtjddvr/
Thanks!
var x = 0;
var y = 1;
var xLoc = 0;
var yLoc = 100;
while (x < 3) {
svg.append('rect')
.transition()
.delay(function(d,i) {
return i*2000
})
.duration(5000)
.attr('width', '100')
.attr('height', '100')
.attr('x', xLoc)
.attr('y', 0)
.style('stroke', 'white' )
while (y < 3) {
svg.append('rect')
.transition()
.duration(5000)
.attr('id', 'trans')
.attr('width', '100')
.attr('height', '100')
.attr('x', xLoc)
.attr('y', yLoc)
.style('stroke', 'white' )
yLoc += 100;
y++;
}
yLoc = 100;
y=1;
xLoc += 100
x++;
}
You don't need loops in d3 and you don't need window timers, here is a rough idea of how to use d3 to do what you want...
var data = [32, 57, 112];
var height = 300;
var width = 300;
d3.select('#chart')
.append('svg')
.style('background-color', 'lightgrey')
var svg=d3.select('svg')
.attr('height', height)
.attr('width', width)
svg.selectAll("rect")
.data([1, 2, 3, 1, 2, 3, 1, 2, 3])
.enter().append('rect')
.attr('id', 'trans')
.attr('width', '100')
.attr('height', '100')
.attr('x', function(d, i){
return (d-1)*100
})
.attr('y', function(d,i){
return Math.floor((i/3))*100
})
.attr("opacity",0)
.style('stroke', 'white' )
.transition()
.delay(function(d,i) {
return i*2000
})
.duration(5000)
.attr("opacity",1)
You could use a sort of recursive setTimeout approach. The idea is this: fade out, wait, fade out, wait, etc. Until you're done.
Something like this:
var elementCount = 3;
function chainReaction() {
// remove your element here
elementCount--;
if(elementCount > 0) {
window.setTimeout(function(){
chainReaction();
}, 500);
}
}
chainReaction();
Here's a quick JSFiddle that does it: http://jsfiddle.net/dgrundel/5poes17s/2/
Related
I'm trying to simulate waves in D3 and, as you can see in the fiddle below, the waves--multiple rectangles that transition up and down in height--change in their duration to go from what appears to a single rolling wave to a bunch of rectangles bouncing up and down. Any help would be appreciated!
This is where the magic happens:
for (let i = 0; i < numRects; i++) {
const height = scaleY(seaLevel)
const bar = svg.append('rect')
.attr('fill', 'blue')
.attr('opacity', 0.7)
.attr('x', i * barWidth + xMargin)
.attr('width', barWidth)
.datum({delay: i * 20})
// add movement
function repeat (start) {
const low = scaleY(seaLevel - height)
const high = scaleY(seaLevel + height)
bar
.attr('y', waveBoxHeight - low)
.attr('height', low)
.transition()
.ease(d3.easeQuad)
.delay((d) => (start) ? d.delay : 0)
.duration(1000)
.attr('y', waveBoxHeight - high)
.attr('height', high)
.transition()
.ease(d3.easeQuad)
.duration(1000)
.attr('y', waveBoxHeight - low)
.attr('height', low)
.on('end', repeat)
}
repeat(true)
}
https://jsfiddle.net/bts9bpdz/
You could apply the chained transition strategy described in this bl.ock. See this modified wave example to fit your needs.
I've tried the circle plot example as the following:
var x=20, y=20, r=50;
var sampleSVG = d3.select("#viz")
.append("svg")
.attr("width", 800)
.attr("height", 600);
sampleSVG.append("circle")
.style("stroke", "gray")
.style("fill", "white")
.attr("r", r)
.attr("cx", x)
.attr("cy", y);
But I want to figure out how to plot without a loop a sequence of circles from an array like:
data = [
[10,20,30],
[20,30,15],
[30,10,25]
];
Maybe this example could help?
var data = [
[10,20,30],
[20,30,15],
[30,10,25]
];
var height = 300,
width = 500;
var svg = d3.select('body').append('svg')
.attr('height', height)
.attr('width', width)
.append('g')
.attr('transform', 'translate(30, 30)');
// Bind each nested array to a group element.
// This will create 3 group elements, each of which will hold 3 circles.
var circleRow = svg.selectAll('.row')
.data(data)
.enter().append('g')
.attr('transform', function(d, i) {
return 'translate(30,' + i * 60 + ')';
});
// For each group element 3 circle elements will be appended.
// This is done by binding each element in a nested array to a
// circle element.
circleRow.selectAll('.circle')
.data(function(d, i) { return data[i]; })
.enter().append('circle')
.attr('r', function(d) { return d; })
.attr('cx', function(d, i) { return i * 60; })
.attr('cy', 0);
Live Fiddle
http://jsfiddle.net/LsMZp/37/
I'm having issues stabilizing the patterns in this mock.
how do I ensure the image is always centrally aligned - it seems to jump around depending on its location/scale.
here is the code to this issue. Hoping the solution can be applied to this force chart example.
d3.js Force Chart - image/node linkage and animation
function addUserPatterns(patternsSvg, userData){
$.each(userData, function( index, value ) {
var defs = patternsSvg.append('svg:defs');
defs.append('svg:pattern')
.attr('id', "--"+index+"-"+value.userName.toLowerCase())
.attr('patternUnits', 'userSpaceOnUse')
.attr('width', 100)
.attr('height',100)
.append('svg:image')
.attr('xlink:href', value.userImage)
.attr('x', 0)
.attr('y', 0)
.attr('width', 100)
.attr('height', 100);
});
var circle = patternsSvg.append("svg:g")
.selectAll("circle")
.data(userData);
//enter
circle
.enter()
.append("svg:circle")
.attr("r", 50)
.style("fill", function(d, i) {
var imgUrl = "--"+i+"-"+d.userName.toLowerCase();
return "url(#"+imgUrl+")";
})
.attr("cy", function(d){
return random(0, 143);
})
.attr("cx", function(d){
return random(0, 143);
})
function random(min, max){
return Math.floor(Math.random() * (max - min + 1)) + min;
}
}
var patternsSvg = d3.select("body")
.append('svg')
.attr('class', 'patterns')
.attr('width', 300)
.attr('height', 300)
.append('g')
.attr("transform","translate(100, 100)")
var userData =[
{
"userName": "Ria",
"userImage" : "https://pbs.twimg.com/profile_images/427892889092231168/4c4Qwynr.png"
},
{
"userName": "Barry",
"userImage" : "https://lh3.googleusercontent.com/-XdASQvEzIzE/AAAAAAAAAAI/AAAAAAAAAls/5vbx7yVLDnc/photo.jpg"
}
]
addUserPatterns(patternsSvg, userData);
I have the images being appended to the circles ok now - but if the images are of different sizes/dimensions - is there a way to ensure the image will be fitted properly?
Is it just an assumption to ensure the images are of the same dimensions as of each other - or is there a more sophisticated way to calculate image width/height and then alter the pattern attributes as required?
http://jsfiddle.net/LsMZp/48/
var defs = patternsSvg.append('svg:defs');
defs.append('svg:pattern')
.attr('id', "--"+index+"-"+value.userName.toLowerCase())
//.attr('patternUnits', 'userSpaceOnUse')
.attr('x', 0)
.attr('y', 0)
.attr('width', 50)
.attr('height', 50)
.append('svg:image')
.attr('xlink:href', value.userImage)
.attr('x', 0)
.attr('y', 0)
.attr('width', 100)
.attr('height', 100);
});
I have a vertical-line which is drawn using d3. Suppose I want to repeat the line for a several number of times like a bar code. How do I do it?
I know that it has to be done using for but i don't have any clue on how to do it.
Suggestions will be very much helpful.
FIDDLE
Here's the code:
var height = 500;
var width = 500;
var svgContianer = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
var line = svgContianer.append("line")
.attr("x1", 20)
.attr("x2", 20)
.attr("y1", 100)
.attr("y2", 0)
.attr("stroke", "#000")
.attr("stroke-width", 2)
Thanks in advance.
If you want to create a bar code, the D3 way to do it would be to bind each line (element) with its width (data) and then using D3's enter phase to construct them.
Also, for bar-codes, you would also want to turn shape-rendering to crispEdges.
Demo.
var height = 500;
var width = 500;
var svgContianer = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
var line = svgContianer.selectAll("line")
.data([1, 2, 1, 3, 2, 1, 3, 2, 1, 3, 2, 4, 3, 1, 2, 2, 2, 1, 3, 2])
.enter()
.append("line")
.attr("x1", function (d, i) {
return 20 + 5 * i;
})
.attr("x2", function (d, i) {
return 20 + 5 * i;
})
.attr("y1", 100)
.attr("y2", 0)
.attr("stroke", "#000")
.attr("stroke-width", function (d) {
return d;
})
There are better options than using a for loop. But, in any case here you go
var height = 500;
var width = 500;
var count = 10, padding = 5;
var svgContianer = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
for(var i = 0; i < count; ++i) {
var line = svgContianer.append("line")
.attr("x1", padding*i)
.attr("x2", padding*i)
.attr("y1", 100)
.attr("y2", 0)
.attr("stroke", "#000")
.attr("stroke-width", 2)
}
I am trying to create a realtime barchart that plots values over time, using d3.js
This is how I am doing it.
var dataset = [ 5, 10, 15, 20, 25 ];
var w = 1800;
var h = 500;
var barPadding = 1;
setInterval(function(){
dataset.push(Math.floor(Math.random()*51));
draw();
},1000);
function draw(){
d3.select("svg").remove();
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
svg.selectAll("rect").data(dataset)
.enter()
.append("rect")
.attr("x", function(d, i){return 12*i;})
.attr("y", function(d){return h -d*4; })
.attr("width", 11)
.attr("height", function(d) { return d * 4; })
.attr("fill", "teal")
.attr("fill", function(d) { return "rgb(0, 0, " + (d * 10) + ")";});
}
The problem is that I am redrawing the whole graph every time a new value is added to the data array.
How do I append a bar to the bar graph that is already drawn, every time a new value is added to the array, rather than redrawing it every time?
You're close, just stop redrawing the svg element. If you only need to add new elements, then that's what your draw function should do when it's called.
var dataset = [ 5, 10, 15, 20, 25 ];
var w = 1800;
var h = 300;
var barPadding = 1;
var container = d3.select("body").append("svg").attr("width", w).attr("height", h).append("g");
setInterval(function(){
dataset.push(Math.floor(Math.random()*51));
draw();
},1000);
function draw(){
container.selectAll("rect").data(dataset).enter().append("rect")
.attr("x", function(d, i){return 12*i;})
.attr("y", function(d){return h -d*4; })
.attr("width", 11)
.attr("height", function(d) { return d * 4; })
.attr("fill", "teal")
.attr("fill", function(d) { return "rgb(0, 0, " + (d * 10) + ")";});
}
http://jsfiddle.net/Wexcode/LYqfU/
Not sure what kind of effect you're looking for, but have a look at this fiddle.
The following redraw function will keep adding to the barchart so it will continue to grow to the right:
function redraw() {
var rect = svg.selectAll("rect")
.data(dataset);
rect.enter().insert("rect", "line")
.attr("x", function(d, i) { return 12*(i+1); })
.attr("y", function(d) { return h -d*4 })
.attr("width", 11)
.attr("height", function(d) { return d * 4; })
.attr("fill", "teal")
.attr("fill", function(d) { return "rgb(0, 0, " + (d * 10) + ")";})
rect.transition()
.duration(800)
.attr("x", function(d, i) { return 12*i; });
}
Borrowed from an mbostock tutorial.