putting it all together

[edit – by popular request, the code is posted here – just click ‘run’]

Caveat – this is by no means the “best” solution, it’s just my solution.  I’m sharing it to demonstrate my learning process so that others can learn from how I learned, not necessarily learn from my code.  Next steps: (1) create a fake bar chart & (2) then a histogram w/ generated data.

———————————

With many steps of iteration & lots of walks to the kitchen to get water, tea, grapes, or just to walk around, I’ve got spinning stars & polygons!  It’s like the ones below, but imagine that they’re rotating at different speeds and in both directions.

Screenshot from 2013-07-23 15:08:02

Note that there was lots of going back & forth to get to even this imperfect place.  I kept going back to parts I’d done before to see how I could make them flow better or use functions/loops to avoid redundancy.

One challenge was inserting a pause in order to slow down the rotations from nearly instantaneous to something visible.  All stars used the same pause time, but each rotated a different distance during each pause creating the sense of different speeds of rotation.  Using the same pause time created some limitations on the range of rotation speeds that looked ok (not jumpy).  I’m still a little unclear on why the ordering around the timeout & for-loops has to be exactly this way, since I solved it by ‘trial & error.’

I’ve also since learned that instead of setTimeout there is a new API called ‘requestAnimationFrame’ which is better for creating animations like this.  In a future version, I would also want to change the child element of the SVG rather than removing & creating it again.  But I’m not sure how possible that is given that I need to change the star parameters & it’s the new star, not the star parameters, which is the child of SVG.

For those curious, the problem with setTimeout is:

“that it doesn’t synchronize at all with how often the display is refreshed.  Normal displays are refreshed 60 times per second. That means that you should update about every 16.67 milliseconds. But in your code you update every 20 milliseconds. That means that every so often there will be two screen updates without the display updating. Also, you will end up updating the display even when the tab is not visible, which is a waste of battery.” (thanks Jonas!)

———- html ————-

<html>
<head>
<title>SVG Circles</title>

<script src=”messingaround6.js”></script>
<style>
#svgContainer {
width:700px;
height:500px;
background-color:#EEEEEE;
}
</style>

</head>
<body>
<div id=”svgContainer”></div>
</body>
</html>

——— js ———–

var width = “100%”
var height = “100%”

/* create different types of stars & polygons
sides is # of sides for a polygon, half the number of sides for a star
cx and cy are coordinates of the center point in px
a is a scaler for the size of the interior shape, in px
scaleFactorPercent is a percent times a “perfect” star for the point lengths, default is 1
— if scaleFactorPercent = 0 then it’s a polygon rather than a star
rotate Factor is angular rotation, default is 0
color is color
rotateSpeed: fraction of a circle to rotate every 20ms, smaller rotates faster
*/

var star1 = { sides: 6, cx: 280,
cy: 130, a: 5, scaleFactorPercent:1,
rotateFactor:2 * Math.PI /20, color: “blue”, rotateSpeed: 150};

var star2 = { sides: 5, cx: 410, cy: 10,
a: 50, scaleFactorPercent:1.2,
rotateFactor: 0, color: “white”, rotateSpeed: -700};

var star3 = { sides: 5, cx: 230, cy: 380,
a: 40, scaleFactorPercent:0.7, rotateFactor:0,
color: “pink”, rotateSpeed: 250};

var star4 = { sides: 5, cx: 560, cy: 257,
a: 30, scaleFactorPercent:1.3, rotateFactor:0 ,
color: “orange”, rotateSpeed: 375};

var star5 = { sides: 3, cx: 101, cy: 440, a: 20,
scaleFactorPercent:0, rotateFactor:0,
color: “yellow”, rotateSpeed: -300};

var star6 = { sides: 5, cx: 10, cy: 120, a: 30,
scaleFactorPercent:0, rotateFactor:0,
color: “green”, rotateSpeed: 200};

var stars = [star1, star2, star3, star4, star5, star6];
window.onload = function() {

// create container for SVG
var container = document.getElementById(“svgContainer”);
var mySvg = document.createElementNS(“http://www.w3.org/2000/svg&#8221;, “svg”);
mySvg.setAttribute(“version”, “1.2”);
mySvg.setAttribute(“baseProfile”, “tiny”);
//why does Firefox require me to set attribute rather than get from style?
mySvg.setAttribute(“width”,width);
mySvg.setAttribute(“height”,height);
container.appendChild(mySvg);

// set background
var backgroundrect = document.createElementNS(“http://www.w3.org/2000/svg&#8221;, “rect”);
backgroundrect.setAttribute(“width”, “100%”);
backgroundrect.setAttribute(“height”, “100%”);
backgroundrect.setAttribute(“fill”, “lightsteelblue”);
mySvg.appendChild(backgroundrect);

// populate initial stars
for(var i = 0; i < stars.length; i++){
var newStar = new starCoord(stars[i]);
var pentagon1 = newStar.createStar();
mySvg.appendChild(pentagon1);
}

// and create rotation using duration
function createAndDelete() {
for(var i = 0; i < stars.length; i++){
mySvg.removeChild(mySvg.lastChild);
stars[i].rotateFactor = stars[i].rotateFactor + 2 * Math.PI / stars[i].rotateSpeed;
}
console.log(“hi”);

setTimeout(createAndDelete, 20);

for(var i = 0; i < stars.length; i++){
var newStar = new starCoord(stars[i]);
var pentagon1 = newStar.createStar();
mySvg.appendChild(pentagon1);
}

}

// run the function to spin
createAndDelete();
}
// function to create star
var starCoord = function(starDef){
this.starDef = starDef;
}

// and the prototype for the functions
starCoord.prototype.createStar = function(){
var rotate = this.starDef.rotateFactor;
var theta = 2 * Math.PI / this.starDef.sides;
var k = theta / 2
var z = [];
var toPoint = this.starDef.a * Math.cos(k) +
this.starDef.scaleFactorPercent * Math.sin(k) *
Math.tan(2 * k) * this.starDef.a;

// do the math to define distance from center
for(var i = 1; i <= this.starDef.sides; i++){
var angle = theta * i + rotate;
var x = this.starDef.cx + this.starDef.a * Math.cos(angle);
var y = this.starDef.cy + this.starDef.a * Math.sin(angle);
var xPoint = this.starDef.cx + toPoint * Math.cos(angle + k);
var yPoint = this.starDef.cy + toPoint * Math.sin(angle + k);
z.push(x);
z.push(y);
z.push(xPoint);
z.push(yPoint);
}

var pentagon = document.createElementNS(“http://www.w3.org/2000/svg&#8221;, “polygon”);
pentagon.setAttribute(“points”, z.join(” “));
pentagon.setAttribute(“stroke”, “0”);
pentagon.setAttribute(“fill”, this.starDef.color);
return(pentagon);
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s