Cannot change element.style.height value with loop - javascript

The code only changes the element's height value at the end of the loop, which means no animation :,(
The code is like following:
function heightenElement(id,interval,step,height,unit) {
y = 0;
function loop() {
y = y + step;
thing = y + unit;
console.log(y);
if (y < height) {
document.getElementById(id).style.height = thing;
setTimeout(loop(),interval);
}
}
loop();
}
An answer would be appreciated :)

setTimeout accepts a function as a parameter - if you put a function call in there, the function will be executed immediately. Simply pass the function name instead:
function heightenElement(id, interval, step, height, unit) {
let y = 0;
function loop() {
y = y + step;
thing = y + unit;
console.log(y);
if (y < height) {
document.getElementById(id).style.height = thing;
setTimeout(loop, interval);
}
}
loop();
}
heightenElement('div', 500, 10, 100, 'px');
div {
height: 30px;
background-color: yellow;
}
<div id="div">
</div>

Related

P5js: why is my bg framerate not refreshing properly?

I have 2 sets of code in here. One is the simple bounce off code. The other is a function. I've tried to make it a function but it doesn't seem to be working properly.
The bg framerate doesn't clear in the sense that a string of balls show rather than a ball bouncing and animating.
if(this.y_pos > 400) this condition doesn't seem to be working even tho it works when it is drawn in the draw function.
var sketch = function (p) {
with(p) {
p.setup = function() {
createCanvas(800, 600);
// x_pos = 799;
// y_pos = 100;
// spdx = -random(5,10);
// spdy = random(12,17);
};
p.draw = function() {
background(0);
// fill(255);
// ellipse(x_pos,y_pos,50);
// x_pos += spdx;
// y_pos += spdy;
// if(y_pos > 400)
// {
// spdy *= -1;
// }
// for( var i = 0; i < 10; i++)
avalance(799, 100, random(5,10), random(12,17));
};
function avalance(x, y, spdx, spdy)
{
this.x_pos = x;
this.y_pos = y;
this.spdx = spdx;
this.spdy = spdy;
this.x_pos = 799;
this.y_pos = 100;
this.spdx = 1;
this.spdy = 1;
this.movement = function()
{
this.x_pos += -spdx;
this.y_pos += spdy;
if(this.y_pos > 400)
{
this.spdy *= -1;
}
}
this.draw = function()
{
this.movement();
this.drawnRox();
}
this.drawnRox = function()
{
fill(255);
ellipse(this.x_pos,this.y_pos,50);
}
}
}
};
let node = document.createElement('div');
window.document.getElementById('p5-container').appendChild(node);
new p5(sketch, node);
body {
background-color:#efefef;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.js"></script>
<div id="p5-container"></div>
Let's address both issues:
The draw() function is called for each new frame and in it you call avalance which creates a new ball. To fix that you need to
Create a global variable let ball; out of setup() and draw() so that you can reuse that later;
In setup create a new ball and assign it to your ball variable: ball = new avalance(799, 100, random(5,10), random(12,17));
In draw() you want to update the ball and that's what its own draw() function does (I would advise renaming it update() for example, to avoid confusion with the p5 specific draw() function). So you just need to call ball.draw() in draw().
This creates a ball which moves but still don't respect your 400px limit.
The issue is that in movement() you add spdx and spdy to the position but when the ball crosses the limit you update this.spdy, so you need to update the function with this code:
this.x_pos += -this.spdx;
this.y_pos += this.spdy;
And you should be good! Here is a code pend with your code working as you intend.
Also as a bonus advise: You probably want to use some p5.Vector objects to store positions, speeds and accelerations it really makes your code easier to read and to use. You could also rename your function Avalance (capital A) to show that you actually use a class and that this function shouldn't be called without new.
As #statox suggested, do new avalance(799, 100, random(5,10), random(12,17)) in the setup() section and call draw of the ball in draw() section.
You can test the code below by clicking "Run code snippet".
var sketch = function (p) {
with(p) {
var ball;
p.setup = function() {
createCanvas(800, 600);
ball = new avalance(799, 100, random(5,10), random(12,17));
};
p.draw = function() {
background(0);
ball.draw();
};
function avalance(x, y, spdx, spdy)
{
function movement ()
{
x += -spdx;
y += spdy;
if (y > height || y < 0)
{
spdy *= -1;
}
if (x > width || x < 0)
{
spdx *= -1;
}
}
this.draw = function()
{
movement();
drawnRox();
}
function drawnRox ()
{
fill(255);
ellipse(x,y,50);
}
}
}
};
let node = document.createElement('div');
window.document.getElementById('p5-container').appendChild(node);
new p5(sketch, node);
body {
background-color:#efefef;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.js"></script>
<div id="p5-container"></div>

Add multiple times image dropping down the screen (video game js)

So I am building a video game where some fireball drop down the screen. However, there is only one image crossing the screen at a time. I would like that the image is actually multiplied an number of times. To get an idea of what I am saying, here is an image:
But what I would like to do is instead of only having one image (fireball) going down the screen, I would like to have a bunch of images dropping down the screen.
Here is the code for the fireball:
//Fireball script
function fFireball(offset) {
return Math.floor(Math.random() * (window.innerWidth - offset))
}
let fireball = {x: fFireball(fireballElement.offsetWidth), y: 0}
const fireLoop = function() {
fireball.y += 2; fireballElement.style.top = fireball.y + 'px'
if (fireball.y > window.innerHeight) {
fireball.x = fFireball(fireballElement.offsetWidth)
fireballElement.style.left = fireball.x + 'px'; fireball.y = 0
}
}
fireballElement.style.left = fireball.x + 'px'
let fireInterval = setInterval(fireLoop, 1000 / 100)
And the image:
<img src="Photo/fireball.png" id="fireball">
Thanks!
Use document.createElement()
Demo : https://jsfiddle.net/hexzero/ukh1dpwn/
I wrote down several comments in the code below to help you understand it better. If you have any additional question don't hesitate to ask.
const fireballArray = [];
// You can add any additional attributes you need for your fire balls here like ids and class names.
function generateFireBallWithAttributes(el, attrs) {
for (var key in attrs) {
el.setAttribute(key, attrs[key]);
}
return el;
}
function createFireBalls(amount){
for (let i = 0; i <= amount; i++) {
fireballArray.push( // create an image element
generateFireBallWithAttributes(document.createElement("img"), {
src: "Photo/fireball.png",
width: "32",
height: "32",
})
);
}}
createFireBalls(10)
fireballArray.forEach((fireballElement) => {
// Just add the id of the game body here, so that you could append your fire balls
document.getElementById("game-body").appendChild(fireballElement);
const fireball = { x: fFireball(fireballElement.offsetWidth), y: 0 };
const fireLoop = function () {
fireball.y += 2;
fireballElement.style.top = fireball.y + "px";
if (fireball.y > window.innerHeight) {
fireball.x = fFireball(fireballElement.offsetWidth);
fireballElement.style.left = fireball.x + "px";
fireball.y = 0;
}
};
fireballElement.style.left = fireball.x + "px";
// I've randomised the interval
// you may want to implement your own version to get more control
let fireInterval = setInterval(fireLoop, 1000 / ((Math.random() * (125 - 75)) + 75));
});
function fFireball(offset) {
return Math.floor(Math.random() * (window.innerWidth - offset));
}
img {
position: absolute;
}
I got inspired by this post Setting multiple attributes for an element at once with JavaScript, when adding extra attributes. If you like it please show them some love.
You can simply create a new <img> element for each bullet
I created a snippet doing it the way you were doing it. However, it would be more efficient if you had a single loop that updates all of the images at once.
const create = (x, y, vx, vy) => {
const object = {
x,
y,
vx,
vy,
el: document.createElement("img")
}
object.el.src = "http://placehold.it/50x50";
object.el.style.left = `${object.x}px`;
object.el.style.top = `${object.y}px`;
document.body.appendChild(object.el);
const intervalID = setInterval(() => {
object.x += object.vx;
object.y += object.vy;
object.el.style.left = `${object.x}px`;
object.el.style.top = `${object.y}px`;
if (object.y > window.innerHeight) {
document.body.removeChild(object.el);
clearInterval(intervalID)
}
}, 20);
}
setInterval(() => {
const randX = Math.floor(Math.random() * (window.innerWidth - 50));
create(randX, -50, 0, 2);
}, 500);
body {
margin: 0;
overflow: hidden;
}
img {
position: absolute;
}

Trigger a javascript function when button is clicked and stop when clicked again

So I wish to add an effect to my website of floating particles. Came across this codepen which does the exact thing - https://codepen.io/OliverKrieger/pen/EjLEVX.
I've been trying to change it a bit so that the particles only trigger when a checkbox is checked. I understand it might've been already answered on SO but in the codepen code, there is an window.onload function which automatically fires it when window is loaded. I want there to be a checkbox instead.
I tried putting the following in html -
<input type="checkbox" id="switch" onchange="function()"">
<label for="switch" >Toggle</label>
But it seems like the script still triggers automatically. Can someone help me with this please? I'm new to programming so I apologize for any vagueness. Please lemme know if I can provide more details. Any input would be really appreciated.
In your code, function is a keyword and not a function name. The function is still run as soon as the page loads because of the way it is defined in the javascript, and your HTML addition has no effect on this.
You need to change your function into a named function, declared using the function keyword:
function functionName() { ... }
And then you can call it by its name in your HTML:
<input type="checkbox" id="switch" onchange="functionName()">
This should work
(function() {
var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
window.requestAnimationFrame = requestAnimationFrame;
})();
$(window).resize(function() {
ParticleCanvas.width = ($(window).width() - 20);
ParticleCanvas.height = ($(window).height() - 10);
});
$(function() {
let request = null;
$("#switch").change(function() {
if (this.checked) {
animateDust();
} else {
window.cancelAnimationFrame(request);
}
})
var ParticleCanvas = document.getElementById("ParticleCanvas");
var context = ParticleCanvas.getContext("2d");
ParticleCanvas.width = ($(window).width() - 20);
ParticleCanvas.height = ($(window).height() - 10);
$("switch").click(function() {
animateDust();
})
// All the info stored into an array for the particles
var particles = {},
particleIndex = 0,
settings = {
density: 20,
particleSize: 2,
startingX: ParticleCanvas.width / 2,
startingY: ParticleCanvas.height,
gravity: -0.01
};
// Set up a function to create multiple particles
function Particle() {
// Establish starting positions and velocities from the settings array, the math random introduces the particles being pointed out from a random x coordinate
this.x = settings.startingX * (Math.random() * 10);
this.y = settings.startingY;
// Determine original X-axis speed based on setting limitation
this.vx = (Math.random() * 2 / 3) - (Math.random() * 3 / 3);
this.vy = -(Math.random() * 5 / 3);
// Add new particle to the index
// Object used as it's simpler to manage that an array
particleIndex++;
particles[particleIndex] = this;
this.id = particleIndex;
this.life = 0;
this.maxLife = 200;
this.alpha = 1;
this.red = 0;
this.green = 255;
this.blue = 255;
}
// Some prototype methods for the particle's "draw" function
Particle.prototype.draw = function() {
this.x += this.vx;
this.y += this.vy;
// Adjust for gravity
this.vy += settings.gravity;
// Age the particle
this.life++;
this.red += 2;
this.alpha -= 0.005;
// If Particle is old, it goes in the chamber for renewal
if (this.life >= this.maxLife) {
delete particles[this.id];
}
// Create the shapes
context.clearRect(settings.leftWall, settings.groundLevel, ParticleCanvas.width, ParticleCanvas.height);
context.beginPath();
context.fillStyle = "rgba(" + this.red + ", " + this.green + ", " + this.blue + ", " + this.alpha + ")";
// Draws a circle of radius 20 at the coordinates 100,100 on the ParticleCanvas
context.arc(this.x, this.y, settings.particleSize, 0, Math.PI * 2, true);
context.closePath();
context.fill();
}
function animateDust() {
context.clearRect(0, 0, ParticleCanvas.width, ParticleCanvas.height);
// Draw the particles
for (var i = 0; i < settings.density; i++) {
if (Math.random() > 0.97) {
// Introducing a random chance of creating a particle
// corresponding to an chance of 1 per second,
// per "density" value
new Particle();
}
}
for (var i in particles) {
particles[i].draw();
}
request = window.requestAnimationFrame(animateDust);
}
})
body {
background-color: #000000;
color: #555555;
margin: 0;
padding: 0;
}
#ParticleCanvas {
border: 1px solid white;
position: absolute;
z-index: -1;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
}
#container {
background: white;
padding: 1rem;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="ParticleCanvas"></canvas>
<div id="container">
<form>
<input type="checkbox" id="switch" name="switch">
</form>
<label for="switch">Toggle</label>
</div>

How to change the time in setInterval with an user input?

I want to know a way to change the setInterval time so my image would move across the screen at that pace. Example if i put in 500 milliseconds it would change the time interval from 250 to 500 when i click a button. Here is what I came up with so far.
var x;
var y;
var timing = 1000;
function window_onLoad() {
x = 0;
y = 100;
window.setInterval("MoveBall()", timing);
picBall.style.top = y + "px";
}
function MoveBall() {
x = x + 5;
if (x < document.body.clientWidth - 91) {
picBall.style.left = x + "px";
}
}
function btnReset_OnClick() {
x = 0;
}
function btnSpeed_OnClick() {
timing = parseInt(txtSpeed.value);
}
window_onLoad()
<img id="picBall" src="Face.jpg" style="position: absolute;"/>
<input id="btnReset" type="button" value="Reset position"
onclick="btnReset_OnClick()"/>
<input id="txtSpeed" type="text"/>
<input id="btnSpeed" type="button" value="Change Speed"
oclick="btnSpeed_onClick()"/>
I suggest not to mix moving speed with framerate (your setInterval speed). You can have a fixed framerate and variable speed. E.g.
var speed = 1, timer, x,y;
function window_onLoad() {
x = 0;
y = 100;
window.setInterval("MoveBall()", 100); // 10 frames per second
picBall.style.top = y + "px";
}
function MoveBall() {
x = x + speed;
if (x < document.body.clientWidth - 91) {
picBall.style.left = x + "px";
}
}
function btnReset_OnClick() {
x = 0;
}
function btnSpeed_OnClick() {
/*
speed = 200 will move tbe ball by 20px per sec
speed = 100 will move the ball by 10px per sec
speed = 50 will move the ball by 5px per sec
*/
speed = parseInt(txtSpeed.value)/100;
}
Your main issue is that you need to clear the previous interval and create a new one. However, I'd recommend moving your code that creates the interval into another function like this...
function window_onLoad() {
x = 0;
y = 100;
createInterval(timing);
picBall.style.top = y + "px";
}
var intervalId = 0;
// this will destroy any existing interval and create a new one
function createInterval(interval) {
clearInterval(intervalId);
intervalId = setInterval(MoveBall, interval);
}
function btnSpeed_OnClick() {
timing = parseInt(txtSpeed.value);
createInterval(timing);
}
You have to save a reference to interval and then, every time you want to change the speed you have to clear the previous interval with clearInterval and then apply a new interval, like this:
var x;
var y;
var timing = 1000;
var interval;
function window_onLoad() {
x = 0;
y = 100;
applyInterval();
picBall.style.top = y + "px";
}
function applyInterval() {
if (interval) {
console.log('Clearing previous interval');
clearInterval(interval);
}
console.log('Applying new interval');
interval = window.setInterval("MoveBall()", timing);
}
function MoveBall() {
x = x + 5;
if (x < document.body.clientWidth - 91) {
picBall.style.left = x + "px";
}
}
function btnReset_OnClick() {
x = 0;
}
function btnSpeed_OnClick() {
timing = parseInt(txtSpeed.value);
applyInterval();
}
window_onLoad()
<img id="picBall" src="https://i.stack.imgur.com/vnucx.png?s=64&g=1" style="position: absolute;" width="25" height="25"/>
<input id="btnReset" type="button" value="Reset position"
onclick="btnReset_OnClick()"/>
<input id="txtSpeed" type="text"/>
<input id="btnSpeed" type="button" value="Change Speed"
onclick="btnSpeed_OnClick()"/>

p5.js mousePressed line animation

In p5.js I'm trying to create an animation where the bottom point of the lines smoothly (5px per frame) move to the bottom left corner of the canvas when the mouse is pressed. I can get them to the point they need to get to with lines.align but they instantly move there instead of animating.
let lines = []
function setup() {
createCanvas(600, 400);
for(let i = 0; i < 150; i++) {
lines[i] = new Line(random(600), 0, random(600), 400, random(149,212), random(89, 146), 1);
}
}
function draw() {
background(32, 44, 57);
for(let i = 0; i < lines.length; i++){
lines[i].show();
}
}
function mousePressed() {
for(let i = 0; i < lines.length; i++){
lines[i].align();
}
}
class Line {
constructor(xStart, yStart, xFinish, yFinish, g, b, w) {
this.xStart = xStart;
this.yStart = yStart;
this.xFinish = xFinish;
this.yFinish = yFinish;
this.g = g;
this.b = b;
this.w = w;
}
show() {
stroke(242, this.g, this.b);
strokeWeight(this.w);
line(this.xStart, this.yStart, this.xFinish, this.yFinish);
}
align() {
while (this.xFinish > 0) {
this.xFinish = this.xFinish - 5;
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/p5.js"></script>
You're using a blocking while loop which is why visually you see a jump from the lines' start to end positions.
One option is to use a condition and increment the value in draw():
let lines = []
function setup() {
createCanvas(600, 400);
for(let i = 0; i < 150; i++) {
lines[i] = new Line(random(600), 0, random(600), 400, random(149,212), random(89, 146), 1);
}
}
function draw() {
background(32, 44, 57);
for(let i = 0; i < lines.length; i++){
lines[i].align();
lines[i].show();
}
}
class Line {
constructor(xStart, yStart, xFinish, yFinish, g, b, w) {
this.xStart = xStart;
this.yStart = yStart;
this.xFinish = xFinish;
this.yFinish = yFinish;
this.g = g;
this.b = b;
this.w = w;
}
show() {
stroke(242, this.g, this.b);
strokeWeight(this.w);
line(this.xStart, this.yStart, this.xFinish, this.yFinish);
}
align() {
if (this.xFinish > 0) {
this.xFinish = this.xFinish - 5;
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/p5.min.js"></script>
You might want to also check out lerp() to interpolation between start and end positions.
Additionally, if you need more control over timing/easing/etc you can use something like TweenLite to do the interpolation for you.
(It is a good exercise though to get a hang of incrementing values/interpolating/tweening manually first through)

Categories