I'm trying to make big circle and move divs along the circle's circumference.
Each div must change the content inside the big circle.
The number of div(s) must be dependent on how many are fetched from database (from table category).
I tried to do this and modified the code by putting .eq() but the problem with .eq is that next circle will appear after that circle, all put in the same place. I want them all to appear at the same time like this without repeating functions
Updated your fiddle:
http://jsfiddle.net/wyW2D/1/
Used:
var t = -1.6;
var t2 = -1.6;
var x = 0;
var t = [-1.6, -1.6, -1.6], // starting angle in radians for each circle
delta = [0.05, 0.03, 0.02], // change in radians for each circle
// e.g the first moves fastest, the last
// slowest. if this gets too big, the
// movement won't look circular, since the
// animation is really a bunch of straight lines
finish = [1.4, 1.0, 0.6]; // the stopping point in radians for each
// circle. if the circle size changes, this
// may need to change
function moveit(i) {
t[i] += delta[i]; // move the angle forward by delta
var r = 300; // radius (the .inner div is 600 x 600)
var xcenter = -30; // center X position: this reproduces the .inner horizontal
// center but from the body element
var ycenter = 420; // center Y position: same here but vertical
// Basic trig, these use sin/cos to find the vert and horiz offset for a given
// angle from the center (t[i]) and a given radius (r)
var newLeft = Math.floor(xcenter + (r * Math.cos(t[i])));
var newTop = Math.floor(ycenter + (r * Math.sin(t[i])));
// Now animate to the new top and left, over 1ms, and when complete call
// the move function again if t[i] hasn't reached the finish.
$('div.circle'+(i+1)).animate({
top: newTop,
left: newLeft,
},1, function() {
if (t[i] < finish[i]) moveit(i);
});
// You can slow down the animation by increasing the 1, but that will eventually
// make it choppy. This plays opposite the delta.
}
// Start the ball rolling
$("document").ready(function(e) {
moveit(0);
moveit(1);
moveit(2);
});
This was a quick change to reduce the code to one function that used arrays (t, delta, finish) to keep track of the three circles. It could be improved to accept arbitrary circles, of any size, at any starting / ending angle.
Also, this kind of animation is much easier with CSS. It is simple to specify and has much better performance.
Related
There is endlessly moving sprite "green block" from top to bottom and it works. Is it possible to show sprite moving like "around" the stage show at the top as much as hide in bottom. I don't know exactly how this effect can be called, but I mean when green block is starting to move down the scene border, then start showing it again at the top. How can it be done and can you, please, show how to do this?
const WIDTH = 500;
const HEIGHT = 500;
const app = new PIXI.Application({
width: WIDTH,
height: HEIGHT,
backgroundColor: 0x000000
});
document.body.appendChild(app.view);
const sprite = PIXI.Sprite.from('https://i.ibb.co/b3Sjn6M/greeenblock.png');
sprite.width = 100;
sprite.height = 100;
// Center
sprite.anchor.set(0.5);
sprite.x = app.screen.width / 2;
sprite.y = app.screen.height / 2;
app.stage.addChild(sprite);
// Listen for animate update
app.ticker.add((delta) => {
// Move from topto bottom
sprite.position.y += delta * 2;
if (sprite.position.y > HEIGHT + sprite.height / 2) {
sprite.position.y = -sprite.height / 2;
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/5.3.3/pixi.min.js"></script>
Solution (with flickering) provided by #Blindman67:
const WIDTH = 500;
const HEIGHT = 500;
const app = new PIXI.Application({
width: WIDTH,
height: HEIGHT,
backgroundColor: 0x000000
});
document.body.appendChild(app.view);
const sprite = PIXI.Sprite.from('https://i.ibb.co/b3Sjn6M/greeenblock.png');
const spriteReverse = PIXI.Sprite.from('https://i.ibb.co/b3Sjn6M/greeenblock.png');
sprite.width = 100;
sprite.height = 100;
spriteReverse.width = 100;
spriteReverse.height = 100;
// Center
sprite.anchor.set(0.5);
sprite.x = app.screen.width / 2;
sprite.y = app.screen.height / 2;
spriteReverse.anchor.set(0.5);
spriteReverse.x = app.screen.width / 2;
spriteReverse.y = app.screen.height / 2;
app.stage.addChild(sprite);
app.stage.addChild(spriteReverse);
let y = 0;
// Euqlidian modulo
const modAbs = (value, modulo) => (value % modulo + modulo) % modulo;
// Listen for animate update
app.ticker.add((delta) => {
// Move from topto bottom
y += delta * 2;
if (y > HEIGHT + sprite.height / 2) {
y = -sprite.height / 2;
}
// use modulo to warp
y = modAbs(y, HEIGHT);
// check if sprite overlaps the screen edge
spriteReverse.visible = false;
if (y + sprite.height > HEIGHT) { // is crossing then
spriteReverse.visible = true;
spriteReverse.position.y = (y - HEIGHT) // ... draw a copy at opposite edge.
}
sprite.position.y = y
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/5.3.3/pixi.min.js"></script>
If I understand you: you have one box which you wish to move in an infinite loop from the top to the bottom. Once it hits the bottom it should start showing at the top.
The easiest way I can think of would be to have two identical boxes.
Both starts at the top and only one moves down. Once it hits the bottom the other box can start moving down.
When the first box is completely off-screen you reset it's position.
And repeat.
% Remainder operator
This can be done using the remainder operator %
For example if the screen is 1000 pixels wide and you have a coordinate of 1500, that is the object has warped around the screen 1.5 times, using the remainder operator 1500 % 1000 = 500.
If only moving in a positive direction then this is all that is needed (apart from popping)
x = x % screenWidth;
// and/or for y
y = y % screenHeight;
Negative space
However there is a problem if the object moves in the other direction as the remainder operation keeps the sign of the number -1500 % 1000 === -500, and even worse if you use Math.abs on the result you still get the wrong value Math.abs(-1200 % 1000) === 200 which should be 800
You can fix this using a slightly more complex function. You can add it to the Math object or use a stand alone function as follows.
const modAbs = (value, modulo) => (value % modulo + modulo) % modulo;
With the above function negative values are correctly moved into positive space.
So if you have a coordinate x, y to make it warp the screen with
x = modAbs(x, screenWidth);
y = modAbs(y, screenHeight);
That seams easy, but unfortunately there are still some problems to overcome.
Popping
Using the above function to warp across the screen does not consider the size of the sprite, and because you are rendering only one copy when the sprite is move across the playfield edge it will not appear at the other side until the coordinate crossed the edge.
This causes the sprite to pop in and or out depending on the direction of movement and the position of the sprites origin.
There are two solutions.
Extend the playfield
If you make the playfield larger than the view (Viewable area) by 2 times the size of the sprite and warp using the larger playfield then the sprite will not warp until it has completely disappeared from view. This prevents the ugly popping in and out when warping and is most suited to NPC type sprites. For player (focused) sprites this is not a good options as the sprite will not be completely visible as it crosses the screen edges.
Render extra copies.
To keep the sprite fully visible at all times you need to render it more than once when it is crossing the screen. Example pseudo code
// use modulo to warp
x = modAbs(x, screenWidth);
// check if sprite overlaps the screen edge
if (x + spriteWidth > screenWidth) { // is crossing then
drawSprite(x - screenWidth, // ... draw a copy at opposite edge.
If you are only warping between top and bottom (or left and right) this is all that is needed.
If you are warping in all directions you will need to render the sprite up to 4 times. Twice when crossing top bottom or left right. 4 times if crossing in a corner.
As your question only indicates up and down warps I assume you don't need the extra code.
I want to create a graph with nodes and lines that connect node around the circumference only
I tried this; it didn't work
for (int i = 0; i < nodeX.length; i++) {
fill(0);
ellipse(nodeX[i], nodeY[i], 10, 10);
}
or a pass mark, there must be a display window of size (600, 360) containing a grid with the same number of columns and rows (10 in the video). Your program should work correctly with a different size and/or number of partitions.
You set a constant globally at the VERY TOP, as:
final int N_PARTITIONS = 10;
and use it (N_PARTITIONS) throughout the program. your program should work even if the marker changes the value of N_PARTITIONS to 5 or 8 or 15 or ... (obviously, when N_PARTITIONS changes to 15, there should be 15 rows and 15 columns)
When you click anywhere on the display window, circle of diameter 10 (henceforth called a node) should be displayed at that point. Nodes from previous clicks should stay on the screen and there should be lines that join each node with the next one (and the last is joined to the first node). There should be no other lines besides these lines (and the grid lines of course).
There should be nodes drawn at the three points and lines from (200, 400) to (300, 300); (300, 300) to (60, 100); and (60, 100) to (200, 400).
From how you desribed the expected output, it seems as if the grid lines and the nodes connected by lines are unrelated. Therefore, you have two tasks to accomplish:
Drawing a grid with N_PARTITIONS rows and column lines, and
Drawing and connecting circles of diameter 10 through a mouse-click.
The former is relatively easy. Given
final int N_PARTITIONS = 10;
we need 10 lines across the screen and 10 lines down the screen.
Within void setup(), use size(600, 360); to set the size of the canvas to 600x360 pixels.
Let's handle drawing lines across the screen first. We will use a for loop to accomplish this.
void draw() {
int distVertLine = width / N_PARTITIONS; // This variable defines the distance between each subsequent vertical line.
for(int i = distVertLine / 2; i < width; i += distVertLine) {
line(i, 0, i, height); // Draw a line at x=i starting at the top of the canvas (y=0) and going to the bottom (y=height)
}
}
Above, we initialize a distVertLine variable to define the horizontal distance between each vertical line. Its value is width / N_PARTITIONS because we are splitting up the width of the canvas into a given amount of partitions.
In the for loop, i is initialized as distVertLine / 2 so that the grid lines are in the center.
Similarly, we can do this with the horizontal lines:
int distHorizLine = height / N_PARTITIONS; // This variable defines the distance between each subsequent vertical line.
for(int i = distHorizLine / 2; i < width; i += distHorizLine) {
line(0, i, width, i); // Draw a line at y=i starting at the left of the canvas (x=0) and going to the right (x=width)
}
Now for the nodes and lines. A circle in Processing is given by circle(x, y, extent) (check out the docs here).
When the mouse is pressed, the method void mousePressed() is called. Upon each press, we wish to record the x and y values in an array:
ArrayList nodeX = new ArrayList(); // ArrayLists are nice to use because they're expandable, unlike a Processing array.
ArrayList nodeY = new ArrayList(); // ArrayLists are nice to use because they're expandable, unlike a Processing array.
void mouseClicked() {
nodeX.add(mouseX); // Adds the x-position of the mouse pointer to the nodeX list.
nodeY.add(mouseY); // Adds the y-position of the mouse pointer to the nodeY list.
}
In void draw(), set up a for loop that draws circles at the coordinates in both lists:
stroke(0); // Set the circle's outline as black.
for(int i = 0; i < nodeX.size(); i++) {
float xPos = (int)nodeX.get(i); // Grab the ith x-position from the list.
float yPos = (int)nodeY.get(i); // Grab the ith y-position from the list.
circle(xPos, yPos, 10); // Draw a circle of diameter 10 at (xPos, yPos).
}
(Note, within the first line of void draw() I added
background(255); // Set the background color to white
stroke(125); // Set the stroke color as gray.
to make it easier to see the circles).
Last, we need to connect each circle with a line.
stroke(0, 0, 255); // Make the connecting lines blue.
for(int i = 0; i < nodeX.size() - 1; i++) {
float xPosA = (int)nodeX.get(i); // Grab the ith x-position from the list.
float yPosA = (int)nodeY.get(i); // Grab the ith y-position from the list.
float xPosB = (int)nodeX.get(i+1); // Grab the next x-position from the list.
float yPosB = (int)nodeY.get(i+1); // Grab the next y-position from the list.
line(xPosA, yPosA, xPosB, yPosB);
}
// And draw the connecting line.
if(nodeX.size() > 0) { // The code in this block will crash unless this condition is added to handle an empty list.
int size = nodeX.size() - 1; // Get the index of the last item in nodeX/nodeY.
float xPosA = (int)nodeX.get(0); // Grab the first x-position from the list.
float yPosA = (int)nodeY.get(0); // Grab the first y-position from the list.
float xPosB = (int)nodeX.get(size); // Grab the last x-position from the list.
float yPosB = (int)nodeY.get(size); // Grab the last y-position from the list.
line(xPosA, yPosA, xPosB, yPosB);
}
I am trying to move an object smoothly from point A to point B using HTML canvas and regular javascript.
Point A is a set of coordinates
Point B is in the case the cursor location.
I made a jsfiddle of what I have so far: https://jsfiddle.net/as9fhmw8/
while(projectile.mouseX > projectile.x && projectile.mouseY < projectile.y)
{
ctx.save();
ctx.beginPath();
ctx.translate(projectile.x, projectile.y);
ctx.arc(0,0,5,0,2*Math.PI);
ctx.fillStyle = "blue";
ctx.fill();
ctx.stroke();
ctx.restore();
if(projectile.mouseX > projectile.x && projectile.mouseY < projectile.y)
{
var stepsize = (projectile.mouseX - projectile.x) / (projectile.y - projectile.mouseY);
projectile.x += (stepsize + 1);
}
if(projectile.mouseY < projectile.y)
{
var stepsize = (projectile.y - projectile.mouseY) / (projectile.mouseX - projectile.x);
projectile.y -= (stepsize + 1);
}
}
Essentially what I can't figure out to do is to make the while loop slower (so that it appears animated in stead of just going through every iteration and showing the result).
I also can't figure out how to prevent the Arc from duplicating so that it creates a line that is permanent, instead of appearing to move from point a to point b.
Smooth animation here is really about determining how far to move your object for each iteration of the loop.
There is a little math involved here, but it's not too bad.
Velocity
Velocity in your case is just the speed at which your particles travel in any given direction over a period of time. If you want your particle to travel 200px over the course of 4 seconds, then the velocity would be 50px / second.
With this information, you can easily determine how many pixels to move (animate) a particle given some arbitrary length of time.
pixels = pixelsPerSecond * seconds
This is great to know how many pixels to move, but doesn't translate into individual X and Y coordinates. That's where vectors come in.
Vectors
A vector in mathematics is a measurement of both direction and magnitude. For our purposes, it's like combining our velocity with an angle (47°).
One of the great properties of vectors is it can be broken down into it's individual X and Y components (for 2-Dimensional space).
So if we wanted to move our particle at 50px / second at a 47° angle, we could calculate a vector for that like so:
function Vector(magnitude, angle){
var angleRadians = (angle * Math.PI) / 180;
this.magnitudeX = magnitude * Math.cos(angleRadians);
this.magnitudeY = magnitude * Math.sin(angleRadians);
}
var moveVector = new Vector(50, 47);
The wonderful thing about this is that these values can simply be added to any set of X and Y coordinates to move them based on your velocity calculation.
Mouse Move Vector
Modeling your objects in this way has the added benefit of making things nice and mathematically consistent. The distance between your particle and the mouse is just another vector.
We can back calculate both the distance and angle using a little bit more math. Remember that guy Pythagoras? Turns out he was pretty smart.
function distanceAndAngleBetweenTwoPoints(x1, y1, x2, y2){
var x = x2 - x1,
y = y2 - y1;
return {
// x^2 + y^2 = r^2
distance: Math.sqrt(x * x + y * y),
// convert from radians to degrees
angle: Math.atan2(y, x) * 180 / Math.PI
}
}
var mouseCoords = getMouseCoords();
var data = distanceAndAngleBetweenTwoPoints(particle.x, particle.y, mouse.x, mouse.y);
//Spread movement out over three seconds
var velocity = data.distance / 3;
var toMouseVector = new Vector(velocity, data.angle);
Smoothly Animating
Animating your stuff around the screen in a way that isn't jerky means doing the following:
Run your animation loop as fast as possible
Determine how much time has passed since last time
Move each item based on elapsed time.
Re-paint the screen
For the animation loop, I would use the requestAnimationFrame API instead of setInterval as it will have better overall performance.
Clearing The Screen
Also when you re-paint the screen, just draw a big rectangle over the entire thing in whatever background color you want before re-drawing your items.
ctx.globalCompositeOperation = "source-over";
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
Putting It All Together
Here is a Fiddle demonstrating all these techniques: https://jsfiddle.net/jwcarroll/2r69j1ok/3/
I'm trying to create a chess board, and place it in the middle of the screen, so far i cannot get it to be directly in the center. i don't want to hard code the position to the screen because i'm going to be dealing with different screen sizes.
var winsize = cc.director.getWinSize();
var centerpos = cc.p(winsize.width / 2, winsize.height / 2);
for (i=0; i<64; i++){
var tile = cc.Sprite.create(res.tile_png);
this.addChild(tile,0);
tile.setPosition(winsize.width+i%8*50/-10, winsize.height-Math.floor(i/8)*50);
}
But the tiles and positioning is completely off
#jumpman8947, if you're using Cocos2d js perhaps you have a similar line: cc.view.setDesignResolutionSize(480, 320, cc.ResolutionPolicy.SHOW_ALL);
In this particular case the game will scale to any sceeen, but still run in 480x320 resolution, so no matter what screen resoultion you use, the center in the cocos world would always be cc.p(240, 160) so no matter what's the window size or the screen resolution, the resolution of the game stays the same
You can read more about resolution policies here (and in official js-doc):
http://www.cocos2d-x.org/wiki/Multiple_Resolution_Policy_for_Cocos2d-JS
Also please be aware, that the Sprite position in Cocos is the position of the centre of the sprite, not bottom left corner
In your question it's not completely clear exactly what you want. However, I made some assumptions. The explanation for my solution is embedded in the comments in the code below.
// var winsize = cc.director.getWinSize();
// Here is some example hard-coded return values:
var winsize = {width: 600, height: 400};
// You can change these numbers to see how they influence
// the outcome.
// var centerpos = cc.p(winsize.width / 2, winsize.height / 2);
// This line doesn't seem relevant for the question you asked.
// Or, rather, the following calculations will result in the tiles
// being centred on the screen anyway, so this calculation here
// is unnecessary.
// Being a chess board, I assume that you want the tiles to be square,
// i.e. to have the same width and height.
// If so, first find out which is the minimum dimension
// and calculate the tile size as being 1/8 of that.
var minDimn = Math.min(winsize.width, winsize.height);
var tileSize = minDimn / 8;
// Find out how far in from the left and how far down from the top
// you need the upper left corner of the upper left tile to start.
// This assumes that you don't need any "margin" around the board.
// (If you do need such a "margin", basically subtract it twice
// from each of winsize.width and winsize.height above.)
// Start with default values of 0 for each, but then add in the
// excess for the longer dimension, but divide it by two
// because that excess will be split between either
// the top and bottom or the left and right.
var xStart = 0, yStart = 0;
if (winsize.width > winsize.height) {
xStart = (winsize.width - winsize.height) / 2;
} else if (winsize.height > winsize.width) {
yStart = (winsize.height - winsize.width) / 2;
}
// Instead of looping through all 64 positions in one loop,
// loop through all the horizontal positions in an outer loop
// and all the vertical positions in an inner loop.
for (i = 0; i < 8; i++) {
// For the horizontal dimension, calculate x for each tile
// as the starting position of the left-most tile plus
// the width of the tile multiplied by the number of tiles (0-based)
var x = xStart + i * tileSize;
// Now the inner loop
for (j = 0; j < 8; j++) {
// Same type of calculation for the y value.
var y = yStart + j * tileSize;
// You can see the values in this demo here.
document.write("<pre>(" + x + ", " + y + ")</pre>");
// The following two lines don't seem to be relevant to the question.
// var tile = cc.Sprite.create(res.tile_png);
// this.addChild(tile,0);
// Use your calculated values in your function call.
// tile.setPosition(x, y);
}
}
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.