Zhyohzhy Zhyohzhy - 3 months ago 5
jQuery Question

Dynamically add new elements in function

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>




PS: I was also going to program that each added planet would be positioned a bit farther from the central point (the sun) than the previously added planet.

Answer

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);
  });
}

Example using your blue and red then adding a yellow; I've left the part wiring up the button to call makeBall with new values to you:

var objectX = "black";

var ball = {};

makeBall({
  element: {id: "blue", class: "st1", cx: "375.4", cy: "289.7", r: "10"},
  animation: {speed: 1.2, spin: 100, side: 0.0}
});
makeBall({
  element: {id: "red", class: "st2", cx: "375.4", cy: "289.7", r: "10"},
  animation: {speed: 1.2, spin: 200, side: 0.0}
});
makeBall({
  element: {id: "yellow", class: "st3", cx: "375.4", cy: "289.7", r: "10"},
  animation: {speed: 1.2, spin: 300, side: 0.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) {
    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() {
  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);
}

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;
}
<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>
<button type="button" onclick="addObject()">
  button
</button>


Side note: You might look at using requestAnimationFrame rather than setInterval.