How to start a counter for a setInterval - javascript

I have this code working, but I'd like to improve it:
var c = canvas.getContext("2d");
//this is called as an object method I created
var animar = function () {
var obj = this;//saves the object that called it to later access to its properties
var counter= 0;
var animacion = setInterval(function(){
c.save()
c.clearRect(0, 0, canvas.width, canvas.height);
c.rotate(0.1*counter);
obj.pintar();
c.restore();
counter++;
}, 50);
}
I'd like to use a outter function for posible future use but when I change the code I have, there is a hoisting problem and I don't know how to get the counter inside the rotar() function without overwriting it all the time:
var animar = function () {
var obj = this;
var counter= 0;
var animacion = setInterval(function(){
rotar(obj)
}, 50);
}
function rotar (obj) {
c.save()
c.clearRect(0, 0, canvas.width, canvas.height);
c.rotate(0.1*counter);
obj.pintar();
c.restore();
counter++;
}
I get that the first one works because they are nested, while in the second example isn't. How can I have a counter for the setInterval WITHOUT USING A GLOBAL VARIABLE?
(I mean... if I call it a second time, it will not start in 0. If I set it to be 0 in the animar() function, it would work, but then I'd need to set it to 0 in every function that uses a counter or using counters with diferent names. This two posibilities don't sound good.)
Thanks

You just need to put var counter = 0 outside the functions:
var counter;
var animar = function () {
var obj = this;
counter = 0; // reset every time before 'animacion'...
var animacion = setInterval(function(){
rotar(obj)
}, 50);
}
function rotar (obj) {
c.save()
c.clearRect(0, 0, canvas.width, canvas.height);
c.rotate(0.1*contador);
obj.pintar();
c.restore();
counter++;
}
Alternatively, if you don't want a global variable, you can follow Walter's answer, or do this:
var animar = function () {
var obj = this;
var counter = 0; // new variable every time animar() is called
var animacion = setInterval(function(){
rotar(obj, counter);
counter++;
}, 50);
}
function rotar (obj, counter) {
c.save()
c.clearRect(0, 0, canvas.width, canvas.height);
c.rotate(0.1*contador);
obj.pintar();
c.restore();
}

make counter scoped inside of your animar object:
var animar = function () {
var obj = this;
obj.counter= 0;
var animacion = setInterval(function(){
rotar(obj)
}, 50);
}
function rotar (obj) {
c.save()
c.clearRect(0, 0, canvas.width, canvas.height);
c.rotate(0.1*obj.counter);
obj.pintar();
c.restore();
obj.counter++;
}

Related

