Related
I have a question about attrTween (sometimes tween()).
I understood custom tween function as
after " attrTween('d' " argument,
I define the custom function.
So, I wrote the custom function as below.
d3.selectAll('circle#circles1')
.transition()
.attrTween('d',function(){
let interpolator=d3.interpolateArray(sdata.vader,sdata1.vader);
return function(t){
return d3.select(this).attr('cy',interpolator(t))
}
})
What I intended is
For All the circles I drew, makes a transition. The transition
is attrTween. The changes is based on data array tied into the
circles. Original data array is sdata and the cy value in the
sdata is sdata.vader. And the transition is heading toward
sdata1.and cy value for sdata1 is sdata1.vader.
To access all the cy value for every single circle, I used
d3.select(this).attr('cy')
However, no error message is shown but no animation was made either.
What did I misunderstand for the custom tween function?
Can anyone help me to fix this code?
Thank you inadvance.
Full code is in the following link.
https://codepen.io/jotnajoa/pen/WNQeEBE
There are multiple problems in the example code, which is not minimal. Providing a minimal, reproducible example would really help solve the problems.
usage of HTML Id to multiple elements.
In HTML, and id attribute must be unique. Here, ids are assigned to groups of circles. A class attribute should be used for this purpose, not an id.
.attr('id','circles1')
should be:
.attr('class','circles1')
Accordingly, the attrTween should lookup the circles with class circle1, rather than the unique circle with id #circle1
d3.selectAll('circle#circles1')
should be
d3.selectAll('.circles1')
Id (or class) assigned in the wrong place.
The circles1 class is assigned before the creation of the circle, hence the instructions applies to an empty selection. The class attribute should be set right after circles have been created.
.attr('id','circles1')
.enter()
.append('circle')
should be
.enter()
.append('circle')
.attr('class','circles1')
Wrong attribute tweened
The attribute to transition is the circle's cy attribute, not a path's d attribute. Hence
.attrTween('d',function(){
should be
.attrTween('cy',function(){
Wrong data interpolated
sdata.vader and sdata1.vader do not exist, sdata and sdata1 seem to be arrays of objects, which in turn do have a vader property.
You probably want d.vader, and the corresponding .vader in sdata1, which would be sdata1[i].vader, in case items are the same order in both arrays.
Interpolating original measures instead of coordinates.
cy is originally defined as:
height-yscale(d.vader)
In the interpolator function, the scale function should also be used.
The attrTween function calls becomes:
.attrTween('cy',function(d, i){
//console.log( i, height-yscale(d.vader), height-yscale(sdata1[i].vader))
let interpolator=d3.interpolateArray(height-yscale(d.vader), height-yscale(sdata1[i].vader));
return function(t) { return interpolator(t)}
})
Using attrTween where not needed.
Simply transitioning the circles with attr is sufficient for this use case, there is no need to define an interpolator.
d3 will move the position of circles from the original position to the destination, interpolating implicitly.
d3.selectAll('.circles1')
.transition()
.duration(2000)
.attr('cy',function(d, i){
return height-yscale(sdata1[i].vader)
})
I added a long duration for demo purpose, to make obvious that the circles move to the correct location. Once in their final position, they disappear, because they are under the pink circles.
P.S. Same set of corrections is applicable to circles2 set whenever relevant.
Demo of the solution in the snippet below, as codepen does not allow to save modifications without creating an account.
var svg;
var xscale;
var yscale;
var sdata;
var xAxis;
var yAxis;
var width=1500;
var height=500;
var margin=50;
var duration =250;
var vader ='vader'
var textblob='textblob'
var delay =5000;
var tbtrue=false;
var areas
var circles1,circles2;
var sdata1,sdata2
d3.csv('https://raw.githubusercontent.com/jotnajoa/Javascript/master/tweetdata.csv').then(function(data){
svg=d3.select('body').append('svg').attr('width',width).attr('height',height)
var parser = d3.timeParse("%m/%d/%y")
// data를 처리했고, date parser 하는 법 다시한번 명심하자.
sdata = data;
sdata.forEach(function(d){
d.vader = +d.vader;
d.textblob= + d.textblob;
d.date=parser(d.date)
})
// scale을 정해야 함. 나중에 brushable한 범위로 고쳐야함. nice()안하면 정렬도안되고, 첫번째 엔트리 미싱이고
// 난리도 아님.
xscale=d3.scaleTime()
.domain(d3.extent(sdata, function(d) {return d.date }))
.range([0,width*9/10])
.nice()
yscale =d3.scaleLinear()
.domain(d3.extent([-1,1]))
.range([height*4/5,height*1/5])
.nice()
//yaxis는 필요 없을 것 같은데.
//캔버스에 축을 그려야 함 단, translate해서 중간에 걸치게 해야함.
svg.append('g').attr('class','xaxis')
.call(d3.axisBottom(xscale))
.attr('transform','translate('+margin+','+height*1/2+')')
//sdata plotting
var circles = svg.append('g').attr('class','circles')
var area = svg.append('g').attr('class','pathline')
firststage();
//generator로 데이터를 하나씩 떨어뜨리도록 한다.
function firststage(){
function* vaderdropping(data){
for( let i=0;i<data.length;i++){
if( i%50==0) yield svg.node();
let cx = margin+xscale(data[i].date)
let cy = height-yscale(data[i].vader)
circles.append('circle')
.attr('cx',cx)
.attr('cy',0)
.transition()
.duration(duration)
.ease(d3.easeBounce)
.attr('cy',cy)
.attr('r',3)
.style('fill','rgba(230, 99, 99, 0.528)')
}
yield svg.node()
}
//generator 돌리는 부분
let vadergen = vaderdropping(sdata);
let result = vadergen.next()
let interval = setInterval(function(){
if(!result.done) {
vadergen.next();
}
else {
clearInterval(interval)
}
}, 100);
setTimeout(secondstage,5000)
}
function secondstage(){
function* textblobdropping(data){
for( let i=0;i<data.length;i++){
if( i%50==0) yield svg.node();
let cx = margin+xscale(data[i].date)
let cy = height-yscale(data[i].textblob)
circles.append('circle')
.attr('cx',cx)
.attr('cy',0)
.transition()
.duration(duration)
.ease(d3.easeBounce)
.attr('cy',cy)
.attr('r',3)
.style('fill','rgba(112, 99, 230, 0.528)')
}
yield svg.node()
}
//generator 돌리는 부분
let textblobgen = textblobdropping(sdata);
let tresult = textblobgen.next()
let tinterval = setInterval(function(){
if(!tresult.done) {
textblobgen.next();
}
else {
clearInterval(tinterval)
}
}, 100);
setTimeout(thirdstage,2500)
}
function thirdstage(){
//진동을 만들기 위해서,
//베이다와 텍스트 블랍 값을 플립한거다 (제발 워크 아웃하길...)
//그 다음 트윈으로 sdata 와 sdata1을 왔다갔다 하게하면 되지않을까?
sdata1 = sdata.map(function(x){
var y={};
y['date']=x.date;
y['vader']=x.textblob;
y['textblob']=x.vader;
return y});
sdata2 = sdata.map(function(x){
var y={};
y['date']=x.date;
y['vader']=0;
y['textblob']=0;
return y});
d3.selectAll('circle').transition()
.duration(3500)
.style('fill','rgba(1, 1, 1, 0.228)')
//areas는 일종의 함수다, 에리아에다가 데이터를 먹이면,
//에리아를 그리는 역할을 하는것임.
areas = d3.area()
.x(function(d){return margin+xscale(d.date)})
.y0(function(d){return height-yscale(d.vader)})
.y1(function(d){return height-yscale(d.textblob)})
.curve(d3.curveCardinal)
//이렇게 하지말고, sdata2도 만들었으니까 2->1->0 반복하는
// 무한반복 on('end','repeat') loop를 만들어보자.
var uarea=area.append('path')
setTimeout(repeat,500)
function repeat(){
uarea
.style('fill','rgba(112, 99, 230, 0.4)')
.attr('d', areas(sdata))
.transition()
.duration(500)
.attrTween('d',function(){
var interpolator=d3.interpolateArray(sdata,sdata1);
return function(t){
return areas(interpolator(t))
}
})
.transition()
.duration(500)
.attrTween('d',function(){
var interpolator=d3.interpolateArray(sdata1,sdata2);
return function(t){
return areas(interpolator(t))
}
})
.transition()
.duration(500)
.attrTween('d',function(){
var interpolator=d3.interpolateArray(sdata2,sdata);
return function(t){
return areas(interpolator(t))
}
})
.on('end',repeat)
}
setTimeout(fourthstage,500)
}
function fourthstage(){
// console.log(d3.selectAll('circle#circles1').node())
circles1=svg.append('g').selectAll('circle').data(sdata)
.enter().append('circle').attr('class','circles1')
.attr('cx',function(d){return margin+xscale(d.date)})
.attr('cy',function(d){return height-yscale(d.vader)})
.style('fill','green')
.attr('r',3)
circles2=svg.append('g').selectAll('circle').data(sdata)
.enter().append('circle').attr('class','circles2')
.attr('cx',function(d){return margin+xscale(d.date)})
.attr('cy',function(d){return height-yscale(d.textblob)})
.style('fill','pink')
.attr('r',3)
d3.selectAll('.circles1')
.transition()
.duration(5000)
.attr('cy',function(d, i){
return height-yscale(sdata1[i].vader)
})
// d3.selectAll('circle#circles2')
// .transition()
// .attr('cy',function(d){return 0})
//tween 팩토리를 정의해야한다.
//주의사항, 리턴을 갖는 함수여야한다는 것.
//왜 꼭 return function(){}을 해야하나?
/*
function movey(d2){
let y1 = this.attr('cy')
let y2 = d2.vader
let interpolate=d3.interpolate(y1,y2);
interpolate;
} 하면 안되나??
*/
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
I currently have two variables that I can map to two different colours on two colours scales s1 and s2. s1 gives me the shade of red corresponding to my a value of my variable X (4 different possible colours). s1 gives me the shade of blue corresponding to my a value of my variable Y (4 different possible colours too).
Now what I would like to get is something that allows me to combine these two to get a unique colour for a combination of the variables. So for a pair (X,Y) I get a colour on the scale. So I get a scale of 16 possible colours.
Here is a legend that illustrate the kind of thing I am looking for:
I have been looking at online examples but cannot figure out how to achieve this.
You could combine two threshold scales fairly easily into a new scale function. The core of the function could look like:
d3.scaleBivariate = function() {
function scaleBivariate(value) {
var r = reds(value[0]);
var b = blues(value[1]);
return "rgb("+r+","+((r+b)/2)+","+b+")";
}
var blues = d3.scaleThreshold()
.range([255,205,155,105,55])
.domain([0,1,2,3,4,5]);
var reds = d3.scaleThreshold()
.range([255,205,155,105,55])
.domain([0,1,2,3,4,5]);
return scaleBivariate;
}
This sets the red and blue channels with the help of two d3 threshold scales. The green is simply set as the average between the two, though you could set that to whatever is desirable, say 0 or the minimum of the two other channels. My red/blue ranges are arbitrary and easily changed as well.
The above could be used as:
d3.scaleBivariate = function() {
function scaleBivariate(value) {
var r = reds(value[0]);
var b = blues(value[1]);
return "rgb("+r+","+((r+b)/2)+","+b+")";
}
var blues = d3.scaleThreshold()
.range([255,205,155,105,55])
.domain([0,1,2,3,4,5]);
var reds = d3.scaleThreshold()
.range([255,205,155,105,55])
.domain([0,1,2,3,4,5]);
return scaleBivariate;
}
// Dummy data:
var data = d3.range(16).map(function(d) {
return {x: d%4, y: Math.floor(d/4) }
})
var svg = d3.select("svg");
var size = 30;
var color = d3.scaleBivariate();
svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", function(d) { return d.x * size })
.attr("y", function(d) { return d.y * size })
.attr("width",size)
.attr("height",size)
.attr("fill",function(d) {
return color([d.x,d.y]);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.0.0/d3.min.js"></script>
<svg></svg>
Of course you might want to add some flexibility by adding methods to modify what datum property sets what colors are associated with what properties, what the thresholds should be, etc. To provide a basic example, the example below has added accessors for setting what property should be mapped to blue and red channels:
d3.scaleBivariate = function() {
function scaleBivariate(value) {
var r = reds(red(value));
var b = blues(blue(value));
return "rgb("+r+","+((r+b)/2)+","+b+")";
}
var blues = d3.scaleThreshold()
.range([255,205,155,105,55])
.domain([0,1,2,3,4,5]);
var reds = d3.scaleThreshold()
.range([255,205,155,105,55])
.domain([0,1,2,3,4,5]);
var red = function(d) { return d[0]; }
var blue = function(d) { return d[1];}
// Accessors:
scaleBivariate.red = function(_) {
return arguments.length ? (red = _, scaleBivariate): red;
}
scaleBivariate.blue = function(_) {
return arguments.length ? (blue = _, scaleBivariate): blue;
}
return scaleBivariate;
}
var data = d3.range(16).map(function(d) {
return {x: d%4, y: Math.floor(d/4) }
})
var svg = d3.select("svg");
var size = 30;
// set up the color scale:
var color = d3.scaleBivariate()
.red(function(d) { return d.x; })
.blue(function(d) { return d.y; });
svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", function(d) { return d.x * size })
.attr("y", function(d) { return d.y * size })
.attr("width",size)
.attr("height",size)
.attr("fill",color);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.0.0/d3.min.js"></script>
<svg></svg>
I have the following script for rendering a simple barchart in D3.js. I have been able to render charts ok up until a point.
I have this data below which I am struggling to insert into my chart, there is no specific key I can call upon and I'm really confused how I would insert all of these into my chart.
Object { "food-environmental-science": 0, "art-media-research": 0, .....}
I have a seperate file for the HTML (only a snippet):
var barchart1 = barchart("#otherchart");
function clickScatter(d){
var unitOfAssessment = d.UoAString;
click = d.environment.topicWeights
renderTopicWeights(click)
}
function renderTopicWeights(clickedPoint){
barchart1.loadAndRenderDataset(clickedPoint)
}
When I call upon the loadAndRenderDataset function, console gives me a data.map is not a function error.
function barchart(targetDOMelement) {
//=================== PUBLIC FUNCTIONS =========================
//
barchartObject.appendedMouseOverFunction = function (callbackFunction) {
console.log("appendedMouseOverFunction called", callbackFunction)
appendedMouseOverFunction = callbackFunction;
render();
return barchartObject;
}
barchartObject.appendedMouseOutFunction = function (callbackFunction) {
appendedMouseOutFunction = callbackFunction;
render();
return barchartObject;
}
barchartObject.loadAndRenderDataset = function (data) {
dataset=data.map(d=>d); //create local copy of references so that we can sort etc.
render();
return barchartObject;
}
barchartObject.overrideDataFieldFunction = function (dataFieldFunction) {
dataField = dataFieldFunction;
return barchartObject;
}
barchartObject.overrideKeyFunction = function (keyFunction) {
//The key function is used to obtain keys for GUP rendering and
//to provide the categories for the y-axis
//These valuse should be unique
GUPkeyField = yAxisCategoryFunction = keyFunction;
return barchartObject;
}
barchartObject.overrideMouseOverFunction = function (callbackFunction) {
mouseOverFunction = callbackFunction;
render();
return barchartObject;
}
barchartObject.overrideMouseOutFunction = function (callbackFunction) {
mouseOutFunction = callbackFunction;
render(); //Needed to update DOM
return barchartObject;
}
barchartObject.overrideTooltipFunction = function (toolTipFunction) {
tooltip = toolTipFunction;
return barchartObject;
}
barchartObject.overrideMouseClickFunction = function (fn) {
mouseClick2Function = fn;
render(); //Needed to update DOM if they exist
return barchartObject;
}
barchartObject.render = function (callbackFunction) {
render(); //Needed to update DOM
return barchartObject;
}
barchartObject.setTransform = function (t) {
//Set the transform on the svg
svg.attr("transform", t)
return barchartObject;
}
barchartObject.yAxisIndent = function (indent) {
yAxisIndent=indent;
return barchartObject;
}
//=================== PRIVATE VARIABLES ====================================
//Width and height of svg canvas
var svgWidth = 900;
var svgHeight = 450;
var dataset = [];
var xScale = d3.scaleLinear();
var yScale = d3.scaleBand(); //This is an ordinal (categorical) scale
var yAxisIndent = 400; //Space for labels
var maxValueOfDataset; //For manual setting of bar length scaling (only used if .maxValueOfDataset() public method called)
//=================== INITIALISATION CODE ====================================
//Declare and append SVG element
var svg = d3
.select(targetDOMelement)
.append("svg")
.attr("width", svgWidth)
.attr("height", svgHeight)
.classed("barchart",true);
//Declare and add group for y axis
var yAxis = svg
.append("g")
.classed("yAxis", true);
//Declare and add group for x axis
var xAxis = svg
.append("g")
.classed("xAxis", true);
//===================== ACCESSOR FUNCTIONS =========================================
var dataField = function(d){return d.datafield} //The length of the bars
var tooltip = function(d){return d.key + ": "+ d.datafield} //tooltip text for bars
var yAxisCategoryFunction = function(d){return d.key} //Categories for y-axis
var GUPkeyField = yAxisCategoryFunction; //For 'keyed' GUP rendering (set to y-axis category)
//=================== OTHER PRIVATE FUNCTIONS ====================================
var maxValueOfDataField = function(){
//Find the maximum value of the data field for the x scaling function using a handy d3 max() method
//This will be used to set (normally used )
return d3.max(dataset, dataField)
};
var appendedMouseOutFunction = function(){};
var appendedMouseOverFunction = function(){};
var mouseOverFunction = function (d,i){
d3.select(this).classed("highlight", true).classed("noHighlight", false);
appendedMouseOverFunction(d,i);
}
var mouseOutFunction = function (d,i){
d3.select(this).classed("highlight", false).classed("noHighlight", true);
appendedMouseOutFunction(d,i);
}
var mouseClick2Function = function (d,i){
console.log("barchart click function = nothing at the moment, d=",d)
};
function render () {
updateScalesAndRenderAxes();
GUP_bars();
}
function updateScalesAndRenderAxes(){
//Set scales to reflect any change in svgWidth, svgHeight or the dataset size or max value
xScale
.domain([0, maxValueOfDataField()])
.range([0, svgWidth-(yAxisIndent+10)]);
yScale
.domain(dataset.map(yAxisCategoryFunction)) //Load y-axis categories into yScale
.rangeRound([25, svgHeight-40])
.padding([.1]);
//Now render the y-axis using the new yScale
var yAxisGenerator = d3.axisLeft(yScale);
svg.select(".yAxis")
.transition().duration(1000).delay(1000)
.attr("transform", "translate(" + yAxisIndent + ",0)")
.call(yAxisGenerator);
//Now render the x-axis using the new xScale
var xAxisGenerator = d3.axisTop(xScale);
svg.select(".xAxis")
.transition().duration(1000).delay(1000)
.attr("transform", "translate(" + yAxisIndent + ",20)")
.call(xAxisGenerator);
};
function GUP_bars(){
//GUP = General Update Pattern to render bars
//GUP: BIND DATA to DOM placeholders
var selection = svg
.selectAll(".bars")
.data(dataset, GUPkeyField);
//GUP: ENTER SELECTION
var enterSel = selection //Create DOM rectangles, positioned # x=yAxisIndent
.enter()
.append("rect")
.attr("x", yAxisIndent)
enterSel //Add CSS classes
.attr("class", d=>("key--"+GUPkeyField(d)))
.classed("bars enterSelection", true)
.classed("highlight", d=>d.highlight)
enterSel //Size the bars
.transition()
.duration(1000)
.delay(2000)
.attr("width", function(d) {return xScale(dataField(d));})
.attr("y", function(d, i) {return yScale(yAxisCategoryFunction(d));})
.attr("height", function(){return yScale.bandwidth()});
enterSel //Add tooltip
.append("title")
.text(tooltip)
//GUP UPDATE (anything that is already on the page)
var updateSel = selection //update CSS classes
.classed("noHighlight updateSelection", true)
.classed("highlight enterSelection exitSelection", false)
.classed("highlight", d=>d.highlight)
updateSel //update bars
.transition()
.duration(1000)
.delay(1000)
.attr("width", function(d) {return xScale(dataField(d));})
.attr("y", function(d, i) {return yScale(yAxisCategoryFunction(d));})
.attr("height", function(){return yScale.bandwidth()});
updateSel //update tool tip
.select("title") //Note that we already created a <title></title> in the Enter selection
.text(tooltip)
//GUP: Merged Enter & Update selections (so we don't write these twice)
var mergedSel = enterSel.merge(selection)
.on("mouseover", mouseOverFunction)
.on("mouseout", mouseOutFunction)
.on("click", mouseClick2Function)
//GUP EXIT selection
var exitSel = selection.exit()
.classed("highlight updateSelection enterSelection", false)
.classed("exitSelection", true)
.transition()
.duration(1000)
.attr("width",0)
.remove()
};
return barchartObject;'
}
Any help would be much appreciated, and I appolguise if what I'm asking is not clear. Thanks
format your input data into object like {key:"",value:""} and pass this into d3 so that they can understand and render chart.
var input = {'a':1,"b":2};
function parseData(input){
return Object.keys(input).reduce(function(output, key){
output.push({"key":key,"value":input[key]});
return output;
},[])
}
console.log(parseData(input));
// [{"key":"a","value":1},{"key":"b","value":2}]
jsFiddle demo - https://jsfiddle.net/1kdsoyg2/1/
I are trying to create a Qlikview extension object to build a D3 Tree chart.
Below is the link for the Tree chart in D3
https://gist.github.com/mbostock/2949981
So for I am able to read the parent and child nodes and also able to plot the nodes(dots) on the Qlikview Object Window.
However the code is throwing an error while generating the links when the d3.svg.diagonal() function is being called in the d3.v2.js/d3.js/d3.v3.min.js
Below is the code snippet where my script throws an error.
var diagonal = d3.svg.diagonal().projection(function(d) { return [d.y, d.x]; });
// Create the link lines.
svg.selectAll(".link")
.data(links)
.enter().append("path")
.attr("class", "link")
.attr("d", diagonal);
Error: "Unable to get value of the property 'y': the object is null or undefined
Below is the diagonal function from the d3.v2.js where the error is thrown at line 3
d3.svg.diagonal = function() {
function diagonal(d, i) {
var p0 = source.call(this, d, i), p3 = target.call(this, d, i), m = (p0.y + p3.y) / 2, p = [ p0, {
x: p0.x,
y: m
}, {
x: p3.x,
y: m
}, p3 ];
p = p.map(projection);
return "M" + p[0] + "C" + p[1] + " " + p[2] + " " + p[3];
}
var source = d3_svg_chordSource, target = d3_svg_chordTarget, projection = d3_svg_diagonalProjection;
diagonal.source = function(x) {
if (!arguments.length) return source;
source = d3_functor(x);
return diagonal;
};
diagonal.target = function(x) {
if (!arguments.length) return target;
target = d3_functor(x);
return diagonal;
};
diagonal.projection = function(x) {
if (!arguments.length) return projection;
projection = x;
return diagonal;
};
return diagonal;
};
Kindly help
Here is the data I have used. It is a Simple .csv file with no x, y values:
source,target
flare,animate
flare,analytics
analytics,cluster
analytics,graph
animate,sequence
animate,transition
animate,interpolate
cluster,agglomerativeCluster
cluster,hierarchicalCluster
interpolate,arrayInterpolator
interpolate,colorInterpolator
interpolate,dateInterpolator
I'm using D3.js to manipulate some SVG elements. I learned (the hard way) that newer versions of FireFox don't really handle D3's force layout well. So I switched to a simple rotation and it's STILL running crappy in Firefox. In the following code, group1 is an array of 200 <circle> svg elements which I create dynamically:
function orbit( target, first ) {
/* Other easing options here: https://github.com/mbostock/d3/wiki/Transitions#wiki-d3_ease */
var ease = ( first ) ? 'sin-in' : 'linear';
target
.transition()
.duration(40000)
.ease( ease )
.attrTween("transform", rotTween)
.each('end', function(){ orbit( group1, false ); } );
function rotTween() {
var i = d3.interpolate(0, 360);
return function(t) {
return "rotate(" + i(t) + ","+width/2+","+height/2+")";
};
}
}
orbit( group1, true );
It's perfect smooth in Chrome but it chugs along like a Choo Choo train in Firefox.
As requested, here is how group1 is selected:
var makeNode = function(coeficient, x, y) {
coeficient = coeficient || 1;
return {
radius: (Math.random() * coeficient ).toFixed(2),
cx: function() { return x || Math.round(Math.random()*width) },
cy: function() { return y || Math.round(Math.random()*height) }
}
};
var nodes1 = d3.range(300).map( function(){ return makeNode(1.9); } );
var nodes2 = d3.range(700).map( function(){ return makeNode(.6); } );
// var nodes2 = [];
var svg = d3.select('#sky_svg');
var group1 = svg.append('g').attr("class", "group1");
var group2 = svg.append('g').attr("class", "group2");
var addNodes = function(group, nodes) {
for (var i=0; i<nodes.length; i++){
var node = nodes[i];
var circle = group.append('circle');
circle
.attr("r", node.radius )
.attr("cx", node.cx )
.attr("cy", node.cy )
.attr("stroke-width", 8 )
.attr("stroke", "transparent")
.style("fill", "#FFFFFF");
}
}
addNodes( group1, nodes1 );
addNodes( group2, nodes2 );
I also, consistently have problems with FireFox in rendering svg transformations that IE/Chrome handle with no problem. Follow the Posts:
Google Search: "Looking for SVG that was/is slow in Firefox"
You can also search on: Firefox's Gecko rendering engine+SVG, and see that Firefox has a poor rep for responsive SVG rendering.
My suggestion is to keep pressure on FireFox to fix this poor performance in dynamic SVG.