Is there any way to transition between text values for a particular element - e.g. fading between them or something similar? I've found no relevant references to this, but D3 seems built for this kind of thing. This jsfiddle may assist: http://jsfiddle.net/geotheory/2wJr7/
<p>Intro text - click mouse on this text</p>
<script>
var i = 0;
var data = ['text 1','text 2','text 3','text 4','text 5','text 6','text 7','text 8'];
var change = d3.select('p').on("mousedown", function(){transition();});
function transition() {
change.transition().duration(1000).text(data[i]);
i = i + 1;
}
</script>
You can do this by setting the opacity to 0 and then to 1 in two connected transitions:
change.transition().duration(500)
.style("opacity", 0)
.transition().duration(500)
.style("opacity", 1)
.text(data[i]);
Complete jsfiddle here.
Like this?
var data = ['text 1','text 2','text 3','text 4','text 5','text 6','text 7','text 8'];
var change = d3.select('p').on("mousedown", function(){transition();});
function transition() {
for (i=0; i < data.length; i++)
change
.transition()
.delay(i * 1000)
.duration(500)
.text(data[i]);
}
This is more beautiful than changing opacity
function transition() {
d3.select('text').transition()
.duration(500)
.style("font-size","1px")
.transition()
.duration(500)
.text(data[i])
.style("font-size","16px");
i = i + 1;
}
Related
I have been trying this code to create a bar chart with given data. It works but it never returns a red, rather returns blue always. I have tried putting b[i]>0, still only blue bars. The function wit parameter b is executed for sure and I have tried (b,i) too.
<!DOCTYPE html>
<html>
<head>
<script src = "https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<svg height = "250px" width="500px"></svg>
<script>
var b = [5,9,6,4,3];
var x = 100;
const height = 250;
for(var i=0; i<b.length;i++)
{
var svg = d3.select("svg").data(b).append("rect").attr("x",x)
.attr("y",height - (b[i]*20))
.attr("width",20).attr("height", b[i]*20).attr("fill", function(b){
if(b[i]<6) //This if condition never gets checked and hence doesn't work
{
return "red";
}
else
{
return "blue"; //This executes
}
})
x = x + 25; //Increment for the position of next bar
}
</script>
</body>
</html>
You make a shadow variable b, as an argument of the callback, so it is not an array, but the item of it. Since you are using cycle for drawing each point separately, you could avoid use callback for this.
So instead of
.attr("fill", function(b) { ... })
you need to write this
.attr("fill", b[i] < 6 ? "red" : "blue")
Btw, d3 js can iterate points for you:
<script>
var b = [5, 9, 6, 4, 3];
var xOffset = 100;
const height = 250;
var svg = d3.select("svg")
.selectAll('rect')
.data(b)
.enter()
.append('rect')
.attr("x", (d, i) => xOffset + 25 * i)
.attr("y", d => height - d * 20)
.attr("width", 20)
.attr("height", d => d * 20)
.attr("fill", d => d < 6 ? "red" : "blue");
</script>
i'm using a rowchart to show the total of sales by item of a salesman.
Already tried a composite chart unsuccessfully like many posts from the google, but none of the examples uses a rowchart.
I need to do like the image, creating the red lines to represent the sale value target for each item, but i dont know how, can you guys help me? Thanks!
Actually this is my code to plot the rowchart
spenderRowChart = dc.rowChart("#chart-row-spenders");
spenderRowChart
.width(450).height(200)
.dimension(itemDim)
.group(totalItemGroup)
.elasticX(true);
Obviously you need a source for the target data, which could be a global map, or a field in your data.
I've created an example which pulls the data from a global, but it would also take from the data if your group reduction provides a field called target.
Then, it adds a new path element to each row. Conveniently the rows are already SVG g group elements, so anything put in there will already be offset to the top left corner of the row rect.
The only coordinate we are missing is the height of the rect, which we can get by reading it from one of the existing bars:
var height = chart.select('g.row rect').attr('height');
Then we select the gs and use the general update pattern to add a path.target to each one if it doesn't have one. We'll make it red, make it visible only if we have data for that row, and start it at X 0 so that it will animate from the left like the row rects do:
var target = chart.selectAll('g.row')
.selectAll('path.target').data(function(d) { return [d]; });
target = target.enter().append('path')
.attr('class', 'target')
.attr('stroke', 'red')
.attr('visibility', function(d) {
return (d.value.target !== undefined || _targets[d.key] !== undefined) ? 'visible' : 'hidden';
})
.attr('d', function(d) {
return 'M0,0 v' + height;
}).merge(target);
The final .merge(target) merges this selection into the main selection.
Now we can now animate all target lines into position:
target.transition().duration(chart.transitionDuration())
.attr('visibility', function(d) {
return (d.value.target !== undefined || _targets[d.key] !== undefined) ? 'visible' : 'hidden';
})
.attr('d', function(d) {
return 'M' + (chart.x()(d.value.target || _targets[d.key] || 0)+0.5) + ',0 v' + height;
});
The example doesn't show it, but this will also allow the targets to move dynamically if they change or the scale changes. Likewise targets may also become visible or invisible if data is added/removed.
thank you, due the long time to have an answer i've developed a solution already, but, really thank you and its so nice beacause its pretty much the same ideia, so i think its nice to share the code here too.
The difference its in my code i use other logic to clear the strokes and use the filter value of some other chart to make it dynamic.
.renderlet(function(chart) {
dc.events.trigger(function() {
filter1 = yearRingChart.filters();
filter2 = spenderRowChart.filters();
});
})
.on('pretransition', function(chart) {
if (aux_path.length > 0){
for (i = 0; i < aux_path.length; i++){
aux_path[i].remove();
}
};
aux_data = JSON.parse(JSON.stringify(data2));
aux_data = aux_data.filter(venda => filter1.indexOf(venda.Nome) > -1);
meta_subgrupo = [];
aux_data.forEach(function(o) {
var existing = meta_subgrupo.filter(function(i) { return i.SubGrupo === o.SubGrupo })[0];
if (!existing)
meta_subgrupo.push(o);
else
existing.Meta += o.Meta;
});
if (filter1.length > 0) {
for (i = 0; (i < Object.keys(subGrupos).length); i++){
var x_vert = meta_subgrupo[i].Meta;
var extra_data = [
{x: chart.x()(x_vert), y: 0},
{x: chart.x()(x_vert), y: chart.effectiveHeight()}
];
var line = d3.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.curve(d3.curveLinear);
var chartBody = chart.select('g');
var path = chartBody.selectAll('path.extra').data([extra_data]);
path = path.enter()
.append('path')
.attr('class', 'oeExtra')
.attr('stroke', subGruposColors[i].Color)
.attr('id', 'ids')
.attr("stroke-width", 2)
.style("stroke-dasharray", ("10,3"))
.merge(path)
path.attr('d', line);
aux_path.push(path);
}
}
})
And that's how it looks
I have two datasets in my chart and I'm switching between them by clicking to the paragraphs (I'm drawing the buttons there later). The datasets have the different dimensions so I want to replace one another in the tooltip according to the chosen dataset. I can adjust the tooltip once
.on("mouseover", function(d, i) {
var tickDate = d3.select(d3.selectAll(".axis .tick text")[0][i]).data()[0];
console.log (tickDate);
var formatDate = RU.timeFormat("%B %Y");
var tooltipDate = formatDate(tickDate);
//Get this bar's x/y values, then augment for the tooltip
var xPosition = parseFloat(d3. select(this). attr("x")) + ((barWidth - barPadding) / 2);
var yPosition = parseFloat(d3. select(this). attr("y")) / 2 + h / 2;
//Update the tooltip position and value
d3.select("#tooltip" )
.style("left" , xPosition + "px")
.style("top" , yPosition + "px")
.select("#value")
.text(d + " DIMENSION1");
d3.select("#tooltip" )
.select("#label")
.text(tooltipDate);
//Show the tooltip
d3.select("#tooltip" ).
classed("hidden" , false);
d3.select(this)
.attr("fill", "orange");
})
but I don't manage to refresh it after the switching. There is the text "DIMENSION1" in both cases now, my purpose is the text "DIMENSION2" appearance after switching and return "DIMENSION1" after the choosing of the initial dataset.
Here is my fiddle: https://jsfiddle.net/anton9ov/dw1xp1ux/
Several problems here :
Avoid code duplication by creating a function transition(dataset, dimension) called for the transitions
You don't update the mouseover event when you change you dataset. So call your mouseover function each time you update the data
svg.selectAll("rect").on("mouseover", function(d, i) {
// Your mouseover function
});
See this fiddle https://jsfiddle.net/bfz97hdw/
(I also fixed the color issue)
I am working with D3.js and actually I am trying to select all elements with a certain class except one on the particular event "mouseover".
I tried different kind of solutions, but no one worked or just partially.
This is my first solution:
.on("mouseover",
function(d) {
d3.select(this)
.style("stroke-width", "4px");
var selectedMethod = document.getElementById(d.name + "-line");
var notSelectedMethods = $(".method").not(selectedMethod);
d3.selectAll(notSelectedMethods)
.style("opacity", 0.2);
var selectedLegend = document.getElementById(d.name + "-legend");
var notSelectedLegends = $(".legend").not(selectedLegend);
d3.selectAll(notSelectedLegends)
.style("opacity", 0.2);
}
)
Debugging I can see that notSelectedMethods store all nodes ignoring the not() function. This is true for the first part, because with the second one of the snippet work.
Looking around I found this one, so I tried what that they said (focusing on the first part, the line selection), but no one work.
d3.selectAll(".method")
.style("opacity",
function() {
return (this === selectedMethod) ? 1.0 : 0.2;
}
);
Or
var selectedMethod = this;
d3.selectAll(".method")
.filter(
function(n, i) {
return (this !== selectedMethod);
}
)
.style("opacity", 0.2);
Or
d3.selectAll(".method:not(." + this.id + ")")
.style("opacity", 0.2);
How can I solve this issue?
UPDATE:
#TomB and #altocumulus point me in the right direction. With some bit changes, code are now working.
var selectedMethod = d.name;
d3.selectAll(".method")
.style("opacity",
function(e) {
return (e.name === selectedMethod) ? 1.0 : 0.2;
}
);
I did not mention data structure of the d element, that's was my bad.
This snipper do the job. I think I cannot do better, am I right?
UPDATE 2:
I cheered too soon. I tried to replicate previously on mouseover solution on legend to change lines and legend (same logic as before)
.on("mouseover",
function(d) {
// Change opacity of legend's elements
var selectedLegend = d.name;
d3.selectAll(".legend")
.style("opacity",
function(e) {
return (e.name === selectedLegend) ? 1.0 : 0.2;
}
);
// Change width of selected line
var selectedMethod = d.name;
d3.selectAll(".method")
.style("stroke-width",
function(e) {
return (e.name === selectedMethod) ? "4.5px" : "1.5px";
}
);
// Change opacity of no-selected lines
d3.selectAll(".method")
.style("opacity",
function(e) {
return (e.name === selectedMethod) ? 1.0 : 0.2;
}
);
I do not know why, snippet where I change width do not work (width does not change).
You have to compare the id like that :
.on("mouseover", function(d) {
d3.select(this).style("stroke-width", "4px");
var selectedLine = d.name + "-line";
d3.selectAll(".method")
.style("opacity", function(e) {
return (e.id === selectedLine) ? 1.0 : 0.2;
});
})
All items with "method" class will have an opacity of 1 except the one with id === d.name + "-line"
You can't compare JQuery items and D3 items like that, have a look at http://collaboradev.com/2014/03/18/d3-and-jquery-interoperability/
#TomB and #altocumulus point me in the right direction. With some bit changes, code are now working.
var selectedMethod = d.name;
d3.selectAll(".method")
.style("opacity",
function(e) {
return (e.name === selectedMethod) ? 1.0 : 0.2;
}
);
I did not mention data structure of the d element, that's was my bad.
I am developing a line chart using D3.js library and AngularJS in Ionic.
I want to customize the colour of data point depending on the value of Y-axis. Say,
the value is in range 0-30 show the data point in green colour,
the value is in range 31-40 show the data point in yellow colour,
the value is in range 41-60 show the data point in red colour etc.
I am using D3 for the first time. Also, In my code I'll be fetching data dynamically(to be specific) from back-end json file. At the back-end, there is a parameter named e(dependent on data value of Y-axis) which ranges from 0 to 3 and accordingly colour code is set at front-end depending on the which value e has. Also, the e parameter will wary for different scenarios of Y axis(Sales, Tax). For sales, 0 means 110-150, but for Tax, 0 means 50-90.Can anyone help me with this?
Expected line chart image
You can try something like this to color the circle nodes for the y axis values (In my example below its data close):
svg.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 3.5)
.style("fill", function(d){
if (d.close < 100)
return "red";
if (d.close < 200)
return "brown";
if (d.close < 300)
return "green";
if (d.close < 400)
return "blue";
if (d.close < 500)
return "yellow" ;
if (d.close < 600)
return "pink" ;
if (d.close < 700)
return "orange" ;
})
For angular you need to make this as directive...
EDIT
I have added a div for showing tooltip like below
<div id="mytooltip" style="position: absolute; z-index: 10; visibility: hidden; top: 82px; left: 81px;">
<div id="ttclose"></div>
<div id="ttdate"></div>
</div>
Then on move event you can show and set values as below:
.on("mouseover", function () {
return d3.select("#mytooltip").style("visibility", "visible"); //making the tooltip visible
})
.on("mousemove", function (d) {
console.log()
d3.select("#mytooltip").style("top", (d3.mouse(this)[1] + 10) + "px").style("left", (d3.mouse(this)[0] + 10) + "px");
d3.select("#mytooltip").select("#ttdate").text(function () {
return d.date; //setting the date values to tooltip
});
d3.select("#mytooltip").select("#ttclose").text(function () {
return d.close; //setting the date values to tooltip
});
return;
})
.on("mouseout", function () {
return d3.select("#mytooltip").style("visibility", "hidden"); //hidding the tooltip
});
Full working code here
Hope this helps!