I have 100 items entering the viewport in random order. Together, they need to form a circle inside a DOM container. I need some way to calculate the position the items need to move to...
The structure is kinda like this:
http://codepen.io/anon/pen/WvbKjb (visual sample with a bit of css and js inside)
<div id="circle"><!-- 100 items in here --></div</div>
And then the JS, for this sample, would generate 100 divs and set their position with css:
for (i = 0; i <= 100; i++) {
var item = document.createElement('div');
item.id = 'item'+i;
item.className = 'item';
item.setAttribute('style', 'left:0px;top:0px');
document.getElementById('circle').appendChild(item);
}
So I would generate 100 .item elements and move them around the screen. This movement is not an issue: what I don't know how to do is find the position each item has to end up at to properly fill the circle. Is there any way to easily calculate this with a formula? I'm afraid it's way beyond my math skills...
Thanks in advance for any help.
EDIT: using jQuery would be fine too.
You may start with this, just a bit of a math:
Example
the main part is:
var spacing = 360 / count;
var l = Math.cos(rotation * Math.PI / 180) * radius - itemRadius;
var t = Math.sin(rotation * Math.PI / 180) * radius - itemRadius;
rotation += spacing;
Where spacing is actually an angle
Probably a little overloaded but this is what i tried and what worked for me: https://jsfiddle.net/tx7po9eg/1/
Main Part is this function which will calculate the position of a specific element depending on the defined center and the radius.
function getPos(cent, rad, amount, iteration) {
var degree = 360 / amount * iteration;
var changeY = cent.y - (Math.sin(degree * Math.PI / 180) * rad);
var changeX = cent.x - (Math.cos(degree * Math.PI / 180) * rad);
var ret = {};
ret.x = Math.round(changeX * 100) / 100;
ret.y = Math.round(changeY * 100) / 100;
return ret;
}
here an example including visualization:
https://jsfiddle.net/tx7po9eg/3/
You will have to decide exactly HOW you want to fit all the elements inside the circle. Do you want them in a single line around the parameter? At Random? Fill the circle?
If you want to fill the circle, one thing you could do is work like that - move from the center outside, starting by putting an item in the middle, then going out and putting another circle around it, then another around it, and like that keep wrapping it in layers until you hit the end of the circle. The only thing to figure out here would be the spacing (e.g. how many circles in each layer), and that would depend on the size of each element (are the sizes fixed)?
If you can make the container circle as large as you desire, you can just start making layers until you run out of items, and then place the big circle around them.
Hope this helps...
Related
I am trying to make a game board with p5.js. I want the circular shape of the board to have 60 tiles in total, which means the rotation should be 6 degrees (6*60 = 360 degrees), if I want 60 tiles in a circular shape.
I cannot get this to work, even though I think I'm doing it right. This is essentially what I want to do:
If I try with rotate(6) and no translate (because that apparently completely doesn't work), I get this result:
Check out my code at https://jsfiddle.net/mortenmoulder/ze6fn3av/ (you might want to resize the window and hit run again) and here:
angleMode(DEGREES);
for (let i = 0; i <= 60; i++) {
//translate(width / 2, height / 2);
rotate(6);
rect(centerW + 500, centerH, 100, 50);
}
What am I doing wrong?
Preface: I have never used p5.js before.
anyway, i tweaked your code and got pretty close:
var tileInnerWidth = 52;
var tileOuterWidth = 57
var tileHeight = 50;
translate(width / 2, height / 2);
for (let i = 0; i <= 60; i++) {
quad(-tileOuterWidth/2, centerH, tileOuterWidth/2, centerH, tileInnerWidth/2, centerH - tileHeight,-tileInnerWidth/2,centerH - tileHeight);
rotate(6);
}
Here's a fiddle: https://jsfiddle.net/mht3o21p/2/
To explain what i'm doing:
There's a single translate to move the origin to the middle of the screen, so that rotations can be relative to that
I draw the trapezoid which goes at the very bottom of the circle. It needs to be centered horizontally, so its x coordinates have division by 2. The sizes were picked by trial and error and what looked good.
I rotate 6 degrees and repeat.
Using three.js, I'm creating a game with cars that move in a specific direction, depending on their y-rotation. An example would be 90 degrees. I use object.translateZ() to move them forward but I've run into a problem.
I'm using physijs to simulate the cars. Cars that run into each other may change their y-rotation (because of the crash) and I want to find a way for the cars to slowly change their rotation back to the original rotation like they are turning to get back on the road. Without this my city is very chaotic.
Here's the code that I'm already using (this is just part of it):
var targetRotation = 90
var rotation = car.mesh.rotation.y * 180 / Math.PI //to convert to degrees
I want to find a way to slowly change the car's rotation so it's the same as the target rotation.
Any help is appreciated! (but some sort of function would be perfect)
I've done stuff like this before in other systems (2D, not Three.js), and essentially all you want to do is gradually increment the angle until you reach something close enough to the target angle. Usually this means the float is less than the turning speed you're incrementing by (so you don't "overshoot" the value).
The amount of the increment depends on how quickly you want them to turn.
You also want to check if it's better to increase the angle or decrease (do you turn clockwise or counterclockwise) depending on if you're closer to 360 or 0. This prevents the car from turning the "long way" around. You can find this out by seeing if the difference is greater/less than 180.
We can use the modulus operator to get the "real" angle between -360/360.
var currentAngle = car.mesh.rotation.y * 180 / Math.PI; //car's angle in degrees
var targetAngle = 90; //preferred angle *this time* ;)
var turningSpeed = 1; //one degree per update (wayyy to high for real stuff, usually)
currentAngle = currentAngle % 360; //get the 0-360 remainder
if ( Math.abs(targetAngle - currentAngle) >= turningSpeed) {
var addto = 0
if (targetAngle - currentAngle < 0) {
addto = 360
}
if ( targetAngle - currentAngle + addto <= 180 ) {
currentAngle += turningSpeed;
}
else {
currentAngle -= turningSpeed;
}
}
else { currentAngle = targetAngle; } //we're close enough to just set it
car.mesh.rotation.y = ( currentAngle * Math.PI ) / 180; // back to radians!
i've been searching for this the last couple of weeks and i can't find an answer.
This is what i'm trying to accomplish ! Click to see the image.
I need to scroll through an infinite number of apps, on a touchscreen device.
The one in the middle needs to be bigger, and the rest smaller and smaller, to give the idea that there are more apps that are not visible and you need to touch and scroll though apps.
I'm trying to do this with Jquery, HTML5 and CSS3.
Can anybody help me, please?
I'm really stuck here..
Thank you very much!
I tried using this http://jsfiddle.net/67zMe/5/
But i had no luck making it semi-circular and based on touch screen scrolling action.
var radius = 300;
var angleSteps = 360 / $('#circle-list li').length;
var baseAngle = 0;
function updateListPositions()
{
$('#circle-list li').each(function(index, element)
{
var angle = baseAngle + (index * angleSteps);
var center = 150;
var distance = 100;
var x = distance * Math.cos(angle * (Math.PI / 180));
var y = distance * Math.sin(angle * (Math.PI / 180));
$(element).css({left:center+x, top:center+y});
});
}
var stepInterval = setInterval(stepAngle, 25);
function stepAngle()
{
baseAngle++;
updateListPositions();
}
Here is the javascript. It's the closest thing i found, to what i need.
The guy who posted this, had the same problem, and didn't find the answer he needed.
this is my first question after having relied on this site for years!
Anyway, I'd like to accomplish something similar to this effect:
http://www.flashmonkey.co.uk/html5/wave-physics/
But on a circular path, instead of a horizon. Essentially, a floating circle/blob in the center of the screen that would react to mouse interaction. What I'm not looking for is gravity, or for the circle to bounce around the screen - only surface ripples.
If at all possible I'd like to apply a static texture to the shape, is this a possibility? I'm completely new to Canvas!
I've already tried replacing some code from the above example with circular code from the following link, to very limited success:
http://www.html5canvastutorials.com/tutorials/html5-canvas-circles/
If only it were that easy :)
Any ideas?
Thanks in advance!
I tried to figure out how wave simulation works using View Source and JavaScript console. It's working fine but threw some JS errors. Also, it seems physics update is entangled with rendering in the render() method.
Here is what I found about the code:
The mouseMove() method creates disturbances on the wave based on mouse position, creating a peak around the mouse. The target variable is the index of the particle that needs to be updated, it's calculated from mouse pos.
if (particle && mouseY > particle.y) {
var speed = mouseY - storeY;
particles[target - 2].vy = speed / 6;
particles[target - 1].vy = speed / 5;
particles[target].vy = speed / 3;
particles[target + 1].vy = speed / 5;
particles[target + 2].vy = speed / 6;
storeY = mouseY;
}
Then, the particles around target are updated. The problem I found is that it does no bounds checking, i.e. it can potentially particles[-1] when target == 0. If that happens, an exception is thrown, the method call ends, but the code does not stop.
The render() method first updates the particle positions, then renders the wave.
Here is its physics code:
for (var u = particles.length - 1; u >= 0; --u) {
var fExtensionY = 0;
var fForceY = 0;
if (u > 0) {
fExtensionY = particles[u - 1].y - particles[u].y - springs[u - 1].iLengthY;
fForceY += -fK * fExtensionY;
}
if (u < particles.length - 1) {
fExtensionY = particles[u].y - particles[u + 1].y - springs[u].iLengthY;
fForceY += fK * fExtensionY;
}
fExtensionY = particles[u].y - particles[u].origY;
fForceY += fK / 15 * fExtensionY;
particles[u].ay = -fForceY / particles[u].mass;
particles[u].vy += particles[u].ay;
particles[u].ypos += particles[u].vy;
particles[u].vy /= 1.04;
}
Basically, it's Hooke's Law for a chain of particles linked by springs between them. For each particle u, it adds the attraction to the previous and next particles (the if statements check if they are available), to the variable fForceY. I don't fully understand the purpose of the springs array.
In the last four lines, it calculates the acceleration (force / mass), updates the velocity (add acceleration), then position (add velocity), and finally, reduce velocity by 1.04 (friction).
After the physics update, the code renders the wave:
context.clearRect(0, 0, stageWidth, stageHeight);
context.fillStyle = color;
context.beginPath();
for (u = 0; u < particles.length; u++) {
...
}
...
context.closePath();
context.fill();
I'm not explaining that, you need to read a canvas tutorial to understand it.
Here are some ideas to get started, note that I didn't test these code.
To modify the code to draw a circular wave, we need introduce a polar coordinate system, where the particle's x-position is the angle in the circle and y-position the distance from center. We should use theta and r here but it requires a large amount of refactoring. We will talk about transforming later.
mouseMove(): Compute particle index from mouse position on screen to polar coordinates, and make sure the disturbance wrap around:
Define the function (outside mouseMove(), we need this again later)
function wrapAround(i, a) { return (i + a.length) % a.length; }
Then change
particles[target - 2] --> particles[wrapAround(target - 2, particles)]
particles[target - 1] --> particles[wrapAround(target - 1, particles)]
...
The modulo operator does the job but I added particles.length so I don't modulo a negative number.
render(): Make sure the force calculation wrap around, so we need to wrapAround function again. We can strip away the two if statements:
fExtensionY = particles[wrapAround(u - 1, particles)].y - particles[u].y - springs[wrapAround(u - 1, springs)].iLengthY;
fForceY += -fK * fExtensionY;
fExtensionY = particles[u].y - particles[wrapAround(u + 1, particles)].y - springs[warpAround(u, springs)].iLengthY;
fForceY += fK * fExtensionY;
Here is the result so far in jsfiddle: Notice the wave propagate from the other side. http://jsfiddle.net/DM68M/
After that's done, the hardest part is rendering them on a circle. To do that, we need coordinate transform functions that treat particle's (x, y) as (angle in the circle, distance from center), and we also need inverse transforms for mouse interaction in mouseMove().
function particleCoordsToScreenCoords(particleX, particleY) {
return [ radiusFactor * particleY * Math.cos(particleX / angleFactor),
radiusFactor * particleY * Math.sin(particleX / angleFactor) ];
}
function screenCoordsToParticleCoords(screenX, screenY) {
// something involving Math.atan2 and Math.sqrt
}
Where the ...Factor variables needed to be determined separately. The angleFactor is two pi over the highest x-position found among particles array
Then, in the coordinates supplied to the context.lineTo, context.arc, use the particleCoordsToScreenCoords to transform the coordinates.
I'm sorry to say that Math really isn't my strong suit. Normally I can get by, but this has got me totally stumped.
I'm trying to code up a quiz results screen in HTML/CSS/Javascript.
On my interface, I have a semicircle (the right hemisphere of a target).
I have a range of 'scores' (integers out of 100 - so 50, 80, 90 etc.).
I need to plot these points on the semicircle to be n% away from the centre, where n is the value of each score - the higher the score, the closer to the centre of the target the point will appear.
I know how wide my semicircle is, and have already handled the conversion of the % values so that the higher ones appear closer to the centre while the lower ones appear further out.
What I can't wrap my head around is plotting these points on a line that travels out from the centre point (x = 0, y = target height/2) of the target at a random angle (so the points don't overlap).
Any suggestions are gratefully received!
Do you have an example of what you want this to look like? It sounds like you want to divide up the circle into N slices where N is the number of points you need to display, then plot the points along each of those radii. So you might have something like:
Edit: code was rotating about the origin, not the circle specified
var scores = [];
//...
//assume scores is an array of distances from the center of the circle
var points = [];
var interval = 2 * Math.PI / N;
var angle;
for (var i = 0; i < N; i++) {
angle = interval * i;
//assume (cx, cy) are the coordinates of the center of your circle
points.push({
x: scores[i] * Math.cos(angle) + cx,
y: scores[i] * Math.sin(angle) + cy
});
}
Then you can plot points however you see fit.
After much headscratching, I managed to arrive at this solution (with the help of a colleague who's much, much better at this kind of thing than me):
(arr_result is an array containing IDs and scores - scores are percentages of 100)
for (var i = 0; i < arr_result.length; i++){
var angle = angleArray[i]; // this is an array of angles (randomised) - points around the edge of the semicircle
var radius = 150; // width of the semicircle
var deadZone = 25 // to make matters complicated, the circle has a 'dead zone' in the centre which we want to discount
var maxScore = 100
var score = parseInt(arr_result[i]['score'], 10)
var alpha = angle * Math.PI
var distance = (maxScore-score)/maxScore*(radius-deadZone) + deadZone
var x = distance * Math.sin(alpha)
var y = radius + distance * Math.cos(alpha)
$('#marker_' + arr_result[i]['id'], templateCode).css({ // target a specific marker and move it using jQuery
'left' : pointX,
'top': pointY
});
}
I've omitted the code for generating the array of angles and randomising that array - that's only needed for presentational purposes so the markers don't overlap.
I also do some weird things with the co-ordinates before I move the markers (again, this has been omitted) as I want the point to be at the bottom-centre of the marker rather than the top-left.