Related
`
var margin = {top: 50, right: 50, bottom: 50, left: 50}
, width = window.innerWidth - margin.left - margin.right
, height = window.innerHeight - margin.top - margin.bottom;
// n data points
var n = 7;
// X scale
var xScale = d3.scaleBand()
.domain(['A','B','C','D','F','E','Z']) // input
.range([0, width]); // output
// Y scale
var yScale = d3.scaleLinear()
.domain([0, 1])
.range([height, 0]);
var line = d3.line()
.x(function(d, i) { return xScale(i); })
.y(function(d) { return yScale(d.y); })
.curve(d3.curveMonotoneX)
var dataset = d3.range(n).map(function(d) { return {"y": d3.randomUniform(1)()} })
// SVGs
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("rect")
.attr("width", "100%")
.attr("height", "100%")
.attr("fill", "white");
svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// x axis call
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale));
// y axis call
svg.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(yScale));
svg.append("path")
.datum(dataset)
.attr("class", "line")
.attr("d", line);
// 12. Appends a circle for each datapoint
svg.selectAll(".dot")
.data(dataset)
.enter().append("circle") // Uses the enter().append() method
.attr("class", "dot") // Assign a class for styling
.attr("cx", function(d, i) { return xScale(i) })
.attr("cy", function(d) { return yScale(d.y) })
.attr("r", 6);
svg.append("text")
.attr("class", "title")
.attr("x", width/2)
.attr("y", 0 - (margin.top / 2))
.attr("text-anchor", "middle")
.text("Testing");
/* 13. Basic Styling with CSS */
/* Style the lines by removing the fill and applying a stroke */
.line {
fill: none;
stroke: green;
stroke-width: 3;
}
/* Style the dots by assigning a fill and stroke */
.dot {
fill: red;
stroke: #fff;
}
<!DOCTYPE html>
<meta charset="utf-8">
<style type="text/css">
</style>
<!-- Body tag is where we will append our SVG and SVG objects-->
<body>
</body>
<!-- Load in the d3 library -->
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
</script>
I need for each data point to correspond to an (string) x-coordinate.
I am knew to d3 and I have yet to get accustomed to formatting axis.
I would also be great if anyone can point me out to how to add a tooltip. (Just an explanation)
Thank you everyone.
Not sure why it keeps saying your: "
It looks like your post is mostly code; please add some more details."
`
The scaleOrdinal is mapped to an array of alphabets but when you are calculating the cx you are mapping to an integer i. To resolve this:
Separate the labels as as array first:
var labels = ['A','B','C','D','F','E','Z'];
Then pass the labels to the domain:
// X scale
var xScale = d3.scaleBand()
.domain(labels) // input
.range([0, width]); // output
Finally, when you call calculate the cx, you need to send a value which was used in the domain. In your case since your domain is an array of alphabets you need to reparse the i to that particular alphabet. Hence you need to return xScale(labels[i]) as below:
svg.selectAll(".dot")
.data(dataset)
.enter().append("circle") // Uses the enter().append() method
.attr("class", "dot") // Assign a class for styling
.attr("cx", function(d, i) { return xScale(labels[i]) })
.attr("cy", function(d) { return yScale(d.y) })
.attr("r", 6);
Full working snippet below. Hope this helps.
var margin = {top: 50, right: 50, bottom: 50, left: 50}
, width = window.innerWidth - margin.left - margin.right
, height = window.innerHeight - margin.top - margin.bottom;
// n data points
var n = 7;
//labels
var labels = ['A','B','C','D','F','E','Z'];
// X scale
var xScale = d3.scaleBand()
.domain(labels) // input
.range([0, width]); // output
// Y scale
var yScale = d3.scaleLinear()
.domain([0, 1])
.range([height, 0]);
var line = d3.line()
.x(function(d, i) { return xScale(i); })
.y(function(d) { return yScale(d.y); })
.curve(d3.curveMonotoneX)
var dataset = d3.range(n).map(function(d) { return {"y": d3.randomUniform(1)()} })
// SVGs
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("rect")
.attr("width", "100%")
.attr("height", "100%")
.attr("fill", "white");
svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// x axis call
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale));
// y axis call
svg.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(yScale));
svg.append("path")
.datum(dataset)
.attr("class", "line")
.attr("d", line);
// 12. Appends a circle for each datapoint
svg.selectAll(".dot")
.data(dataset)
.enter().append("circle") // Uses the enter().append() method
.attr("class", "dot") // Assign a class for styling
.attr("cx", function(d, i) { return xScale(labels[i]) })
.attr("cy", function(d) { return yScale(d.y) })
.attr("r", 6);
svg.append("text")
.attr("class", "title")
.attr("x", width/2)
.attr("y", 0 - (margin.top / 2))
.attr("text-anchor", "middle")
.text("Testing");
/* 13. Basic Styling with CSS */
/* Style the lines by removing the fill and applying a stroke */
.line {
fill: none;
stroke: green;
stroke-width: 3;
}
/* Style the dots by assigning a fill and stroke */
.dot {
fill: red;
stroke: #fff;
}
<!DOCTYPE html>
<meta charset="utf-8">
<style type="text/css">
</style>
<!-- Body tag is where we will append our SVG and SVG objects-->
<body>
</body>
<!-- Load in the d3 library -->
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
</script>
Updated Snippet with Lines:
var margin = {top: 50, right: 50, bottom: 50, left: 50}
, width = window.innerWidth - margin.left - margin.right
, height = window.innerHeight - margin.top - margin.bottom;
// n data points
var n = 7;
//labels
var labels = ['A','B','C','D','F','E','Z'];
// X scale
var xScale = d3.scaleBand()
.domain(labels) // input
.range([0, width]); // output
// Y scale
var yScale = d3.scaleLinear()
.domain([0, 1])
.range([height, 0]);
var line = d3.line()
.x(function(d, i) { return xScale(labels[i]); })
.y(function(d) { return yScale(d.y); })
.curve(d3.curveMonotoneX)
var dataset = d3.range(n).map(function(d) { return {"y": d3.randomUniform(1)()} })
// SVGs
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("rect")
.attr("width", "100%")
.attr("height", "100%")
.attr("fill", "white");
svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// x axis call
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale));
// y axis call
svg.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(yScale));
svg.append("path")
.datum(dataset)
.attr("class", "line")
.attr("d", line);
// 12. Appends a circle for each datapoint
svg.selectAll(".dot")
.data(dataset)
.enter().append("circle") // Uses the enter().append() method
.attr("class", "dot") // Assign a class for styling
.attr("cx", function(d, i) { return xScale(labels[i]) })
.attr("cy", function(d) { return yScale(d.y) })
.attr("r", 6);
svg.append("text")
.attr("class", "title")
.attr("x", width/2)
.attr("y", 0 - (margin.top / 2))
.attr("text-anchor", "middle")
.text("Testing");
/* 13. Basic Styling with CSS */
/* Style the lines by removing the fill and applying a stroke */
.line {
fill: none;
stroke: green;
stroke-width: 3;
}
/* Style the dots by assigning a fill and stroke */
.dot {
fill: red;
stroke: #fff;
}
<!DOCTYPE html>
<meta charset="utf-8">
<style type="text/css">
</style>
<!-- Body tag is where we will append our SVG and SVG objects-->
<body>
</body>
<!-- Load in the d3 library -->
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
</script>
I've noticed a change in behavior when updating from d3 version 4 to version 5. In v4, when a dataset contains all zero values for the y-axis, the "0" tick is correctly aligned to the bottom of the chart.
<head>
<!-- load the d3.js library -->
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
.line {
fill: none;
stroke: steelblue;
stroke-width: 2px;
}
</style>
</head>
<script>
var margin = {
top: 50,
right: 50,
bottom: 50,
left: 50
},
width = window.innerWidth - margin.left - margin.right,
height = window.innerHeight - margin.top - margin.bottom;
var n = 21;
// An array of objects of length N. Each object has key -> value pair, the key being "y" and the value is a random number
// var dataset = d3.range(n).map(function(d) { return {"y": d3.randomUniform(1)() } })
var dataset = d3.range(n).map(function(d) {
return {
"y": 0
}
})
var xScale = d3.scaleLinear()
.domain([0, n - 1])
.range([0, width]);
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset, d => d.y)])
.range([height, 0]);
var line = d3.line()
.x(function(d, i) {
return xScale(i);
})
.y(function(d) {
return yScale(d.y);
})
.curve(d3.curveMonotoneX)
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale));
svg.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(yScale));
svg.append("path")
.datum(dataset)
.attr("class", "line")
.attr("d", line);
</script>
For my use case, this is the expected behavior.
In v5, under the same conditions, the "0" is aligned to the center of the y-axis.
<head>
<!-- load the d3.js library -->
<script src="https://d3js.org/d3.v5.min.js"></script>
<style>
.line {
fill: none;
stroke: steelblue;
stroke-width: 2px;
}
</style>
</head>
<script>
var margin = {top: 50, right: 50, bottom: 50, left: 50}
, width = window.innerWidth - margin.left - margin.right
, height = window.innerHeight - margin.top - margin.bottom;
var n = 21;
// An array of objects of length N. Each object has key -> value pair, the key being "y" and the value is a random number
// var dataset = d3.range(n).map(function(d) { return {"y": d3.randomUniform(1)() } })
var dataset = d3.range(n).map(function(d) { return {"y": 0 } })
var xScale = d3.scaleLinear()
.domain([0, n-1])
.range([0, width]);
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset, d => d.y)])
.range([height, 0]);
var line = d3.line()
.x(function(d, i) { return xScale(i); })
.y(function(d) { return yScale(d.y); })
.curve(d3.curveMonotoneX)
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale));
svg.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(yScale));
svg.append("path")
.datum(dataset)
.attr("class", "line")
.attr("d", line);
</script>
The only difference between the two examples is the version of d3 that is loaded.
Is there any way that I can keep the same behavior exhibited in v4 in the current version (v5) of d3?
This is not a difference between D3 v4 and v5. Actually, this change was introduced in D3 v5.8.
Have a look here, this is your code using D3 v5.7:
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.js"></script>
<style>
.line {
fill: none;
stroke: steelblue;
stroke-width: 2px;
}
</style>
</head>
<script>
var margin = {
top: 50,
right: 50,
bottom: 50,
left: 50
},
width = window.innerWidth - margin.left - margin.right,
height = window.innerHeight - margin.top - margin.bottom;
var n = 21;
// An array of objects of length N. Each object has key -> value pair, the key being "y" and the value is a random number
// var dataset = d3.range(n).map(function(d) { return {"y": d3.randomUniform(1)() } })
var dataset = d3.range(n).map(function(d) {
return {
"y": 0
}
})
var xScale = d3.scaleLinear()
.domain([0, n - 1])
.range([0, width]);
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset, d => d.y)])
.range([height, 0]);
var line = d3.line()
.x(function(d, i) {
return xScale(i);
})
.y(function(d) {
return yScale(d.y);
})
.curve(d3.curveMonotoneX)
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale));
svg.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(yScale));
svg.append("path")
.datum(dataset)
.attr("class", "line")
.attr("d", line);
</script>
Now the same code, using D3 v5.8:
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.8.0/d3.js"></script>
<style>
.line {
fill: none;
stroke: steelblue;
stroke-width: 2px;
}
</style>
</head>
<script>
var margin = {
top: 50,
right: 50,
bottom: 50,
left: 50
},
width = window.innerWidth - margin.left - margin.right,
height = window.innerHeight - margin.top - margin.bottom;
var n = 21;
// An array of objects of length N. Each object has key -> value pair, the key being "y" and the value is a random number
// var dataset = d3.range(n).map(function(d) { return {"y": d3.randomUniform(1)() } })
var dataset = d3.range(n).map(function(d) {
return {
"y": 0
}
})
var xScale = d3.scaleLinear()
.domain([0, n - 1])
.range([0, width]);
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset, d => d.y)])
.range([height, 0]);
var line = d3.line()
.x(function(d, i) {
return xScale(i);
})
.y(function(d) {
return yScale(d.y);
})
.curve(d3.curveMonotoneX)
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale));
svg.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(yScale));
svg.append("path")
.datum(dataset)
.attr("class", "line")
.attr("d", line);
</script>
The explanation can be found on the release notes for D3 v5.8:
D3-Scale:
For collapsed domains, use midpoint of domain or range rather than start. (emphases mine)
Therefore, I'm afraid there is nothing you can do, unless moving back to D3 v5.7 or lower.
In fact, D3 v5.8 is so different from v5.7 (and not backwards compatible, see the new scale constructors, for instance, or the new join method) that in my humble opinion it should have been named D3 v6.0 (of course, following the semantic versioning it was still named v5 because there were no breaking changes). There is arguably more differences from v5.7 to v5.8 than from v4 to v5.
In your definition of the yScale domain there is only one value (zero). Therefor, the value is correctly displayed in the middle of the axis.
You can fix that by adapting the domain but then you will get more ticks on the axis.
Only one example (you could add any other number):
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset, d => d.y)+1])
.range([height, 0]);
Encountered this same issue in d3 v7 - the workaround is setting a valid range like previously answered and conditionally display the tick marks and labels.
const height = *HEIGHT OF YOUR CHART*
const dataMax = Math.max(...*YOUR DATA*)
const dataMin = Math.min(...*YOUR DATA*)
const dataMinMaxZero = dataMax === 0 && dataMin === 0
const scaleMax = dataMinMaxZero ? 1 : dataMax;
const yScale = d3.scaleLinear()
.domain([dataMin, scaleMax])
.range([height, 0]);
const yAxis = d3.axisLeft(yScale)
d3.select("#yaxis").call(yAxis);
if (dataMinMaxZero) {
d3.select("#yaxis").selectAll(".tick").style("opacity", 0);
} else {
d3.select("#yaxis").selectAll(".tick").style("opacity", 1);
}
I solved my initial goal to have the last point to be joint by a dashed line, while the rest of the curve is joint with solid line (see the image).
But, by doing that as follows, I lost the smooth of the curve.
How would you address this?
In addition, what to do for having a style supersedes a class like it does in HTML5? (the circle has a red stroke as a style attribute but win the class it has a blue one).
// curve
self.svg.append("path")
.datum(function() {return data.slice(0,data.length-1);})
.attr("stroke", colors(i))
.attr("class", "line ")
.attr("d", d3.line()
.curve(d3.curveCatmullRom)
.x(function(d) { return self.xSales(d.period) + self.xSales.bandwidth()/2;})
.y(function(d) { return self.ySales(v(d)); })
);
self.svg.append("path")
.datum(function() {return data.slice(data.length-2, data.length);})
.attr("stroke", colors(i))
.attr("class", "line currentPeriod")
.attr("d", d3.line()
.curve(d3.curveCatmullRom)
.x(function(d) { return self.xSales(d.period) + self.xSales.bandwidth()/2;})
.y(function(d) { return self.ySales(v(d)); })
);
With regards to my comment:
For your first question, you'll need to generate a single path and use dash-offset and a gradient coloring.
I provided a pretty good answer here about how to dash a section of single path but it doesn't provide the colors. So I've updated below for that:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.line {
fill: none;
stroke-width: 1.5px;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 50
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var data = d3.range(11).map(function(d, i) {
return {
x: i,
y: Math.random() * 100
};
});
var x = d3.scale.linear()
.range([0, width])
.domain([0, 10]);
var y = d3.scale.linear()
.range([height, 0])
.domain([0, 100]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
.x(function(d) {
return x(d.x);
})
.y(function(d) {
return y(d.y);
})
.interpolate("basis");
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var lG = svg.append("defs")
.append("linearGradient")
.attr("id", "lineColor")
.attr("x1", "0%")
.attr("x2", "100%")
.attr("y1", "0%")
.attr("y2", "0%");
lG.append("stop")
.attr("offset", "0")
.attr("stop-color", "steelblue");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
var p = svg.append("path")
.datum(data)
.attr("class", "line")
.attr("stroke", "url(#lineColor)")
.attr("d", line);
// draw dashed from 2.7 to 7 in the X domain
var dashBetweenX = [2.5, 7]
path = p.node(),
totalLen = path.getTotalLength();
// find the corresponding line lengths
var dashBetweenL = dashBetweenX.map(function(d, i) {
var beginning = 0,
end = totalLen,
target = null,
d = x(d);
// find the line lengths the correspond to our X values
// stolen from #duopixel from http://bl.ocks.org/duopixel/3824661
while (true) {
target = Math.floor((beginning + end) / 2);
pos = path.getPointAtLength(target);
if ((target === end || target === beginning) && pos.x !== d) {
break;
}
if (pos.x > d) end = target;
else if (pos.x < d) beginning = target;
else break; //position found
}
return target;
})
var sd = dashBetweenL[0],
dp = dashBetweenL[0],
count = 0;
while (dp < dashBetweenL[1]) {
count++;
dp += 4;
sd += ", 4";
}
if (count % 2 == 0) {
sd += ", 4, " + (totalLen - dashBetweenL[1]);
} else {
sd += ", " + (totalLen - dashBetweenL[1]);
}
p.attr("stroke-dasharray", sd);
lG.append("stop")
.attr("offset", dashBetweenX[0] / x.domain()[1])
.attr("stop-color", "steelblue");
lG.append("stop")
.attr("offset", dashBetweenX[0] / x.domain()[1] + 0.001)
.attr("stop-color", "red");
lG.append("stop")
.attr("offset", dashBetweenX[1] / x.domain()[1])
.attr("stop-color", "red");
lG.append("stop")
.attr("offset", dashBetweenX[1] / x.domain()[1] + 0.001)
.attr("stop-color", "steelblue");
lG.append("stop")
.attr("offset", '1')
.attr("stop-color", "steelblue");
</script>
</body>
</html>
My first time to post question here. I am converting my version 3 of d3 path line transition code to version 4, and I am having a hard time.
First of all, I saw Mike's example (posted about two days agao) of smooth line transition with non-time x axis for version 4, so I did the similar thing to his example of version 3 with time x axis. The path line moves smoothly, but the x axis doesn't. Also, for my work, I cannot trigger the transition from where he did in this example, so I cannot use the variable "this" in the tick function. Here is the code:
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="//d3js.org/d3.v4.min.js"></script>
<style>
.line {
fill: none;
stroke: #000;
stroke-width: 1.5px;
}
</style>
</head>
<body>
<svg width="960" height="500"></svg>
<script>
(function() {
var n = 243,
duration = 750,
now = new Date(Date.now() - duration),
count = 0,
data = d3.range(n).map(function() { return 0; });
random = d3.randomNormal(0, .2),
data = d3.range(n).map(random);
var margin = {top: 6, right: 0, bottom: 20, left: 40},
width = 960 - margin.right,
height = 120 - margin.top - margin.bottom;
var x = d3.scaleTime()
.domain([now - (n - 2) * duration, now - duration])
.range([0, width]);
var y = d3.scaleLinear()
.range([height, 0]);
var line = d3.line()
.x(function(d, i) { return x(now - (n - 1 - i) * duration); })
.y(function(d, i) { return y(d); });
var svg = d3.select("body").append("p").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.style("margin-left", -margin.left + "px")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var axis = svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(x.axis = d3.axisBottom().scale(x));
var timeline = svg.append("g")
.attr("clip-path", "url(#clip)")
.append("path")
.datum(data)
.attr("class", "line")
.transition()
.duration(500)
.ease(d3.easeLinear)
.on("start", tick);
var transition = d3.select({}).transition()
.duration(750)
.ease(d3.easeLinear);
function tick() {
data.push(random());
now = new Date();
x.domain([now - (n - 2) * duration, now - duration]);
y.domain([0, d3.max(data)]);
// redraw the line
svg.select(".line")
.attr("d", line)
.attr("transform", null);
// slide the x-axis left
axis.call(x.axis);
// slide the line left
d3.active(this)
.attr("transform", "translate(" + x(now - (n - 1) * duration) + ")")
.transition().on("start", tick);
// pop the old data point off the front
data.shift();
}
})()
</script>
</body>
at the tick function, there is a "this", from debugging, I found out it's a path, so I tried to replace it with d3.active(d3.selectAll("path")), or d3.active(d3.selectAll(".line")), neither works. I also tried to assign a variable timeline to the path, so that I tried d3.active(timeline). It doesn't work either.
I am at my wits' end on this issue. I posted on d3 google group, nobody answered. I hope somebody here can give me some suggestions.
Thanks
Diana
The transition to v4 is indeed not easy. Had the same issue as you. Try the following code:
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="//d3js.org/d3.v4.min.js"></script>
<style>
.line {
fill: none;
stroke: #000;
stroke-width: 1.5px;
}
</style>
</head>
<body>
<svg width="960" height="500"></svg>
<script>
(function() {
var n = 243,
duration = 750,
now = new Date(Date.now() - duration),
count = 0,
data = d3.range(n).map(function() {
return 0;
});
random = d3.randomNormal(0, .2),
data = d3.range(n).map(random);
var margin = {top: 6, right: 0, bottom: 20, left: 40},
width = 960 - margin.right,
height = 120 - margin.top - margin.bottom;
var x = d3.scaleTime()
.domain([now - (n - 2) * duration, now - duration])
.range([0, width]);
var y = d3.scaleLinear()
.range([height, 0]);
var line = d3.line()
.x(function(d, i) {
return x(now - (n - 1 - i) * duration);
})
.y(function(d, i) {
return y(d);
});
var svg = d3.select("body").append("p").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.style("margin-left", -margin.left + "px")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var axis = svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(x.axis = d3.axisBottom().scale(x));
var timeline = svg.append("g")
.attr("clip-path", "url(#clip)")
.append("path")
.datum(data)
.attr("class", "line")
.transition()
.duration(500)
.ease(d3.easeLinear);
var transition = d3.select({}).transition()
.duration(750)
.ease(d3.easeLinear);
(function tick() {
transition = transition.each(function() {
data.push(random());
now = new Date();
x.domain([now - (n - 2) * duration, now - duration]);
y.domain([0, d3.max(data)]);
// redraw the line
svg.select(".line")
.attr("d", line)
.attr("transform", null);
// slide the x-axis left
axis.call(d3.axisBottom(x));
// slide the line left
//d3.active(this).attr("transform", "translate(" + x(now - (n - 1) * duration) + ")");
// pop the old data point off the front
data.shift();
}).transition().on("start", tick);
})();
})()
</script>
</body>
The trick is to chain transitions and using transition().on rather than transition().each. To update the axis you need to call the d3.axisBottom(x) in the same way that you instantiate the axis.
I am starting with this simple example but I have the following error (using Firebug)
TypeError: string is undefined
[Break On This Error]
var c, p, i = 0, n = template.length, m = string.length;
Any hint?? (I tried similar responses and it didn't work, I guess there is something wrong with the time and date format, but I believe the "%Y-%m-%d" format is the right one that I have in the csv file...)
Thanks!!
//here my prueba.html file
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body{font: 12px arial;}
path{stroke: steelblue;
stroke-width: 2;
fill: none;}
.axis path,
.axis line {fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;}
</style>
<body>
<script type="text/javascript" src="d3/d3.v3.js"></script>
<script>
var margin = {top: 30, right: 20, bottom: 30, left: 50},
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
var parseDate = d3.time.format("%Y-%m-%d").parse; // HERE ERROR !!!
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
var valueline = d3.svg.line()
.x(function(d) { return x(d.timestamp); })
.y(function(d) { return y(d.temperature); });
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top +")");
// Get the data
d3.tsv("data/data.csv", function(error, data)
{data.forEach(function(d)
{d.timestamp = parseDate(d.timestamp); // HERE ERROR !!!
d.temperature = +d.temperature;});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.close; })]);
svg.append("path") // Add the valueline path.
.attr("d", valueline(data));
svg.append("g") // Add the X Axis
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g") // Add the Y Axis
.attr("class", "y axis")
.call(yAxis);
});
</script>
</body>
//here my data.csv file
timestamp,temperature
1900-01-01,20
1900-01-02,20
1900-01-03,17
1900-01-04,23
1900-01-05,15
1900-01-06,22
1900-01-07,24
1900-01-08,15
1900-01-09,25
1900-01-10,19
1900-01-11,23
1900-01-12,19
1900-01-13,17
1900-01-14,15
1900-01-15,17
1900-01-16,21
1900-01-17,23
1900-01-18,25
1900-01-19,17
1900-01-20,22
1900-01-21,23
1900-01-22,17
1900-01-23,15
1900-01-24,23
1900-01-25,19
1900-01-26,25
1900-01-27,21
1900-01-28,22
1900-01-29,20
1900-01-30,15
1900-01-31,21
1900-02-01,19
1900-02-02,15
1900-02-03,15
1900-02-04,25
1900-02-05,23
1900-02-06,25
1900-02-07,15
1900-02-08,18
1900-02-09,22
1900-02-10,15
1900-02-11,19
1900-02-12,18
1900-02-13,24
1900-02-14,22
1900-02-15,16
1900-02-16,21
1900-02-17,24
1900-02-18,25
A picture of this using Chrome developer tools...
http://i.imgur.com/0vZhCgK.jpg?1
You are using d3.tsv, you should be using d3.csv instead.
Also if you want to use tsv (Tab-separated values) you'll need to replace every comas by a tabulation.
PS: care to not save the file with an editor that automatically replace the tabulations with spaces :)