I have a trivial little game I wrote in javascript that creates a wave from where you click. I figured out two ways to make the "wave" move across the screen:
by calling jQuery.animate() with increasingly large times. demo
by recursvely calling setTimeout. demo
The problem I have is that I want the behavior of 2 (columns all grow at the same speed with an offset timing) but with the action of 1 (subsequent clicking compounds the "waves" formed).
Right now on the second demo, if you click again before the "wave" is finished, it immediately clears the setTimeouts and starts them all over again. I want to be able to stack them like in the first example.
You can view the source of either of those pages to see the code (methods are getMouseXYUp and getMouseXYDown).
the basic gist of what i am doing in the second demo is found in these two functions:
function getMouseXYUp(e) {
$("body").die('mousemove');
h = (document.height - e.pageY + 17);
left = id-1;
right = id+1;
setTimeout("moveleft()", 100);
setTimeout("moveright()", 100);
return true
}
function moveleft(){
if (left >= 0){
$("#div"+left).animate({'height': h}, 400);
left--;
setTimeout("moveleft()", 50);
}
}
The problem is that you're resetting the variables "left" and "right" as soon as the second mouse click happens. Is this any closer to what you're looking for?
function getMouseXYUp(e) {
$("body").die('mousemove');
var h = (document.height - e.pageY + 17);
var left = id-1;
var right = id+1;
setTimeout(function() { moveleft(left, h); }, 100);
setTimeout(function() { moveright(right, h); }, 100);
return true;
}
...
function moveleft(left, h) {
if (left >= 0){
$("#div"+left).animate({'height': h}, 400);
left--;
setTimeout(function() { moveleft(left, h); }, 50);
}
}
function moveright(right, h) {
if (right < divs){
$("#div"+right).animate({'height': h}, 400);
right++;
setTimeout(function () { moveright(right, h); }, 50);
}
}
Looking at the code, looks like you have issues with global variables. You would need to pass along left and right in the function calls and not have it in a global scope.
Related
I am new to the amazing world of creative coding and p5js. I want to create a website for my design studio with a p5js effect that I can't find the solution for. I searched everywhere and I can't find anyone with the same problem as me, that's why I'm posting my very first message here. Here's the idea: in a canvas, I would like that with each mouse click, a different image can appear. Currently, my code allows to display images randomly but I would like to be able to set a cyclic order of appearance : work0.png, work1.png, work2.png... and it starts again.
If someone has seen this problem somewhere or could explain it to me I would be very grateful. Thanks !
let works = []
function preload() {
for (let i = 0; i < 5; i++) {
works[i] = loadImage("work" + i + ".png")
}
}
function setup() {
canvas = createCanvas(windowWidth, windowHeight);
canvas.position(0, 0);
canvas.style('z-index', '1');
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
canvas.position(0, 0);
canvas.style('z-index', '1');
}
function draw() {
cursor(CROSS);
}
function mouseClicked() {
imageMode(CENTER);
let r = floor(random(0, 6));
image(works[r], mouseX, mouseY, 500, 600);
}
instead of using random, you can use a counter
let works = []
function preload() {
for (let i = 0; i < 5; i++) {
works[i] = loadImage("work" + i + ".png")
}
}
function setup() {
canvas = createCanvas(windowWidth, windowHeight);
canvas.position(0, 0);
canvas.style('z-index', '1');
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
canvas.position(0, 0);
canvas.style('z-index', '1');
}
function draw() {
cursor(CROSS);
}
var counter = 0
function mouseClicked() {
imageMode(CENTER);
counter++ //add one to the counter
image(works[counter%5], mouseX, mouseY, 500, 600); // the % symbols is modulo, which is a fancy word for remainder when divided by that number, so it would cycle from 0 to 4
}
So the counter starts at zero, and every time you click we increase the counter by one. to stop it from increasing constantly, we can use the modulo operator %(in programming this isn't the percent sign). examples: 2%5 is 2, 16%5 is 1, 25%5 is 0.
this essentially makes the value cycle from 0 to 4. the counter is constantly increasing, but the remainder when divided by five will always cycle.
also btw p5.js is a javascript library, and you are loading in code made by other people, which means without the p5.js library the code doesn't run in this code snippet on StackOverflow
I want to control the amount of times draw() is being called, basically lets immagine that i have that basic code:
var e = 1;
function setup() {
window.canvas = createCanvas(200, 200);
}
function draw() {
fill(255, 255, 0);
ellipse(50 + e, 50, 30);
e++;
}
and I want to make a function moveOneStep() that will active draw() once, so let's assume I'm doing a for loop 5 times, then draw will be called 5 times and the circle will move 5 steps ( pixels ) to the right,
how can it be done?
Do you want to fixe the framerate in your code or to call draw() function in a specific time? If it's the case I don't think you can because the draw function is, as mentioned in the documentation
the draw() function continuously executes the lines of code contained inside its block until the program is stopped or noLoop() is called
But if you want to fixe the frame rate u can call the frameRate(number) in the setup() function. So, now if you want to have draw() called 30 times per second
(or 30 fps for a gaming reference) your code will look like this.
var e = 1;
function setup() {
window.canvas = createCanvas(200, 200);
frameRate(30);
}
function draw() {
fill(255, 255, 0);
ellipse(50 + e, 50, 30);
e++;
}
Check the dcumentation for further references.
Happy coding ^^.
Instead of controlling the number of times that draw gets called you can control what happens inside of draw.
To use draw in a way that allows user interaction and works well with the p5js lib you can listen for user interaction with a keyPressed method and record what the user is telling the system to do. Back in the draw method you can update positions and then render the sketch.
This approach breaks the direct connection between frame rate and user interaction. For example we could slow the frame rate down but still allow the user to click buttons as quickly as the key board would allow. Positions would still get adjusted and draw would catch up.
Here is a simple example that allows a user to move a circle with a ,s, d, w keys or up, down, left, right arrow keys.
var xPos = 0;
var yPos = 0;
var moveRightCount = 0;
var moveLeftCount = 0;
var moveUpCount = 0;
var moveDownCount = 0;
function setup() {
window.canvas = createCanvas(200, 200);
}
function keyPressed() {
if (keyCode === RIGHT_ARROW || keyCode === 68) {
moveRightCount+=1;
} else if (keyCode === LEFT_ARROW || keyCode === 65) {
moveLeftCount+=1;
} else if (keyCode === DOWN_ARROW || keyCode === 83) {
moveDownCount+=1;
}else if (keyCode === UP_ARROW || keyCode === 87) {
moveUpCount+=1;
} else if (keyCode === 70){
// console.log(keyCode);
}
function draw() {
background(255);
if (moveRightCount > 0){
xPos++;
moveRightCount--;
}
if (moveLeftCount > 0){
xPos--;
moveLeftCount--;
}
if (moveUpCount > 0){
yPos--;
moveUpCount--;
}
if (moveDownCount > 0){
yPos++;
moveDownCount--;
}
fill(255, 255, 0);
ellipse(50 + xPos, 50 + yPos, 30);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/p5.min.js"></script>
Now if you have a method called moveOneStep() it could adjust the appropriate move count and as draw gets called according to its frameRate the image would be moved. Say we call moveOneStep() in a loop that executes 5 times the image would be moved one position per execution of draw 5 times.
I created a rectangle , and i want to give it a speed that change over time (acceleration) .
So I made a "setTimeout" inside a "while" loop.
Supposedly , the "while" loop should continuously change the interval of the "setTimeout" (var=interval) by -1 , but instead it replaces it with 1 !!, wich makes the rectangle print every 1 milliseconds .
I would like to know why this happens .
the same thing happens if i use the "for" loop.
and i wouldn't mind any other alternative to create acceleration effect.
thank you
var canvas = document.getElementById("canvas")
var context = canvas.getContext("2d")
var posX=20;
var posY=20;
var interval = 500;
function print () {
//background
context.fillStyle="black";
context.fillRect(0, 0, 500, 500);
//object
context.fillStyle="#4286f4";
context.fillRect(posX, posY, 50, 50);
posX = posX + 1;
posY = posY + 1;
}
while (interval > 300) {
interval-- ;
setTimeout(print, interval);
}
<!DOCTYPE html>
<html>
<head>
<title>Particles</title>
<meta charset="UTF-8"/>
</head>
<body>
<canvas id="canvas" width="500" height="500"></canvas>
</body>
</html>
the "while" loop should continuously change the interval of the "setTimeout"
No, it schedules a new timer at the new interval. The previous one keeps running as well. You very, very, very quickly end up with a bunch of timers pending -- which all then expire one right after the next.
Once you start a timer, you can't change when it fires. You can cancel it, but you can't change when it fires.
setTimeout also schedules a single timed callback. If you want repeated ones, use setInterval or schedule a new callback from the setTimeout callback when it runs.
I recommend taking a step back and experimenting with the basics of timers and intervals before moving on to something complex like doing animations.
Separately: setTimeout and setInterval are the wrong tools for animation, at least in isolation. Instead, when you know you need to update the circle, use requestAnimationFrame to have the browser call you immediately before it renders the display (it will fire ~60 times/second, so only request it when you need it). That helps you coordinate with the browser's internal display cycles.
Something along these lines:
scheduleNext();
function scheduleNext() {
if (interval > 300) {
setTimeout(function() {
requestAnimationFrame(function() {
print();
scheduleNext();
});
}, interval);
}
}
Live Example:
var canvas = document.getElementById("canvas")
var context = canvas.getContext("2d")
var posX = 20;
var posY = 20;
var interval = 500;
function print() {
//background
context.fillStyle = "black";
context.fillRect(0, 0, 500, 500);
//object
context.fillStyle = "#4286f4";
context.fillRect(posX, posY, 50, 50);
posX = posX + 1;
posY = posY + 1;
}
scheduleNext();
function scheduleNext() {
if (interval > 300) {
setTimeout(function() {
requestAnimationFrame(function() {
print();
scheduleNext();
});
}, interval);
}
}
<canvas id="canvas" width="500" height="500">
setTimeout doesn't pause the script, it schedules the callback to be run in the given milliseconds. This means that you're scheduling all the callbacks right away, and there's only a 1 millisecond difference between them, which isn't enough to cause an observable difference.
Update :
ahaa , Yes its possible to create the animation loop with just "setTimeout" ,
function test () {
print ();
interval = interval - 10;
setTimeout(test, interval);
}
test ();
var video;
var snapshots = [];
var readyCheck = false;
var button;
function setup() {
createCanvas(800, 600);
background(0);
video = createCapture(VIDEO, ready);
video.size(200, 150);
}
function ready() {
readyCheck = true;
console.log('work');
}
function draw() {
var w = 200;
var h = 150;
var x = 0;
var y = 0;
if (readyCheck) {
for (var i = 0; i < 100; i++) {
// use setTimeout() to wait for 2 seconds
setTimeout(function() {
snapshots[i] = video.get();
image(snapshots[i],x, y);
x += w;
if (x >= width) {
x = 0;
y += h;
}
}, 2000);
}
}
}
my purpose is taking pictures from the webcam after specific time. So I use the setTimeout() in JS. I expect pictures will appear on the canvas every 2 seconds in a row.
when entering the for part, the code will wait 2 seconds and capture the image from webcam and display it.
but my situation is that all the picture appear on the canvas at the same time.
You need to take a step back and understand how the draw() function and the setTimeout() functions work.
The draw() function is automatically called 60 times per second. You can adjust this by calling the frameRate() function or the noLoop() function. More info is available in the reference.
The setTimeout() function sets up a callback function that is automatically called after some duration, in your case 2 seconds.
So, what your code is doing is setting up 100 callback functions that will all fire in 2 seconds- and it's doing this 60 times per second! So in 1 second, you'll have 6000 functions that will start firing 2 seconds later! This is almost definitely not what you want.
P5.js already has its own timing mechanism in the draw() function that's called 60 times per second, so it seems a little weird to use the setTimeout() function inside P5.js code. Instead, you should probably set up your own timing using the frameCount variable or the millis() function.
Here's an example that shows a random color every second:
function setup() {
createCanvas(200, 200);
}
function draw() {
if(frameCount % 60 == 0){
background(random(256), random(256), random(256));
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.11/p5.min.js"></script>
This code uses the frameCount variable and the % modulus operator to check whether 60 frames have passed, and if so, it sets the background to a random color. You'll want to do something similar.
Like I said above, more info about all of this can be found in the reference.
I have two setTimeout functions: one makes a div pop up randomly on the y axis, and one moves it vertically. These to need to be perfectly in sync, and setTimeout just doesn't cut it: the div has to reset its x position as soon as its y position becomes greater than the window height, before resetting to 0 to start anew. Something like that. I'm thinking of somehow integrating getMilliseconds, but any alternative will do.
Here's the current Javascript:
var width = window.outerWidth;
var height = window.outerHeight;
var h2 = height * 5;
//left
function LR() {
setTimeout(function() {
var left = [];
var one = 1;
do {
one++;
left.push(one);
}
while (one <= width);
var random = Math.floor(Math.random() * left.length);
document.getElementById("test").style.left = random + "px";
LR();
}, h2);
}
LR();
//top
function TB() {
setTimeout(function() {
var one = document.getElementById("test").offsetTop;
one++;
document.getElementById("test").style.top = one + "px";
if (one == height) {
one == 0;
document.getElementById("test").style.top = 0;
}
TB();
}, 2);
}
TB();
If you'd like to look at my full code, and/or preview, I've set up a JSFiddle: http://jsfiddle.net/JqVb9/. Thanks in advance.
Yes, for animations you always should check the current time - setTimeout/.Interval are absolutely unreliable and tend to drift.
To do that, just use Date.now() and make your animation a mathematical function to get the position from time (easiest: linear movement).
As #JanDvorak already pointed out in the comments, you should consider performing all movements from the same timer. This will make the animation smoother as you don't change the DOM / styling too often - each frame is usually rendered only once. Also, this will ensure synchronity.