I'm trying to get various progress bars for my website that load at equal speeds but of different lengths. I have tried this code but it isn't working
var prog = document.getElementsByClassName("prog");
var c = 0;
var ski = [50, 40, 35, 25, 32, 33, 20, 25];
var j;
var t = 0;
function inc() {
if (t == ski[c]) {
clearTimeout(ty);
t = 0;
} else {
t++;
prog[c].style.width = t + "%";
}
}
function ani() {
for (j = 0; j < ski.length; j++) {
var ty = setTimeout(inc, 30);
c = j;
}
}
ani();
The ski array is the length of percent for div to load after animation is complete considering 50% width as a full length of a progress bar. prog is an array of divs of small height and long width.
Please Help.
It looks like you're setting the width of each item to t which is an index in your case. You could try to set it to ski[t].
Basically you're setting each one to 1%, then 2%, etc.
You seem to be massively over-complicating the problem. You can simply loop through each of the .prog elements in the prog array, then set their width. If you want the change to be animated, set transition: width in the CSS of those elements, like this:
window.addEventListener('load', function() {
var prog = document.getElementsByClassName("prog");
var ski = [50, 40, 35, 25, 32, 33, 20, 25];
function ani() {
for (i = 0; i < prog.length; i++) {
prog[i].style.width = (ski[i] * 2) + '%';
}
}
ani();
});
.prog {
width: 0;
height: 25px;
background-color: #C00;
margin: 0 0 5px;
transition: width 0.5s;
}
<div class="prog"></div>
<div class="prog"></div>
<div class="prog"></div>
<div class="prog"></div>
<div class="prog"></div>
<div class="prog"></div>
<div class="prog"></div>
<div class="prog"></div>
Related
I'm trying to realise an infinite up and down move of the platform . How can I modify the code to get this thing ? I have only managed to have just one un-down movement . I know that I could do this with CSS animations but I would like to modify my code .
var n = 0;
var grid = document.querySelector('.grid');
function move() {
const pixels = [200, 196, 192, 188, 184, 180, 176, 172, 168, 164, 160, 164, 168, 172, 176, 180];
const style = grid.style.bottom
const computedStyle = window.getComputedStyle(grid)
console.log('bottom from computed style', computedStyle.bottom)
grid.style.bottom = pixels[n] + 'px';
n++;
}
move();
setInterval(move, 90);
.grid {
background-color: blue;
height: 20px;
width: 100px;
left: 100px;
bottom: 200px;
position: absolute;
}
<div class="grid"></div>
The function is not looping because you are not resetting n - after the first up down movement, n goes out of bounds, grid.style.bottom = pixels[n] + 'px'; tries to set the stile to undefined +'px' and fails, and the bar stays where it is.
I added n = n % pixels.length; to reset n once it goes out of bounds.
var n = 0;
var grid = document.querySelector('.grid');
function move() {
const pixels = [200, 196, 192, 188, 184, 180, 176, 172, 168, 164, 160, 164, 168, 172, 176, 180];
const style = grid.style.bottom
const computedStyle = window.getComputedStyle(grid)
console.log('bottom from computed style', computedStyle.bottom)
n = n % pixels.length;
grid.style.bottom = pixels[n] + 'px';
n++;
}
move();
setInterval(move, 90);
.grid {
background-color: blue;
height: 20px;
width: 100px;
left: 100px;
bottom: 200px;
position: absolute;
}
<div class="grid"></div>
You can have a Boolean checking once you get at the end of your array and once you do you start to decrement your n variable. This way it will go from 200px -> 180px -> 200px
let grid = document.querySelector(".grid");
let n = 0;
let bool = true
function move() {
const pixels = [200, 196, 192, 188, 184, 180, 176, 172, 168, 164, 160, 164, 168, 172, 176, 180]
const style = grid.style.bottom;
const computedStyle = window.getComputedStyle(grid);
console.log("bottom from computed style", computedStyle.bottom);
grid.style.bottom = pixels[n] + "px";
if(n === pixels.length - 1 ){
bool = false
} else if(n === 0){
bool = true
}
bool ? n++ : n--
}
move();
setInterval(move, 90);
.grid {
background-color: blue;
height: 20px;
width: 100px;
left: 100px;
bottom: 200px;
position: absolute;
}
<div class="grid"></div>
Instead of incrementing in an interval, better compute the position based on the time passed since start.
According to your pixels array and your interval, you move 40px up/down every 900ms. (10 steps of 4px / 90ms)
const start = Date.now()
function move(){
const time = Date.now() - start;
let t = time / 900; // the time passed in terms of up/down strokes
// t = t % 1; // turns this into a sawtooth pattern, just up-strokes
// not what we want, we want every other stroke to be a down-stroke.
t = t&1 ? // every other stroke
(1 - t%1) : // move down
(t%1); // otherwise mode up
// now we have out position as a percentage value 0..1;
// let's compute the pixels.
const pos = 200 - 40*t; // start at 200px and travel a fraction of 40px down.
grid.style.bottom = pos + "px";
// rAF is way smoother than your 90ms interval.
requestAnimationFrame(move);
}
move();
const DURATION = 900;
const grid = document.querySelector('.grid');
const start = 0;
function move() {
let t = (Date.now() - start) / 900;
t = t&1 ? 1-t%1 : t%1; // zig-zag
//t = t%1; // sawtooth; try this instead of the zig-zag and see/understand the difference.
//add some easing; try it.
//t = t*t*(3-2*t);
grid.style.bottom = 200 - 40*t + "px";
requestAnimationFrame(move);
}
move();
.grid {
background-color: blue;
height: 20px;
width: 100px;
left: 100px;
bottom: 200px;
position: absolute;
}
<div class="grid"></div>
I'm trying to reset a bar if my player walks on a tile (water1, water2, water3 variables). When the player collides with one of these three tiles, the bar resets. If my player didn't walk on the tiles and the bar is on 100, you get a Game Over screen.
It works so far, but the problem is, when the player collides with the water tile, the bar resets to start, but the counter to the Game Over screen didn't reset.
For example: If I reset the bar when it's at 40%, it goes back to 0, but after the bar goes another 60% I get the Game Over Screen, not after 100% like I want.
var water1 = {x: 352, y: 64, width: 32, height: 32}
var water2 = {x: 352, y: 96, width: 32, height: 32}
var water3 = {x: 384, y: 64, width: 32, height: 32}
function thirst() {
var elem2 = document.getElementById("durst");
var width = 1;
var id2 = setInterval(frame, 1000);
function frame() {
if (player.xPos < water1.x + water1.width &&
player.xPos + player.width > water1.x &&
player.yPos < water1.y + water1.height &&
player.height + player.yPos > water1.y) {
thirst();
return;
} if (player.xPos < water2.x + water2.width &&
player.xPos + player.width > water2.x &&
player.yPos < water2.y + water2.height &&
player.height + player.yPos > water2.y) {
thirst();
return;
} if (player.xPos < water3.x + water3.width &&
player.xPos + player.width > water3.x &&
player.yPos < water3.y + water3.height &&
player.height + player.yPos > water3.y) {
thirst();
return;
} if (width >= 100) {
clearInterval(id2);
} else {
width++;
elem2.style.width = width + '%';
} if(width == 100) {
location.href = '666_GameOver.html';
}
}
}
CSS
#balkenDurst {
width: 5%;
background-color: #0000ff;
z-index: 4;
position: absolute;
margin-left: 8% ;
}
#durst {
width: 0%;
height: 30px;
background-color: #ffffff;
z-index: 4;
}
HTML
<div id="balkenDurst">
<div id="durst"></div>
</div>
Maybe you have an idea what's wrong.
I know I could write it with less code, but I'm learning and I'm really glad it works so far.
Programming with objects
Okay, i see your close to your target with the collision detection and the methods you use.
There are some issues with the code, and thats for you to fix.
But i got some pointers for you.
Water tiles
In your code:
if (player.xPos < water1.x + water1.width &&
player.xPos + player.width > water1.x &&
player.yPos < water1.y + water1.height &&
player.height + player.yPos > water1.y) {
thirst();
return;
}
This is repeated multiple times. Try to avoid repeating the same code over and over.
You can easily change it to become a function you can call multiple times.
Here is an example:
function hit( tile) {
//Hits on x axis
if(player.xPos < tile.x + tile.width && player.xPos + player.width > tile.x) {
//Hits on y axis
if (player.yPos < tile.y + tile.height && player.yPos + player.height > tile.y) {
return true;
}
}
return false;
}
Note: Is it not easier to read the code now?
Now since we made it into a fuction it can take in any tile!
Like say water tiles.
//Defines our water tiles in an array. (Easier to use)
var waterTiles = [
{x: 352, y: 64, width: 32, height: 32},
{x: 352, y: 96, width: 32, height: 32},
{x: 384, y: 64, width: 32, height: 32},
];
//Checks if any of the water tiles hit
function checkHits() {
for (var i = 0; i < waterTiles.length; i++) {
//Use our method above.
var check = hit ( waterTiles[i] );
if (check) {
//Code to check if it hits.
}
}
}
This is nice and all, but you did not answer my question.
Okay so going to the thirst logic of your game.
How you are setting with with of the thirst bar is not shown in your code above.
But since your thirst bar is not reseting probably it sounds like a variable not being reset correctly.
If you have a var thirst defined and a var width of the bar. You need to change both.
So in your thirst method set the thirst level: player.thirst = 60; (example).
Then in your frame() method (usualy called "update" in game loops) get this thirt something like: var width = player.thirst
When you decrease it (each frame?) Just set the variable again: player.thirst = player.thirst - 1;
I have a task to make animation with JavaScript.
Basically I have two squares (red and yellow) and a two buttons (button 1 and button 2).
When I click on button1 the red square goes from the (top-left corner) to the (bottom-right corner).
I need to make another button (button2) such that when I click on it I need the red square to go back to the beginning.
I need it to do the opposite move (moving from the bottom-right corner to the top-left corner).
What changes should I do in the second function?
here is the code
function myMove1() {
var elem = document.getElementById("animate");
var pos = 0;
var id = setInterval(frame, 5);
function frame() {
if (pos == 350) {
clearInterval(id);
} else {
pos++;
elem.style.top = pos + 'px';
elem.style.left = pos + 'px';
}
}
}
function myMove2() {
}
#container {
width: 400px;
height: 400px;
position: relative;
background: yellow;
}
#animate {
width: 50px;
height: 50px;
position: absolute;
background-color: red;
}
<p>
<button onclick="myMove1()">button 1</button>
<button onclick="myMove2()">button 2</button>
</p>
<div id="container">
<div id="animate"></div>
</div>
I'm going to assume the teacher is trying to teach basic javascript, and tell you how I'd solve this with the parts you've provided.
That said, your commenters are correct, requestAnimationFrame is the right tool here. Also, the 5 ms delay on your interval is really short (125fps). If you made this number, I'd suggest changing it to 16, which is roughly 60fps.
// We want each function to be able to see these vars.
var pos = 0;
// Either -1, 0, or 1, depending on if were moving forward, backwards or
// stopped.
var direction = 0;
// This var now serves dual purpose, either its a number which is the
// interval id or its falsy, which we can use to understand the animation
// has stopped.
var id = null;
// Doing this here, will save the browser from having to redo this step on
// each frame.
var elem = document.getElementById("animate");
// Render the elem to the correct starting location.
elem.style.top = pos + 'px';
elem.style.left = pos + 'px';
// A single animation function.
function frame() {
// Assume we are heading for 350.
var goal = 350
if (direction < 0) {
// unless the goal is -1, when the goal is zero.
goal = 0
}
if (pos != goal) {
pos += direction;
elem.style.top = pos + 'px';
elem.style.left = pos + 'px';
} else {
// Reset all the shared vars.
direction = 0;
clearInterval(id);
id = null;
}
}
function myMove1() {
if (id) {
clearInterval(id)
}
direction = 1;
id = setInterval(frame, 5);
}
function myMove2() {
if (id) {
clearInterval(id)
}
direction = -1;
id = setInterval(frame, 5);
}
#animate {
position: absolute;
width: 10px;
height: 10px;
background-color: red;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<p>
<button onclick="myMove1()">button 1</button>
<button onclick="myMove2()">button 2</button>
</p>
<div id="container">
<div id="animate"></div>
</div>
</body>
</html>
What you're asking is straightforward: take the function you already wrote and change the increment direction on pos. The only difference is you'll need to keep track of x and y coordinates separately since they move in opposite directions. I used this object initialized to the start position of the box:
pos = {x: 350, y: 0};
function myMove1() {
var elem = document.getElementById("animate");
var pos = 0;
var id = setInterval(frame, 5);
function frame() {
if (pos == 350) {
clearInterval(id);
} else {
pos++;
elem.style.top = pos + 'px';
elem.style.left = pos + 'px';
}
}
}
function myMove2() {
var elem = document.getElementById("animate");
var pos = {x: 350, y: 0};
var id = setInterval(frame, 5);
function frame() {
if (pos.y >= 350 || pos.x <= 0) {
clearInterval(id);
} else {
pos.x--;
pos.y++;
elem.style.top = pos.y + 'px';
elem.style.left = pos.x + 'px';
}
}
}
#container {
width: 400px;
height: 400px;
position: relative;
background: yellow;
}
#animate {
width: 50px;
height: 50px;
position: absolute;
background-color: red;
}
<p>
<button onclick="myMove1()">button 1</button>
<button onclick="myMove2()">button 2</button>
</p>
<div id="container">
<div id="animate"></div>
</div>
However, these functions aren't reusable without parameters; this code is WET (wrote everything twice). The animation is brittle because each click creates a new timeout (you can spam the buttons and watch it crumble). Entities in the animation have no state. If you want to change the position or add another box, you have to we-write and duplicate all of your code again.
With that in mind, here's a sketch to illustrate a somewhat improved version as food for thought. The functions and objects are more general and don't need to be re-written for new movements you decide to add. The Box class keeps track of entity state over time. requestAnimationFrame() is used to update and draw all entities on the screen at once, avoiding the many problems with setTimeout.
const lerp = (v0, v1, t) => (1 - t) * v0 + t * v1;
const dist = (a, b) => ((a.x - b.x) ** 2 + (a.y - b.y) ** 2) ** 0.5;
class Box {
constructor(elem, pos, size, color, speed) {
this.elem = elem;
this.speed = speed;
this.from = this.to = this.pos = pos;
this.t = 0;
this.elem.style.position = "absolute";
this.elem.style.background = color;
this.elem.style.height = `${size}px`;
this.elem.style.width = `${size}px`;
this.elem.style.top = `${this.pos.y}px`;
this.elem.style.left = `${this.pos.x}px`;
}
move(to) {
this.from = {x: this.pos.x, y: this.pos.y};
this.to = {x: to.x, y: to.y};
this.t = 0;
}
update() {
if (dist(this.pos, this.to) > 1) {
this.pos.x = lerp(this.from.x, this.to.x, this.t);
this.pos.y = lerp(this.from.y, this.to.y, this.t);
this.elem.style.top = `${this.pos.y}px`;
this.elem.style.left = `${this.pos.x}px`;
this.t += this.speed;
}
}
}
const data = [
{color: "red", pos: {x: 0, y: 0}, size: 10},
{color: "yellow", pos: {x: 350, y: 0}, size: 10},
];
const elems = document.getElementsByClassName("box");
const boxes = [];
for (let i = 0; i < elems.length; i++) {
boxes.push(new Box(elems[i], data[i].pos, data[i].size, data[i].color, 0.01));
}
function myMove1() {
boxes[0].move({x: 350, y: 350});
boxes[1].move({x: 0, y: 350});
}
function myMove2() {
boxes[0].move({x: 0, y: 0});
boxes[1].move({x: 350, y: 0});
}
(function render() {
boxes.forEach(e => e.update());
requestAnimationFrame(render);
})();
<p>
<button onclick="myMove1()">button 1</button>
<button onclick="myMove2()">button 2</button>
</p>
<div id="container">
<div class="box"></div>
<div class="box"></div>
</div>
Lastly, consider using CSS animations, JS canvas or an animation framework to do this sort of task; these tools will abstract away a lot of the math and state representation that animations involve.
If the left half of the screen is a specific rgb value, and the right is another, how would one smoothly transition depending on mouse position between those two on mousemove?
So that when the mouse in at the far left, it's one colour, and far right it's the other. In the middle it would be the half way point.
Something like this: http://jsfiddle.net/j08691/BrZjJ/ - But based on set colours, not arbitrary ones.
var $el = $('div');
var p_x, p_y,
window_width = window.innerWidth,
window_height = window.innerHeight;
var m = {
moving_left: false,
moving_up: false,
last_x: 0,
last_y: 0,
x: 0,
y: 0
};
var colors = {
orange: { r: 238, g: 119, b: 0 },
blue: { r: 40, g: 203, b: 215 }
};
$el.on('mousemove', function(e){
m.x = e.pageX;
m.y = e.pageY;
m.moving_left = (m.x < m.last_x) ? true : false;
m.moving_up = (m.y < m.last_y) ? true : false;
m.last_x = m.x;
m.last_y = m.y;
});
function animateBackground() {
p_width = m.x / window_width;
p_height = m.y / window_height;
p_x = p_width.toFixed(2);
p_y = p_height.toFixed(2);
switch(true) {
// top left
case p_x <= 0.5 && p_y <= 0.5:
fr = colors.orange.r;
fg = colors.orange.g;
fb = colors.orange.b;
break;
// bottom right
default:
fr = colors.blue.r;
fg = colors.blue.g;
fb = colors.blue.b;
break;
}
$el.css({
backgroundColor: 'rgb('+fr+', '+fg+', '+fb+')'
});
requestAnimationFrame(animateBackground);
}
requestAnimationFrame(animateBackground);
https://jsfiddle.net/o32juay5/
I'm using something similar to the above, (except using 4 colours on four corners, but for simplicity...). I just can't work out in my head how to transition between two colours...
Any ideas?
Thanks.
You need to calculate the colors mathematically according to the mouse position.
For each of the R, G, B values: Take values for the two colors, determine the range between them, select a position inside that range according to the mouse position, and combine them into a background-color to be used in CSS.
https://jsfiddle.net/kbpyxxcn/6/
If you want to use this method with four colors, your calculations will become more complicated, unless you also define a color for the center.
Add the CSS property transition and it works:
https://jsfiddle.net/o32juay5/2/
div {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
-ms-transition: background-color 1s;
-webkit-transition: background-color 1s;
transition: background-color 1s;
}
If you make it with javascript the performance will be less than this css property.
first of all i want to say that the following function is by chance and by this i mean that its functioning is very strange:
function x (e,s,v){ // e =element, s = desired size of an element, v = speed
var div = document.getElementById(e),
width = 0;
for(var i =0; width<s; i++){
if(i%v === 0){
width = width+1;
div.setAttribute('style', 'width:'+width+'px;');
}else{
width = width +0;
}
};
};
this function is working perfectly and doing what i want but the problem is that the width is changing at once when the working of this function is finished.
in detail
i want that the width of an element increase smoothly, increase 'one' by 'one' px. so i made this function.
there is an if statement because if i didn't put that there then the width of that element would increase at once. that if statement delay the time between the adding of two pixels
but now the problem is that it is adding pixels one by one but the width is increasing at once after the completion of the function.
for example if i write in console x('aynElement', 500, 100) then it is adding pixels one by one but the width of the element is increasing at once when the function stop functioning
you can see this yourself in console
link to JsFiddle for full code
secondly
the problem is that this is strange. you had absolutely felt weird after reading this function. please anyone explain me this weirdness.
thanks
You can use setTimeout() to do this.
function x(e, s, v) { // e =element, s = size of an element, v = speed
var div = document.getElementById(e),
width = 0;
for (var i = 0; i < (s - width); i++) {
setTimeout(function() {
div.setAttribute('style', 'width:' + width+++'px;');
}, i * (1 / (v * 0.01)));
};
};
x('d1', 400, 10)
#d1 {
width: 100px;
height: 100px;
background-color: #aaa;
}
<body>
<div id="d1" style="width:200px;"></div>
</body>
I think this would solve it better (might need the one or other adjustment, just a quick scrape):
DEMO
setInterval(function(){
grow('id',250) //set your parameters here
}, 100); //this is the speed - the lower the quicker
var width = 1;
function grow(e,s) {
document.getElementById('d1').style.width = width+"px";
width++;
};
setInterval(function(){
grow('id',500)
}, 10);
var width = 250;
function grow(e,s) {
document.getElementById('d1').style.width = width+"px";
width++;
};
#d1{
height: 100px;
background-color: #000;
}
<body>
<div id="d1" ></div>
</body>