I can't access my javascript code because of infinite prompts [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed last year.
Improve this question
Help, I've been using this website called "codepen" to do some javascript coding. But I accidently made it send me infinite prompts, everytime I open the project. Now I can't access the code. I searched for some time for an answer but I found none. heres a link to the problem: https://codepen.io/Aibel-Roy/pen/zYPBeEW
//I can't post the code because of the infinite prompts. Sorry.
Here's your code:
//config
var tick = 50;
var fieldOfView = 25;
var Speed = 0.25;
var ZMulti = 4;
var ClearOnDraw = true;
// variables
var keymap = [];
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var meshA = [
"0,0,0",
"1,0,0",
"1,1,0",
"0,1,0",
"0,0,1",
"1,0,1",
"0,1,1",
"1,1,1"
];
var textures = [
"http://www.textures4photoshop.com/tex/thumbs/red-sofa-leather-seamless-texture-53.jpg"
];
var cameraData = [0, 0, 0];
//keymap
window.addEventListener(
"keydown",
(event) => {
var name = event.key;
keymap.push(name);
},
false
);
window.addEventListener(
"keyup",
(event) => {
var name = event.key;
if (keymap.includes(name)) {
keymap.splice(keymap.indexOf(name), 1);
}
},
false
);
//render img
function draw() {
if (ClearOnDraw) {
ctx.clearRect(0, 0, 10000, 1000);
}
var img = new Image(); // texturing
img.src = textures[0];
prompt(img.src);
img.onLoad = function () {
var pattern = context.createPattern(imageObj, "repeat");
ctx.fillStyle = pattern;
var prevVert;
for (let i = 0; i <= meshA.length; i++) {
//convert 3D vector to 2D
var vert = meshA[i];
if (i >= meshA.length) {
vert = meshA[0];
}
var vertPos = vert.split(",");
var zMag = (vertPos[2] - cameraData[2]) * (fieldOfView / ZMulti);
var vertPos2D = [
(vertPos[0] - cameraData[0]) * fieldOfView + zMag,
(vertPos[1] - cameraData[1]) * fieldOfView + zMag
];
ctx.beginPath();
ctx.moveTo(vertPos2D[0], vertPos2D[1]);
for (let i1 = 0; i1 < meshA.length; i1++) {
var prv = meshA[i1].split(",");
var PrevzMag = (prv[2] - cameraData[2]) * (fieldOfView / ZMulti);
var I1VertPos = [
(prv[0] - cameraData[0]) * fieldOfView + PrevzMag,
(prv[1] - cameraData[1]) * fieldOfView + PrevzMag
];
ctx.lineTo(I1VertPos[0], I1VertPos[1]);
ctx.stroke();
}
ctx.closePath();
ctx.fill();
prevVert = vertPos2D;
}
};
}
function Movement() {
if (keymap.includes("w")) {
cameraData[2] -= Speed * 2;
}
if (keymap.includes("s")) {
cameraData[2] += Speed * 2;
}
if (keymap.includes("d")) {
cameraData[0] -= Speed;
}
if (keymap.includes("a")) {
cameraData[0] += Speed;
}
}
draw();
setInterval(function main() {
draw();
Movement();
}, tick);
How to disable the prompt(if the browser doesn't suggest you to suppress it):
On that page, bring up dev tools(Command + Option + I, or F12 on Windows).
Choose the correct page on the dev tool, usually looks like CodePen (Hash ID)
Override the prompt function in the console by typing window.prompt = () => {}.
Change your code, save and refresh the page.
There are probably better ways to do it but disabling JavaScript makes the code section unusable.
In your function draw(), there is a prompt in here:
function draw() {
// Trimmed
prompt(img.src);
// Trimmed
}
You also have setInterval calling draw() at a particular interval defined in tick (where you have assigned 50ms as the value):
setInterval(function main() {
draw();
Movement();
}, tick);
As a result, draw() gets called every 50ms, where prompt(img.src) gets called, leading to infinite prompt.
You need to change whatever you are doing in setInterval().

canvas with IEF, "static" variable and event

I wrote a program in JavaScript that displays 5 rectangles, one after each other with a ~1s delay.
As there is no static variable in Javascript as in C or other languages, I used an IIEF that returns a function in a variable draw to have an internal counter that is visible only in the function.
This code works perfectly.
let myCanvas=document.getElementById("my-canvas");
let draw=(function() {
let ctx = myCanvas.getContext('2d');
let counter=0;
return function() {
if (counter<5) {
ctx.fillRect(25+counter*20, 25, 10, 100);
counter++;
setTimeout(() => {window.requestAnimationFrame(draw)},1000);
}
}
})();
draw();
But now, I would like to fill the rectangle with a texture instead of the black color. Something like that :
let myPatternImg=new Image();
myPatternImg.onload = function() {
let myPattern=ctx.createPattern(myPatternImg,'repeat');
context.fillStyle=pattern;
... // code to draw rectangle
}
myPatternImg.src='pattern-file.png';
I do not how to do because when I initialize draw a function is returned directly. And this does not work with an onload event. I do not want a global variable for the counter. That is why I use an IIEF that returns a function to init the variable draw.
Any help would be appreciated. Thanks.
You can try use promises, to hide pattern creation:
function makePattern(ctx, src) {
return new Promise(function(resolve) {
let myPatternImg = new Image();
myPatternImg.onload = function() {
let myPattern = ctx.createPattern(myPatternImg, 'repeat');
resolve(myPattern);
};
myPatternImg.src = src;
});
}
let myCanvas=document.getElementById("my-canvas");
let draw = (function() {
let ctx = myCanvas.getContext('2d');
let counter = 0;
return function() {
makePattern(ctx, 'pattern-file.png').then(function(pattern) {
ctx.fillStyle = pattern;
if (counter < 5) {
ctx.fillRect(25+counter*20, 25, 10, 100);
counter++;
setTimeout(() => {window.requestAnimationFrame(draw)},1000);
}
});
}
})();
it's always good idea the separate your code into functions that do one thing.
But this code make little of sens, you are executing in a loop draw and each will draw 5 rectangles, I think that what you really want is something like this:
let draw = (function() {
let ctx = myCanvas.getContext('2d');
let counter = 0;
return function() {
makePattern(ctx, 'pattern-file.png').then(function(pattern) {
ctx.fillStyle = pattern;
(function loop() {
if (counter < 5) {
ctx.fillRect(25+counter*20, 25, 10, 100);
counter++;
setTimeout(() => {
window.requestAnimationFrame(loop);
}, 1000);
}
})();
});
}
})();
also this will fire each rectangle after 1000 seconds, if you want delay between each rectangle to be 1 second then use this:
let draw = (function() {
let ctx = myCanvas.getContext('2d');
let counter = 0;
return function() {
makePattern(ctx, 'pattern-file.png').then(function(pattern) {
ctx.fillStyle = pattern;
(function loop() {
if (counter < 5) {
ctx.fillRect(25+counter*20, 25, 10, 100);
counter++;
setTimeout(loop, 1000);
}
})();
});
}
})();
if you call draw multiple times it will create pattern each time, you can make it request only once if you use this code:
let draw = (function() {
let ctx = myCanvas.getContext('2d');
let counter = 0;
return function(pattern) {
ctx.fillStyle = pattern;
(function loop() {
if (counter < 5) {
ctx.fillRect(25+counter*20, 25, 10, 100);
counter++;
setTimeout(loop, 1000);
}
})();
}
})();
makePattern(ctx, 'pattern-file.png').then(draw);

HTML5/Javascript Game

I'm currently working on a game where I need a bunch of random bubbles to fall, and a rectangle below it that can move to "burst" these bubbles. I have a code currently that is not dropping these bubbles. Can someone tell me where I am going wrong? Here is my code:
var canvasColor;
var x,y,radius,color;
var x=50, y=30;
var bubbles= [];
var counter;
var lastBubble=0;
var steps=0, burst=0, escaped=0;
var xx=200;
var moveRectangleRight=false;
var moveRectangleLeft=false;
function startGame()
{
var r, g, b;
canvasColor= '#f1f1f1';
x=10;
y=10;
radius=10;
clearScreen();
counter=0;
while (counter<100)
{
x= Math.floor(450*Math.random()+1);
r = Math.floor(Math.random()*256);
g = Math.floor(Math.random()*256);
b = Math.floor(Math.random()*256);
color='rgb('+r+','+g+','+b+')';
bubbles[counter]=new Bubble(x,y,radius,color);
counter+=1;
}
setInterval('drawEverything()',50);
}
function Bubble(x,y,radius,color)
{
this.x=x;
this.y=y;
this.radius=radius;
this.color=color;
this.active=false;
}
function drawEverything()
{
var canvas=document.getElementById('myCanvas');
var context= canvas.getContext('2d');
steps+=1;
clearScreen();
if (steps%20==0 && lastBubble <100) {
bubbles[lastBubble].active=true;
lastBubble+=1;
}
drawRectangle();
counter=0;
while(counter<100)
{
if (bubbles[counter].active==true) {
context.fillStyle= bubbles[counter].color;
context.beginPath();
context.arc(bubbles[counter].x, bubbles[counter].y,
bubbles[counter.radius], 0, 2*Math.PI);
context.fill();
bubbles[counter].y+=2;
}
y=bubbles[counter].y;
x=bubbles[counter].x;
if (y>=240 && y<=270 && x>=xx-10 && x<=xx+60)
{
bubbles[counter]=false;
}
else if (y>=450)
{
bubbles[counter]=false;
}
counter+=1;
}
}
function clearScreen ()
{
var canvas=document.getElementById('myCanvas');
var context= canvas.getContext('2d');
context.fillStyle= canvasColor;
context.fillRect(0,0,450,300);
}
function drawRectangle()
{ var canvas, context;
if (moveRectangleRight==true && xx<400){
xx+=20;
}
if (moveRectangleLeft==true && xx>0){
xx-=20;
}
canvas=document.getElementById('myCanvas');
context= canvas.getContext('2d');
context.fillStyle = 'blue';
context.fillRect(xx,250,50,10);
}
function moveLeft () {
moveRectangleLeft=true;
}
function moveRight() {
moveRectangleRight=true;
}
function stopMove () {
moveRectangleRight=false;
moveRectangleLeft=false;
}
Your problem is that you initialize counter as 1, so when you add items to counter, you begin with the index of 1, which is actually the 2nd item. Thus, when you try to access bubbles[0], it returns undefined. Instead, initialize counter as 0.
A bracket was located in the wrong place, and this solved the problem.

Stopping an animated gif within 5 seconds

I have the following code to "freeze" a gif within 5 seconds, however the gif stops as soon as the window.onLoad event is fired:
[].slice.apply(document.images).filter(is_gif_image).map(freeze_gif);
function is_gif_image(i) {
return /^(?!data:).*\.gif/i.test(i.src);
}
function freeze_gif(i) {
var c = document.createElement('canvas');
var w = c.width = i.width;
var h = c.height = i.height;
c.getContext('2d').drawImage(i, 0, 0, w, h);
try {
i.src = c.toDataURL("image/gif");
} catch(e) {
for (var j = 0, a; a = i.attributes[j]; j++)
c.setAttribute(a.name, a.value);
i.parentNode.replaceChild(c, i);
}
}
window.onLoad = function(){
var gifs = freeze_gif(i);
setTimeout( function() {
gifs[n].click();
}, 5000);
}
Is it correct to include the setTimeout in window.onLoad? I have tried to include it in the freeze_gif(i), but the gif keeps stopping when the event is fired up. Can you help?
Thank you
EDIT 1:
As rightly recommended, I am including a working example of the current code:
https://jsfiddle.net/rqfdkkgz/
You are running this line at start:
[].slice.apply(document.images).filter(is_gif_image).map(freeze_gif);
And it freezing all of your images.
The problem with jsfiddle is that all the javascript code there is already inside window.load so you can't really use it twice, but here is the same inside snippet:
function is_gif_image(i) {
return /^(?!data:).*\.gif/i.test(i.src);
}
function freeze_gif(i) {
var c = document.createElement('canvas');
var w = c.width = i.width;
var h = c.height = i.height;
c.getContext('2d').drawImage(i, 0, 0, w, h);
try {
i.src = c.toDataURL("image/gif");
} catch(e) {
for (var j = 0, a; a = i.attributes[j]; j++)
c.setAttribute(a.name, a.value);
i.parentNode.replaceChild(c, i);
}
}
window.onload = function() {
setTimeout(function() {
[].slice.apply(document.images).filter(is_gif_image).map(freeze_gif);
}, 5000);
}
<img src="http://rubentd.com/img/banana.gif" alt="" >
I moved the first line (that cases the freeze of all the gif images) inside the setTimeout function.

Strange Behaviour of "onclick" event of a button

html:
<div id="container">
<canvas id="canvas" width="1024" height="1024"></canvas>
</div>
<button id = "click" >fire</button>
<script type="text/javascript" src = "Jquery/easy.js"></script>
javascript:
var ctx = canvas.getContext('2d');
var chop1 = new Image();
chop1.src = "img/chopper.png";
var blt = new Image();
blt.src = "img/bullet.png"
var chopperX = 0;
var chopperY = 0;
var ascent = 2;
var limit = 500;
var start = null;
var bltX = 135;
var hit = document.getElementById("click");
window.onclick = function()
{
fly()
}
hit.onclick = function()
{
fire();
}
function fire()
{
bltX +=ascent;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(blt,bltX,30,
chop1.width, chop1.height);
requestAnimationFrame(fire);
}
function up()
{
chopperY-=ascent;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(chop1,0,chopperY ,
chop1.width, chop1.height);
requestAnimationFrame(up);
if(chopperY == 0){
fly();
}
}
function fly() {
chopperY+=ascent;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(chop1,0,chopperY ,
chop1.width, chop1.height);
if (chopperY < limit) {
requestAnimationFrame(fly);
}
if(chopperY==limit){
up();
}
}
When i click on the fire button it doesn't call the "fire" method and calls the "fly" function. i want to know why "fly" method get called instead of "fire" method?
Is there any mistake in this code? what is the solution of it?
You have two event handlers
window.onclick = function() {
fly()
}
hit.onclick = function() {
fire();
}
which by the way could be written
window.onclick = fly;
etc. But the click bubbles to the window level when you click any element inside the window, even hit, so the fly() function is called as well.
You could try stopping the propagation
hit.onclick = function(event) {
event.stopPropagation();
fire();
}
There already is a solution for your problem:
see here
What you want to do is:
document.getElementById("click").onclick = fire;

Categories

Resources