Yo, dudes.
We last left off with what looked like a really messy line graph layout. This was deliberate, because now with all the individual column groups separated out, it's easier to test your data. Things are going to get a little math-heavy.
Let's begin by modifying the
displayData() function. After deriving
max, we declare a whole series of variables -
percentage,
nextpercentage,
actual,
nextactual,
margintop and
nextmargintop. These are the variables needed for node and line manipulation.
Then there are the variables
node,
line and
tx. These are the variables that are objects. More on that later.
For now, create a nested
For loop. The outer loop should iterate through the
cols array of the
graphdata object while the inner loop should iterate through the
rows array of the
graphdata object.
function displayData()
{
var stat = document.getElementById("ddlStat").value;
var max = getMaxStatistic(stat);
var percentage,nextpercentage,actual,nextactual,margintop,nextmargintop;
var node,line,tx;
for (var i=0;i<graphdata.cols.length;i++)
{
for (var j=0;j<graphdata.rows.length;j++)
{
}
}
displayScale(max);
}
Now, right in the inner loop, set
actual by running the
getStatistic() function. The function is pretty much identical to the one we created for the bar chart...
function displayData()
{
var stat = document.getElementById("ddlStat").value;
var max = getMaxStatistic(stat);
var percentage,nextpercentage,actual,nextactual,margintop,nextmargintop;
var node,line,tx;
for (var i=0;i<graphdata.cols.length;i++)
{
for (var j=0;j<graphdata.rows.length;j++)
{
actual = getStatistic(graphdata.cols[i],graphdata.rows[j],stat);
}
}
displayScale(max);
}
function getStatistic(data,row,stat)
{
var temp;
temp = data.stats.filter(function (x) {return x.year==row;});
if (temp.length>0)
{
return temp[0][stat];
}
return 0;
}
...except for this. This is necessary because in some cases, data would be
undefined.
function getStatistic(data,row,stat)
{
if (data==undefined) return 0;
var temp;
temp = data.stats.filter(function (x) {return x.year==row;});
if (temp.length>0)
{
return temp[0][stat];
}
return 0;
}
Now that we have the value of the statistic set to the variable
actual, just as for the bar chart, we define
percentage.
margintop will use
percentage to determine how many pixels the node needs to be from the top, going by a height of 80% of 500 pixels. Minus 5 because the node is 10 pixels in height and we want its center to be at the level specified, see?
function displayData()
{
var stat = document.getElementById("ddlStat").value;
var max = getMaxStatistic(stat);
var percentage,nextpercentage,actual,nextactual,margintop,nextmargintop;
var node,line,tx;
for (var i=0;i<graphdata.cols.length;i++)
{
for (var j=0;j<graphdata.rows.length;j++)
{
actual = getStatistic(graphdata.cols[i],graphdata.rows[j],stat);
percentage = 100 - ((actual/max) * 100);
margintop = ((percentage/100 * (0.8*500)) -5);
}
}
displayScale(max);
}
And the we set the variable
node to the element whose id corresponds to the
i and
j value (remember setting ids for those nodes?), set the
margin-top property using the
margintop variable, and display the value of
actual using the
innerHTML property for good measure.
function displayData()
{
var stat = document.getElementById("ddlStat").value;
var max = getMaxStatistic(stat);
var percentage,nextpercentage,actual,nextactual,margintop,nextmargintop;
var node,line,tx;
for (var i=0;i<graphdata.cols.length;i++)
{
for (var j=0;j<graphdata.rows.length;j++)
{
actual = getStatistic(graphdata.cols[i],graphdata.rows[j],stat);
percentage = 100 - ((actual/max) * 100);
margintop = ((percentage/100 * (0.8*500)) -5);
node = document.getElementById("node_" + i + "_" +j);
node.style.marginTop = margintop + "px";
node.innerHTML = actual;
}
}
displayScale(max);
}
Now, you see that the nodes have shifted. The values displayed in each node should correspond with the scale.
Next, you want to start rotating the lines so that they join to the next node. First, we need an
If block to ensure that we only attempt this for columns preceding the final column (which only has a node and no line to manipulate).
function displayData()
{
var stat = document.getElementById("ddlStat").value;
var max = getMaxStatistic(stat);
var percentage,nextpercentage,actual,nextactual,margintop,nextmargintop;
var node,line,tx;
for (var i=0;i<graphdata.cols.length;i++)
{
for (var j=0;j<graphdata.rows.length;j++)
{
actual = getStatistic(graphdata.cols[i],graphdata.rows[j],stat);
percentage = 100 - ((actual/max) * 100);
margintop = ((percentage/100 * (0.8*500)) -5);
node = document.getElementById("node_" + i + "_" +j);
node.style.marginTop = margintop + "px";
node.innerHTML = actual;
if (j<graphdata.rows.length-1)
{
}
}
}
displayScale(max);
}
Before going further, I want to bring to your attention
Pythagoras's Theorem. Your objective is to rotate the
magenta line so that the end touches the next node. The length of the line will need to be increased as well, naturally. So as per the diagram below, we need the angle
x and we need the hypotenuse,
h.
a is definitely 200 pixels due to the specification of the
col_container CSS class.
The length of
o needs to be derived. It's not as simple as 24 - 14 = 10. We have to take into account that the number of pixels is based on a height of 80% of 500 pixels!
But basically, once you have
o and
a, you can get
h and
x.
We'll begin by getting the statistic of the next node, running
getStatistic() and setting the returned value to the variable
nextactual.
nextpercentage and
nextmargintop are then derived the same way as
percentage and
margintop.
Then we set the variable
tx by running
getLineTransform(), which will return an object. We will pass in the difference between
margintop and
nextmargintop, and the number 200 as arguments. And of course, we'll create the
getLineTransform() function.
function displayData()
{
var stat = document.getElementById("ddlStat").value;
var max = getMaxStatistic(stat);
var percentage,nextpercentage,actual,nextactual,margintop,nextmargintop;
var node,line,tx;
for (var i=0;i<graphdata.cols.length;i++)
{
for (var j=0;j<graphdata.rows.length;j++)
{
actual = getStatistic(graphdata.cols[i],graphdata.rows[j],stat);
percentage = 100 - ((actual/max) * 100);
margintop = ((percentage/100 * (0.8*500)) -5);
node = document.getElementById("node_" + i + "_" +j);
node.style.marginTop = margintop + "px";
node.innerHTML = actual;
if (j<graphdata.rows.length-1)
{
nextactual = getStatistic(graphdata.cols[i],graphdata.rows[j+1],stat);
nextpercentage = 100 - ((nextactual/max) * 100);
nextmargintop = ((nextpercentage/100 * (0.8*500)) -5);
tx = getLineTransform((margintop-nextmargintop),200);
}
}
}
displayScale(max);
}
function getLineTransform(o,a)
{
}
For the
getLineTransform() function, we first declare the variables
angle and
hypotenuse. These will be returned in an object.
function getLineTransform(o,a)
{
var angle, hypotenuse;
return {"rotate":angle,"width":hypotenuse+100};
}
If
o is zero, this means that the next node has the same value as the previous node. This in turn means that the line does not need to be rotated, and the length of the line is the same as
a.
function getLineTransform(o,a)
{
var angle, hypotenuse;
if (o==0)
{
hypotenuse = a;
angle = 0;
}
else
{
}
return {"rotate":angle,"width":hypotenuse+100};
}
Otherwise, derive
hypotenuse by using the
hypot() method of the
Math object. It will give you the square root of the sum of all arguments squared. Remember to use the absolute value of
o instead.
function getLineTransform(o,a)
{
var angle, hypotenuse;
if (o==0)
{
hypotenuse = a;
angle = 0;
}
else
{
hypotenuse = Math.hypot(Math.abs(o),a).toFixed(0);
}
return {"rotate":angle,"width":hypotenuse+100};
}
Then derive
angle. The tangent of
angle is
o divided by
a, so you can derive
angle by getting the arctangent of the result of dividing
o by
a. Again, remember to use the absolute value of
o. The result is in
radians, and we need to convert it to degrees using the
getDegrees() function.
function getLineTransform(o,a)
{
var angle, hypotenuse;
if (o==0)
{
hypotenuse = a;
angle = 0;
}
else
{
hypotenuse = Math.hypot(Math.abs(o),a).toFixed(0);
angle = getDegrees(Math.atan((Math.abs(o)/a))).toFixed(1);
}
return {"rotate":angle,"width":hypotenuse+100};
}
function getDegrees(radians) {
return radians * 180 / Math.PI;
};
Now, if
o is greater than 0, it means that the value of the next node is higher and therefore the line needs to be rotated counter-clockwise rather than clockwise. So make sure the value is negative.
function getLineTransform(o,a)
{
var angle, hypotenuse;
if (o==0)
{
hypotenuse = a;
angle = 0;
}
else
{
hypotenuse = Math.hypot(Math.abs(o),a).toFixed(0);
angle = getDegrees(Math.atan((Math.abs(o)/a))).toFixed(1);
if (o>0)
{
angle = angle * -1;
}
}
return {"rotate":angle,"width":hypotenuse+100};
}
function getDegrees(radians) {
return radians * 180 / Math.PI;
};
Back to the
displayData() function, we access the element that corresponds to the id derived from
i and
j, assign it to the variable
line, then set the rotation and length of line using the object
tx.
function displayData()
{
var stat = document.getElementById("ddlStat").value;
var max = getMaxStatistic(stat);
var percentage,nextpercentage,actual,nextactual,margintop,nextmargintop;
var node,line,tx;
for (var i=0;i<graphdata.cols.length;i++)
{
for (var j=0;j<graphdata.rows.length;j++)
{
actual = getStatistic(graphdata.cols[i],graphdata.rows[j],stat);
percentage = 100 - ((actual/max) * 100);
margintop = ((percentage/100 * (0.8*500)) -5);
node = document.getElementById("node_" + i + "_" +j);
node.style.marginTop = margintop + "px";
node.innerHTML = actual;
if (j<graphdata.rows.length-1)
{
nextactual = getStatistic(graphdata.cols[i],graphdata.rows[j+1],stat);
nextpercentage = 100 - ((nextactual/max) * 100);
nextmargintop = ((nextpercentage/100 * (0.8*500)) -5);
tx = getLineTransform((margintop-nextmargintop),200);
line = document.getElementById("line_" + i + "_" + j);
line.style.width = tx.width + "px";
line.style.WebkitTransform = "rotate(" + tx.rotate + "deg)";
line.style.transform = "rotate(" + tx.rotate + "deg)";
}
}
}
displayScale(max);
}
And there you are!
Change this back,
.col_container
{
height:100%;
width:95%;
float:left;
margin-right:-100%;
}
Taking shape now...
And of course, do this...
div {outline:0px solid #FFAA00;}
Here's your line graph! Try changing the value in the drop-down list. Do the lines and nodes shift?
There was even more math than the last one, and we even had to pull out good ol' Pythagoras. It's a hoot though - we got the job done! There's plenty of room for improvement, and plenty of variations on this idea, so have at it.
Wasn't that easy? There's node-thing to it,
T___T