I am trying to draw a line in a canvas. I am trying to make the line moving with time. I am using the following code to do so
var ctx = mycanvas.getContext('2d');
ctx.beginPath();
for (var x = 0; x < 200; x++) {
setInterval(draw(x, 0, ctx), 3000);
x = x++;
}
And here is the draw function
function draw(x, y, ctx) {
ctx.moveTo(10 + x, 400);
ctx.lineTo(11 + x, 400);
ctx.lineWidth = 10;
ctx.strokeStyle = "#ff0000";
ctx.stroke();
}
But the setInterval() function is not working and the line is being drawn instantly. Its not waiting for 3s to proceed to next pixel.
Am I making a mistake?
setInterval needs a function as the first parameter. Right now you are just calling draw(x,0,ctx) and it returns undefined. So your code is equivalent to setTimeout(undefined, 3000).
Instead you need to provide a callable function and invoke draw from it. Try this:
setInterval(function() {
draw(x, 0, ctx);
}, 3000);
Another problem is due to typical closure in loop behavior. You will need to create separate scope to be able to work with individual values of x:
for (var x = 0; x < 200; x++) {
(function(x) {
setInterval(function() {
draw(x, 0, ctx);
}, 3000 * x);
})(x);
x = x++;
}
Also check this question for more examples how to fix situations like this.
Related
I'm trying to make a basic 2d game with p5js and p5.play. An issue that seems to cause issues every time I try to do anything is the keyIsDown function. Is there a way to determine if a key is down before pressing it? If I used
upKey = keyIsDown(UP_ARROW);
upKey will show as undefined until I press the up arrow. Is there any way to assign the respective boolean values to these types of things prior to pressing them?
As of now, my game will not properly work until I have pressed every involed key one time.
The keyIsDown() function checks if the key is currently down, i.e. pressed. It can be used if you have an object that moves, and you want several keys to be able to affect its behaviour simultaneously, such as moving a sprite diagonally.
Note that the arrow keys will also cause pages to scroll so you may want to use other keys for your game.. but if you want to use arrow keys this is the code snippet from the reference page
let x = 100;
let y = 100;
function setup() {
createCanvas(512, 512);
}
function draw() {
if (keyIsDown(LEFT_ARROW)) {
x -= 5;
}
if (keyIsDown(RIGHT_ARROW)) {
x += 5;
}
if (keyIsDown(UP_ARROW)) {
y -= 5;
}
if (keyIsDown(DOWN_ARROW)) {
y += 5;
}
clear();
fill(255, 0, 0);
ellipse(x, y, 50, 50);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.min.js"></script>
To implement similar logic without the use of arrow keys you will need to determine the key code of the keys you want to use.
Here is an example that uses awsd keys and also logs out the key code of the currently pressed key.
let x = 50;
let y = 50;
function setup() {
createCanvas(512, 512);
}
function keyPressed(){
console.log(keyCode);
}
function draw() {
if (keyIsDown(65)) {
x -= 5;
if (x < 0) x = 0;
}
if (keyIsDown(68)) {
x += 5;
if (x > width) x = width;
}
if (keyIsDown(87)) {
y -= 5;
if (y < 0) y = 0;
}
if (keyIsDown(83)) {
y += 5;
if ( y > height) y = height;
}
clear();
fill(255, 0, 0);
ellipse(x, y, 50, 50);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.min.js"></script>
I'm having multiple issues.
Everytime I click the animation goes faster. SOLVED #Jorge Fuentes González
Everytime I click the
last animation stops moving SOLVED #Kaiido
I have changed about everything I could think of around and still the same issue. Any help would be appreciated. Thanks!
function drawFrame(frameX, frameY, canvasX, canvasY) {
ctx.drawImage(img,
frameX * width, frameY * height,
width, height,
x_click, y_click,
scaledWidth, scaledHeight);
}
// Number of frames in animation
var cycleLoop = [3, 2, 1, 0, 7, 6, 5];
// Position of sprite in sheet
var currentLoopIndex = 0;
var frameCount = 0;
function step() {
frameCount++;
if (frameCount < 30) {
window.requestAnimationFrame(step);
return;
}
frameCount = 0;
// ctx.clearRect(0, 0, canvas.width, canvas.height);
drawFrame(cycleLoop[currentLoopIndex++], 0, 0, 0);
// Starts animation over
if (currentLoopIndex >= cycleLoop.length) {
// If you want to loop back in oposite direction after full animation
cycleLoop.reverse();
// Reseting position of which sprite to use
currentLoopIndex = 0;
}
window.requestAnimationFrame(step);
}
canvas.addEventListener("mousedown", getPosition, false);
function getPosition(event) {
x_click = event.x;
y_click = event.y;
x_click -= canvas.offsetLeft * 10;
y_click -= canvas.offsetTop * 10;
step();
}
==============================
JS Fiddle:
https://jsfiddle.net/HYUTS/q4fazt6L/9/
=======================================
Each time you click, you call step();, which will call window.requestAnimationFrame(step);, which will call step() the next animation frame. I don't see any stop point so the loop will be called forever.
So, when you call step() the first time, step() will be called continuously for ever, and if you click again, another step() "line" will be called a second time which will call window.requestAnimationFrame(step); for ever again, so now you will have two "lines" calling step(). That's why the animation goes faster, because on each animation frame step() will be called twice, doubling the calculations.
What you have to do is to check if the animation is already running (with a flag) and do not run it again, or to window.cancelAnimationFrame(ID) before starting the step() loop again. Note that on each click you must restart the variables that control the animation, like frameCount and currentLoopIndex
function drawFrame(frameX, frameY, canvasX, canvasY) {
ctx.drawImage(img,
frameX * width, frameY * height,
width, height,
x_click, y_click,
scaledWidth, scaledHeight);
}
// Number of frames in animation
var cycleLoop = [3, 2, 1, 0, 7, 6, 5];
// Position of sprite in sheet
var currentLoopIndex = 0;
var frameCount = 0;
var animationid = null;
function step() {
frameCount++;
if (frameCount < 30) {
animationid = window.requestAnimationFrame(step);
return;
}
frameCount = 0;
// ctx.clearRect(0, 0, canvas.width, canvas.height);
drawFrame(cycleLoop[currentLoopIndex++], 0, 0, 0);
// Starts animation over
if (currentLoopIndex >= cycleLoop.length) {
// If you want to loop back in oposite direction after full animation
cycleLoop.reverse();
// Reseting position of which sprite to use
currentLoopIndex = 0;
}
animationid = window.requestAnimationFrame(step);
}
canvas.addEventListener("mousedown", getPosition, false);
function getPosition(event) {
x_click = event.x;
y_click = event.y;
x_click -= canvas.offsetLeft * 10;
y_click -= canvas.offsetTop * 10;
frameCount = currentLoopIndex = 0;
window.cancelAnimationFrame(animationid);
step();
}
First step in your situation, is to create different objects for every animatables, so they can be drawn and updated independently.
After, you will have to split your logic in several parts.
A basic setup is to have one main loop that runs constantly in the background, and which will call all higher level objects update function, then all the drawing functions.
It's in these higher level methods that you will do the checks as to whether they should actually be discarded or not. The main loop doesn't have to take care of it.
In the example below, I created a class for your animatable objects. These objects will now have their own status, and will be able to update as they wish independently of others.
With this setup, adding a new Object in the scene is just a matter of pushing it in an Array.
// Our Animatable class (ES5 style...)
// Each object as its own frameCount and its own loopIndex
function Animatable(x, y) {
this.x = x;
this.y = y;
this.frameCount = 0;
this.loopIndex = 0;
this.cycleLoop = [3, 2, 1, 0, 7, 6, 5];
}
Animatable.prototype = {
update: function() {
this.frameCount++;
if (this.frameCount < 30) {
return;
}
this.frameCount = 0;
this.loopIndex++
if (this.loopIndex >= this.cycleLoop.length) {
// If you want to loop back in oposite direction after full animation
this.cycleLoop.reverse();
// Reseting position of which sprite to use
this.loopIndex = 0;
}
},
draw: function() {
// check the image is loaded
if (!img.naturalWidth) return;
var frameX = this.cycleLoop[this.loopIndex];
ctx.drawImage(img,
frameX * width, 0,
width, height,
this.x - scaledWidth/2, this.y - scaledHeight/2,
scaledWidth, scaledHeight);
}
};
// the main anim loop, independent
function startAnimLoop() {
animloop();
function animloop() {
requestAnimationFrame(animloop);
// updates
animatables.forEach(update);
// drawings
ctx.clearRect(0,0,canvas.width, canvas.height);
animatables.forEach(draw);
}
function update(animatable) {
animatable.update();
}
function draw(animatable) {
animatable.draw();
}
}
// one image for all
var img = new Image();
img.src = 'https://imgur.com/u2hjhwq.png';
img.onload = startAnimLoop;
// here we will hold all our objects
var animatables = [new Animatable(50, 50)]; // start with a single one
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
// some constant from OP's fiddle
var scale = 1.5;
var width = 100; // Bigger numbers push left <-, smaller right ->
var height = 100;
var scaledWidth = scale * width;
var scaledHeight = scale * height;
canvas.onclick = function(evt) {
var rect = canvas.getBoundingClientRect();
var x = evt.clientX - rect.left;
var y = evt.clientY - rect.top;
// we simply create a new object ;-)
animatables.push(new Animatable(x, y));
};
canvas{border:1px solid}
<canvas id="canvas" width="500" height="500"></canvas>
window.requestAnimationFrame is still running when you click again, and when you click you add another tick per frame to your animation, doubling your speed, as step() is called two times each frame now. You should cancel the previous animation frame when clicking again, using window.cancelAnimationFrame()
https://developer.mozilla.org/en-US/docs/Web/API/Window/cancelAnimationFrame
Like this:
...
var animationID;
//in step() save the id in every call
function step() {
...
animationID = window.requestAnimationFrame(step);
...
}
//In getPosition cancel the current animation
function.getPosition(event) {
...
window.cancelAnimationFrame(animationId);
...
}
And if you want multiple animations running, create an object for each and make the function step() their property, then run window.requestAnimationFrame(this.step) inside of step(). You'd also have to save every variable needed for the animation like currentLoopIndex as part of the object.
I have an ellipse that scales through draw(), but for some reason, it flashes uncontrollably. I can't seem to figure out why. I suspect it has to do with setTimeout. I need it because I need to wait 10 seconds before drawing the ellipse Here's the code:
//diameter of ellipse that increments
var dia1 = 0;
var dia2 = 0;
function setup() {
createCanvas(400,400);
stroke(255);
noFill();
frameRate(40);
}
//draw and increment ellipse
function circle1() {
ellipse(width/2,height/2, dia1,dia1);
dia1 = dia1+1;
if (dia1 >= width) {
dia1 = 0;
}
}
function circle2() {
ellipse(width/2,height/2, dia2,dia2);
dia2 = dia2+1;
if (dia2 >= width) {
dia2 = 0;
}
}
function draw() {
background(40,40,40);
//wait 10 seconds before drawing ellipse
setTimeout(function() { circle1(); }, 10000);
circle2();
console.log(dia1);
}
You should not use setTimeout() to call drawing functions.
If you want to do timing, use the millis() function. More info is available in the reference, but a basic program would look like this:
function draw(){
background(0);
if(millis() > 10000){
ellipse(width/2, height/2, 25, 25);
}
}
I am currently learning canvas and if I wanted to store my shape and create lets say 4 of them but position them at different locations or with different colors how would I do that?
http://jsfiddle.net/bp0bxgbz/50/
var x = 0;
var y = 15;
var speed = 5;
function animate() {
reqAnimFrame = window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.oRequestAnimationFrame
;
reqAnimFrame(animate);
x += speed;
if(x <= 0 || x >= 475){
speed = -speed;
}
draw();
}
function draw() {
var canvas = document.getElementById("ex1");
var context = canvas.getContext("2d");
context.clearRect(0, 0, 500, 170);
context.beginPath();
context.moveTo(x,y);
context.lineTo(x + 105,y + 25);
context.lineTo(x+25,y+105);
context.fillStyle="red";
context.fill();
}
animate();
Create 4 objects--one for each triangle.
Each object holds the current x,y position and the current speed for its 1 triangle.
You can use the information inside any 1 object in the draw() function to draw that 1 triangle at its current x,y position.
In the animation function you can use the information inside each of the 4 objects to change the x position of each triangle.
var shapes=[];
shapes.push({x:10,y:10,speed:2});
shapes.push({x:10,y:125,speed:4});
shapes.push({x:10,y:250,speed:6});
shapes.push({x:10,y:375,speed:8});
In the animation loop, iterate through the array and draw each of the 4 objects by feeding them individually into the draw function.
context.clearRect(0, 0, 500, 170);
for(var i=0; i<shapes.length;i++){
var s=shapes[i];
s.x+=s.speed;
if(s.x <= 0 || s.x >= 475){
s.speed*=-1;
}
draw(s);
}
The draw function should take the specified object and draw according to its specified x,y & speed values.
// create canvas & context variables once at the beginning of the script
var canvas = document.getElementById("ex1");
var context = canvas.getContext("2d");
function draw(s) {
context.beginPath();
context.moveTo(s.x,s.y);
context.lineTo(s.x + 105,s.y + 25);
context.lineTo(s.x+25,s.y+105);
context.fillStyle="red";
context.fill();
}
Note: you can create the canvas & context variables once at the beginning of your script. No need to recreate those variables with each call to draw. Also, if all the drawings will be red-filled, then you could set that once at the beginning of the script, too.
Example code and a Demo:
var context=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var shapes=[];
shapes.push({x:10,y:10,speed:2,color:'red'});
shapes.push({x:10,y:125,speed:4,color:'green'});
shapes.push({x:10,y:250,speed:6,color:'blue'});
shapes.push({x:10,y:375,speed:8,color:'gold'});
animate();
function animate(){
context.clearRect(0,0,cw,ch);
for(var i=0; i<shapes.length;i++){
var s=shapes[i];
s.x+=s.speed;
if(s.x <= 0 || s.x >= cw){
s.speed*=-1;
}
draw(s);
}
requestAnimationFrame(animate);
}
function draw(s) {
context.beginPath();
context.moveTo(s.x,s.y);
context.lineTo(s.x + 105,s.y + 25);
context.lineTo(s.x+25,s.y+105);
context.fillStyle=s.color
context.fill();
}
body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red;}
<canvas id="canvas" width=300 height=450></canvas>
I have a Javascript question, that might be obvious, but I just can't seem to find the solution for it, and I don't know how to solve it.
(Also, I'm still pretty new to coding)
So I'm writing a patrol function for squares in my game, and for now I started out with just making the square move one way. Later on I will make it patrol back and forth. That's why I put the move function in the draw function.
I want the move function to be reusable for several squares, but I can't seem to make a general move function work. However, I can make a move function specifically for a certain square, work.
Can anyone tell me why this works:
var square = 16;
var posX = 32;
var posY = 32;
function moveSquare() {
for (i = 0; i < 10; i++) {
posX++;
}
}
function draw() {
var redSquare = { x: posX, y: posY, w: square, h: square, color: "red" };
ctx.fillStyle = redSquare.color;
rect(redSquare.x,redSquare.y,redSquare.w,redSquare.h);
moveSquare();
}
And this doesn't:
var square = 16;
var posX = 32;
var posY = 32;
function move(pos) {
for (i = 0; i < 10; i++) {
pos++;
}
}
function draw() {
var redSquare = { x: posX, y: posY, w: square, h: square, color: "red" };
ctx.fillStyle = redSquare.color;
rect(redSquare.x,redSquare.y,redSquare.w,redSquare.h);
move(posX);
}
By the way, I defined the rect function elsewhere, but I figured it wasn't important to include.
Hope you can help
The value passed to the function move is being passed by value, not by reference.
So the pos inside move is private to the move function.
The pos variable will be a copy of posX, so no matter what you do to it in the move function, the global posX will not be affected.
Consider the code:
var x = 5;
function move(x)
{
x++;
console.log("In function x is: " + x);
}
console.log("Outside function, before call x is: " + x);
move(x);
console.log("Outside function, after call x is: " + x);
This outputs:
"Outside function, before call x is: 5"
"In function x is: 6"
"Outside function, after call x is: 5"
The function move has it's own private copy x.
Look into pass by reference, pass by value and variable scope.