Reputation: 852
I’ve programmed a function that rotates objects around a main object, like how planets rotate around the sun.
I'm trying to dynamically add new planets in my little solar system by a simple click of a button. They are all SVG elements. I'm having trouble working out how I can dynamically generate new elements that rotate around the sun with my rotation(coorX, coorY, object)
function. They would all need to be dynamically named and dynamically positioned, this is too hard on me.
What should my code need to look like in order for me to achieve this? Thank you in advance for any help/tips.
Here's my code:
var objectX = "black";
function addObject(){
objectX = "blue";
}
function rotation(coorX, coorY, object) {
object.side += (1.0 / object.speed);
var ang = object.side * 2.0 * Math.PI / 180.0;
var r = object.spin;
return {
x: Math.cos(ang) * r - Math.sin(ang) * r + coorX,
y: Math.sin(ang) * r + Math.cos(ang) * r + coorY
};
}
function rotationball ( circle ) {
var x, y, x_black, y_black, e, newpos;
e = document.getElementById ( circle );
x_black = parseFloat ( document.getElementById ( objectX ).getAttribute ( "cx" ) );
y_black = parseFloat ( document.getElementById ( objectX ).getAttribute ( "cy" ) );
newpos = rotation( x_black, y_black, ball[circle] );
e.setAttribute ( "cx", newpos.x );
e.setAttribute ( "cy", newpos.y );
}
var ball = {
blue: {speed: 1.2, spin: 100, side: 0.0} ,
red: {speed: 1.2, spin: 200, side: 0.0}
};
function animate () {
rotationball("blue");
rotationball("red");
}
var animateInterval = setInterval(animate, 1000 / 60);
.st0{fill:black;}
.st1{fill:blue;}
.st2{fill:red;}
<div class="spinning"> <svg xmlns="http://www.w3.org/2000/svg" id="solly" viewBox="0 0 1000 600"><g id="Sun2">
<circle id="black" class="st0" cx="500" cy="300.8" r="10"/>
<circle id="blue" class="st1" cx="375.4" cy="289.7" r="10"/>
<circle id="red" class="st2" cx="375.4" cy="289.7" r="10"/>
</div>
<button type="button" onclick="addObject()">
button
</button>
Upvotes: 4
Views: 93
Reputation: 1074008
You're very much on the right track with your ball
object (though I'd call it balls
). To dynamically create a circle
element you do this:
var circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
Then you set its attributes (circle.setAttribute
) and append it to the id="Sun2"
element.
Here's a makeBall
function that does that on the basis of a specification object you pass in which has two subobjects: element
, which defines the element, and animation
which defines the animation:
function makeBall(spec) {
// Create the element
var circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
// Set its various attributes
["id", "cx", "cy", "r", "class"].forEach(function(attrName) {
circle.setAttribute(attrName, spec.element[attrName]);
});
// Add it to the sun
document.getElementById("Sun2").appendChild(circle);
// Remember its animation settings in `ball`
ball[spec.element.id] = spec.animation;
}
Then you use it like this:
makeBall({
element: {id: "blue", class: "st1", cx: "375.4", cy: "289.7", r: "10"},
animation: {speed: 1.2, spin: 100, side: 0.0}
});
The last piece is to replace animate with something that works dynamically with the properties inside ball
:
function animate() {
Object.keys(ball).forEach(function(id) {
rotationball(id);
});
}
Here's an example removing blue
and red
from the markup and adding them dynamically when we start. I've also gotten rid of objectX
and make the rotation always be relative to black
, and hooked up some fields to use when adding a new ball:
var ball = {};
makeBall({
element: {id: "blue", class: "st1", r: "10"},
animation: {speed: 1.2, spin: 20, side: 0.0}
});
makeBall({
element: {id: "red", class: "st2", r: "10"},
animation: {speed: 1.2, spin: 40, side: 120.0}
});
makeBall({
element: {id: "yellow", class: "st3", r: "10"},
animation: {speed: 1.2, spin: 60, side: 240.0}
});
function makeBall(spec) {
// Create the element
var circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
// Set its various attributes
["id", "cx", "cy", "r", "class"].forEach(function(attrName) {
if (spec.element[attrName]) {
circle.setAttribute(attrName, spec.element[attrName]);
}
});
// Add it to the sun
document.getElementById("Sun2").appendChild(circle);
// Remember its animation settings in `ball`
ball[spec.element.id] = spec.animation;
}
function addObject() {
// Create a spec to use with makeBall from the fields
var spec = {
element: {
id: document.getElementById("new-id").value,
class: document.getElementById("new-class").value,
r: parseFloat(document.getElementById("new-r").value)
},
animation: {
speed: parseFloat(document.getElementById("new-speed").value),
spin: parseFloat(document.getElementById("new-spin").value),
side: parseFloat(document.getElementById("new-side").value)
}
};
// Some minimal validation
if (!spec.element.id || !spec.element.r || !spec.animation.speed || !spec.animation.spin || isNaN(spec.animation.side)) {
alert("Need all values to add a ball");
} else if (ball[spec.element.id]) {
alert("There is already a ball '" + spec.element.id + "'");
} else {
// Do it!
makeBall(spec);
}
}
function rotation(coorX, coorY, object) {
object.side += (1.0 / object.speed);
var ang = object.side * 2.0 * Math.PI / 180.0;
var r = object.spin;
return {
x: Math.cos(ang) * r - Math.sin(ang) * r + coorX,
y: Math.sin(ang) * r + Math.cos(ang) * r + coorY
};
}
function rotationball(circle) {
var x, y, x_black, y_black, e, newpos, black;
// We always rotate around black
black = document.getElementById("black");
// Get this circle and update its position
e = document.getElementById(circle);
x_black = parseFloat(black.getAttribute("cx"));
y_black = parseFloat(black.getAttribute("cy"));
newpos = rotation(x_black, y_black, ball[circle]);
e.setAttribute("cx", newpos.x);
e.setAttribute("cy", newpos.y);
}
function animate() {
Object.keys(ball).forEach(function(id) {
rotationball(id);
});
}
var animateInterval = setInterval(animate, 1000 / 60);
.st0 {
fill: black;
}
.st1 {
fill: blue;
}
.st2 {
fill: red;
}
.st3 {
fill: yellow;
}
.st4 {
fill: orange;
}
<div>Add ball:
<label>
ID: <input type="text" id="new-id" value="newball">
</label>
<label>
R: <input type="text" id="new-r" value="10">
</label>
<label>
Speed: <input type="text" id="new-speed" value="1.2">
</label>
<label>
Spin: <input type="text" id="new-spin" value="80">
</label>
<label>
Side: <input type="text" id="new-side" value="0.0">
</label>
<label>
Class: <input type="text" id="new-class" value="st4">
</label>
<button type="button" onclick="addObject()">
Make Ball
</button>
</div>
<div class="spinning">
<svg xmlns="http://www.w3.org/2000/svg" id="solly" viewBox="0 0 1000 600">
<g id="Sun2">
<circle id="black" class="st0" cx="500" cy="300.8" r="10" />
</g>
</svg>
</div>
Side note: You might look at using requestAnimationFrame
rather than setInterval
.
Side note 2: As far as I can tell, what you've called spin
is actually the distance from the center (e.g., the radius of the circle each ball will describe around the black ball).
Upvotes: 5