I've seen a couple answers on how to delay the animation in CSS, but I was hoping to do it in Javascript.
I have an animation that activates as soon as the page loads. The website is a single page and I want the animation to activate when clients scroll to that section. I heard that you could do this by activating the animation X pixels from the bottom of the viewport.
Thanks
Here is a little overkill but it should give you all the concepts you could want for what you're trying to achieve.
Basically, you test that the point you want to start your animation from is inside the rectangle of the screen, and use a flag to prevent the animation repeating itself for each scroll after you're in the region.
// teach JavaScript about coördinates
function CoOrdinate(x, y) {
this.x = x;
this.y = y;
}
CoOrdinate.prototype.inside = function (A, D) {
if (this.x < A.x || D.x < this.x) return false;
if (this.y < A.y || D.y < this.y) return false;
return true;
}
// write a factory for the flag + action test
function actionFactory(hotspot, action) {
var flag_done = false,
test = function (topleft, bottomright) {
if (flag_done) return; // cancel if we've done it before
if (hotspot.inside(topleft, bottomright)) {
flag_done = true;
action();
}
};
return test;
}
// create your action with the factory
var someAction = actionFactory(
new CoOrdinate(0, 100), // the coördinate to be in the screen
function () {console.log('foo')} // pass animation invocation here
);
// so far none of this is getting invoked, so
// make the stuff all work when the page is scrolled
window.addEventListener('scroll', function () {
var x = window.pageXOffset,
y = window.pageYOffset,
h = window.screen.availHeight,
w = window.screen.availWidth;
someAction(
new CoOrdinate(x , y ),
new CoOrdinate(x + w, y + h)
);
});
The above example just logs "foo" if the coördinate [0, 100] is in the screen.
Related
I'm still pretty new to this, so I don't know how to create a collider. My end goal is to have a game like the chrome dinosaur game. Same principles, and all. My question is, though, how do I even make a collider. I will be using a .gif for the "dinosaur". I'd like to make it where if this collider were to touch another collider, the game stops and a "game over" is shown. I have tried to create a collider, but they just keep showing up underneath the screen where the game is shown. Ant tips, tricks, or advice? Thanks
Code is as follows:
let img; //background
var bgImg; //also the background
var x1 = 0;
var x2;
var scrollSpeed = 4; //how fast background is
let music; //for music
let catBus; //catbus
//collider variables
let tinyToto;
let tiniestToto;
let hin;
let totoWithBag;
let noFace;
let happySoot;
var mode; //determines whether the game has started
let gravity = 0.2; //jumping forces
let velocity = 0.1;
let upForce = 7;
let startY = 730; //where cat bus jumps from
let startX = 70;
let totoX = 900;
let totoY = 70;
let tinToX = 900;
let tinToY = 70;
var font1; //custom fonts
var font2;
p5.disableFriendlyErrors = true; //avoids errors
function preload() {
bgImg = loadImage("backgwound.png"); //importing background
music = loadSound("catbus theme song.mp3"); //importing music
font1 = loadFont("Big Font.TTF");
font2 = loadFont("Smaller Font.ttf");
//tinyToto.setCollider("rectangle",0,25,75,75)
}
function setup() {
createCanvas(1000, 1000); //canvas size
img = loadImage("backgwound.png"); //background in
x2 = width;
music.loop(); //loops the music
catBus = {
//coordinates for catbus
x: startX,
y: startY,
};
/*
tinyToto = {
x: totoX,
y: totoY,
}
tinTo = {
x : tinToX,
y: tinToY,
}
*/
catGif = createImg("catgif.gif"); //creates catbus
catGif.position(catBus.x, catBus.y); //creates position
catGif.size(270, 100); //creates how big
/*
tinyToto = createImg("TinyToto.gif")
tinyToto.position(tinyToto.x, tinyToto.y)
tinyToto.size(270,100)
tiniestTo = createImg("tiniest Toto.gif")
tiniestTo.position(tinToX.x, tinToY.y)
tiniestTo.size(270,100)
*/
mode = 0; //game start
textSize(50); //text size
}
function draw() {
let time = frameCount; //start background loop
image(img, 0 - time, 0);
image(bgImg, x1, 2, width, height);
image(bgImg, x2, 2, width, height);
x1 -= scrollSpeed;
x2 -= scrollSpeed;
if (x1 <= -width) {
x1 = width;
}
if (x2 <= -width) {
x2 = width;
} //end background loop
fill(128 + sin(frameCount * 0.05) * 128); //text colour
if (mode == 0) {
textSize(20);
textFont(font1);
text("press SPACE to start the game!", 240, 500); //what text to type
}
fill("white");
if (mode == 0) {
textSize(35);
textFont(font2);
text("CATBUS BIZZARE ADVENTURE", 90, 450); //what text to type
}
catBus.y = catBus.y + velocity; //code for jumping
velocity = velocity + gravity;
if (catBus.y > startY) {
velocity = 0;
catBus.y = startY;
}
catGif.position(catBus.x, catBus.y);
//setCollider("tinyToto")
}
function keyPressed() {
if (keyCode === 32 && velocity == 0) {
//spacebar code
mode = 1;
velocity += -upForce;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
well, this is how I would generally do that kind of thingy:
function draw(){
for(let i in objects) // objects would be cactuses or birds
if(objects[i].x > player.x &&
objects[i].x < player.x + player.width &&
objects[i].y > player.y &&
objects[i].y < player.y + player.height){
noLoop()
// maybe do something else here
} // you could also use: for(let object of objects)
}
or if you want to do class stuff:
let player = new Player()
class Entity {
hasCollided_pointRect(_x, _y, _width, _height){
if(this.x > _x &&
this.x < _x + _width &&
this.y > _y &&
this.y < _y + _height){
return true
}
}
}
class Cactus extends Entity {
update(){
if(hasCollided_pointRect(player.x, player.y, player.width, player.height))
lossEvent()
}
}
class Player {
// ...
}
function lossEvent(){
noLoop()
}
this is a pretty classy way to do it and for a small game you really don't need all of this
also MDN has a nice article on rect with rect & point with rect collisions,
point with point collision is just (x == x && y == y)
https://developer.mozilla.org/en-US/docs/Games/Techniques/2D_collision_detection
this is one of my recent loss "functions":
if(flag.health <= 0){
noLoop()
newSplashText("You lost!\nPress F5 to restart!", "center", "center", 1)
}
The way I handled game states in my Processing games was by making seperate classes for them. Then my main sketch's draw function looked something like
fun draw()
{
currentState.draw();
}
Each gamestate then acted as their own sketches (for example a menu screen, playing, game over, etc), and had a reference to the main sketch which created the states. They would then alter the main's currentState to, i.e., a new GameOverState() etc. where needed.
For now, don't worry about doing that too much if all you want a really simple gameoverscreen with an image and some text.
I would suggest a structure like this instead. Use this pseudocode in your main draw function:
fun draw()
{
if (gameOver)
{
// show game over screen
img(gameOver);
text("game over!");
// skip rest of the function
return;
}
// normal game code goes here
foo();
bar();
// update game over after this frame's game code completes
gameOver = checkGameOver();
}
Now you need a way of checking for a collision to determine the result of checkGameOver()
For the collision handling, check out Jeffrey Thompson's book/website on collision handling. It's an amazing resource, I highly recommend you check it out.
From the website I just linked, here's an excerpt from the website talking about handling collisions between 2d rectangles.
And here's a modified version of the collision handling function listed there (I updated the variable names to be a little more intuitive)
boolean rectRect(float rect1X, float rect1Y, float rect1Width, float rect1Height, float rect2X, float rect2Y, float rect2Width, float r2h)
{
// are the sides of one rectangle touching the other?
if (rect1X + rect1Width >= rect2X && // r1 right edge past r2 left
rect1X <= rect2X + rect2Width && // r1 left edge past r2 right
rect1Y + rect1Height >= rect2Y && // r1 top edge past r2 bottom
rect1Y <= rect2Y + r2h)
{ // r1 bottom edge past r2 top
return true;
}
return false;
You can use that function in your checkGameOver() function which would return a bool depending on whether your collision criteria are met.
For your game, you would loop over every obstacle in your game and check whether the dino and the obstacle overlap.
Pseudocode:
boolean checkGameOver()
{
foreach (Obstacle obstacle in obstacles)
{
if (rectRect(dino, obstacle))
{
return true;
}
}
return false;
}
I need to divide screen to 20 parts (horizontally) and set the value of mouse position from 1 to 20 to update sprite background-image position (for a smooth rotation animation). The code below is working, but there is a problem, when I move mouse very fast - than it can skip a few points, and I need to always change the number by one step. How can I achieve that?
https://codepen.io/kgalka/pen/vbpoqe
var frames = 20;
var frameWidth = Math.round(window.innerWidth / frames);
var xIndex;
function updatePosition(x) {
if (xIndex != x) {
xIndex = x;
document.getElementById('val').innerText = xIndex;
}
}
document.onmousemove = function(e) {
updatePosition(Math.round(e.clientX / frameWidth));
}
Ok i saw th example and i think that i understand the problem and here is how i would fix this.
Have a look and let me know if it work.
var frames = 20;
var frameWidth = Math.round(window.innerWidth / frames);
var xIndex;
var timeout;
function updatePosition(x) {
if (xIndex != x) {
xIndex = x;
document.getElementById('val').innerText = xIndex;
}
}
document.onmousemove = function(e) {
// clear the prev call if the mouse was to fast.
clearTimeout(timeout);
// Now ini new call to updatePosition
timeout= setTimeout(()=> updatePosition(Math.round(e.clientX / frameWidth)), 1 )
// you could play with the timeout and increase it from 1 to 100ms. and see what you prefere
}
<p id="val"></p>
I'm new in JavaScript and I'm creating a kind of shooting game. I'm to make 1 object to move toward another object. So the "bullet" get the location of the "prey" and it will move toward it. I have no idea how to implement that, I can't find anything similar on the internet. So, I tried at first something simpler:
In the following code the "bullet" move to the left. How can I specify to move it toward an object?
I have 2 object. It's the enemyBullet object(not the bullet object) which should go toward another object.
PS: English is not my native language. Sorry for any confusion
thanks.
this.draw = function () {
this.context.clearRect(this.x + 2, this.y + 1.5, this.width - 4.5, this.height);
//x+ the speed make it go to the left
this.x += this.speed;
if (self === "bullet" && this.x >= this.canvasWidth) {
return true;
}
else if (self === "enemyBullet" && this.x >= 1000) {
console.log(this.x);
return true;
}
else {
if (self === "bullet") {
this.context.drawImage(imageRepository.bullet, this.x, this.y);
}
else if (self === "enemyBullet") {
this.context.drawImage(imageRepository.enemyBullet, this.x, this.y);
}
return false;
}
};
Normalised vector
You need to find the normalised vector from one object to the next. A vector is just an arrow that has a direction and a length. In this case we normalise the length, that is make it equal to 1 unit long. We do this so we can easily set a speed when using the vector.
Function to return a vector from one point to the next
// Code is in ES6
// fx, fy is from coordinate
// tx, ty is to coordinate
function getNormVec(fx, fy, tx, ty){
var x = tx - fx; // get differance
var y = ty - fy;
var dist = Math.sqrt(x * x + y * y); // get the distance.
x /= dist; // normalised difference
y /= dist;
return {x,y};
}
Once you have the vector you can move an object by adding the vector times the speed. Example of creating and moving a bullet from myObj to myTarget
var myObj = {}
var myTarget = {};
var myBullet = {}
myObj.x = 100;
myObj.y = 100;
myTarget.x = 1000
myTarget.y = 1000
var vecToTag = getNormVect(myObj.x, myObj.y, myTarget.x, myTarget.y);
myBullet.nx = vecToTag.x; // set bullets direction vector
myBullet.ny = vecToTag.y;
myBullet.x = myObj.x; // set the bullet start position.
myBullet.y = myObj.y;
myBullet.speed = 5; // set speed 5 pixels per frame
To move the bullet
myBullet.x += myBullet.nx * myBullet.speed;
myBullet.y += myBullet.ny * myBullet.speed;
I'm pretty close to finish this program using Canvas. This program is simply a ball that falls down from top to bottom and there's a basket that catches it, that is it. However, I have the following issues.
1) When I press the left or right arrows from keyboard for more than couple of times somehow the basket will go all the way to either left or right and disappear.
2) When the ball hits the basket nothing happens (my Collision detection function doesn't work properly). However, I should say that my collision detection works just fine when the balls hits the ground (alert message shows up saying "Ball hit the ground").
Is there a way to show a message on the top of the canvas like "1 point" every time the basket catches a ball ( if there are 5 balls then I should get a message to say "5 points")
Can someone tell me what I am doing wrong please? Thank you so much in advance!!
LIVE CODE HERE
http://codepen.io/HenryGranados/pen/QNOZRa
Here's my code :
//create the constructor for the class pad
function Pad() {
//initialisation code will go here
//create private variables for the x and y coordinates
var x = 200,
y = 200,
vx = 0,
vy = 0,
padX = (canvas.width - 20) / 2;
rightPressed = false,
leftPressed = false;
//create the draw function to give us the draw method
//it accepts one parameter which is the context from the canvas it is drawn on
Pad.prototype.draw = function (context) {
//save the state of the drawing context before we change it
context.save();
//set the coordinates of the drawing area of the new shape to x and y
context.translate(x, y);
//start the line (path)
context.beginPath();
context.fillStyle = "#800000"; // This is the basket
context.moveTo(15, 20);
context.bezierCurveTo(20, 100, 150, 100, 150, 20);
//close the path
context.closePath();
context.fill();
//go ahead and draw the line
context.stroke();
//restore the state of the context to what it was before our drawing
context.restore();
}
//create a public property called X (note caps!)
Object.defineProperty(this, 'X',
{
//getter
get: function () {
//return the value of x (lower case)
return x;
},
//setter
set: function (value) {
//ste the value of x (lower case)
x = value;
}
}
)
//create a public property called Y (note caps!)
Object.defineProperty(this, 'Y',
{
//getter
get: function () {
//return the value of y (lower case)
return y;
},
//setter
set: function (value) {
//ste the value of y (lower case)
y = value;
}
}
)
padX = function () {
if (rightPressed && padX < canvas.width - 20) {
padX += 5;
}
else if (leftPressed && padX > 0) {
padX -= 5;
}
}
Pad.prototype.move = function () {
//change the x axis by the x velocity
x += vx;
//change the y axis by the y velocity
y += vy;
}
Pad.prototype.setVector = function (vector) {
//set the vx value based on this vector
vx = vector.VX;
//set the vy value based on this vector
vy = vector.VY;
}
//public method to set the vector of the saucer
Pad.prototype.accelerate = function (Acceleration) {
//set vx
vx += Acceleration.AX;
////set vy
//vy += Acceleration.AY;
}
//create a public property called Top
Object.defineProperty(this, 'Top',
{
//getter
get: function () {
//return the y posn less the height
return y - 10;
}
}
)
//create a public property called Bottom
Object.defineProperty(this, 'Bottom',
{
//getter
get: function () {
//return the y posn plus the height
return y + 10;
}
}
)
//create a public property called Left
Object.defineProperty(this, 'Left',
{
//getter
get: function () {
//return the x posn less the width
return x - 80;
}
}
)
//create a public property called Right
Object.defineProperty(this, 'Right',
{
//getter
get: function () {
//return the x posn plus the width
return x + 80;
}
}
)
}
(1) There are at least two options to solve this problem
in your Pad.move function you could limit the change of x. You change it only when its within canvas width:
Pad.prototype.move = function() {
//change the x axis by the x velocity
var canvasWidth = 400,
padWidth = 150;
if (x + vx < canvasWidth - padWidth && x + vx >= 0)
x += vx;
//change the y axis by the y velocity
y += vy;
}
or similarly as you create ground you could create walls on both sides and collide pad with them.
(2) There is no collision handling between ball and pad:
place it in function drawFrame():
if (collision.Overlapping(ball, pad)) {
context.strokeText('ball hit pad!',20,100)
//..do some other stuff here
}
(3)Which brings us to showing message on canvas, you can just draw text on canvas
var ctx = canvas.getContext("2d");
ctx.font = "30px Arial";
ctx.fillText("Hello World",10,50);
Demo: http://codepen.io/anon/pen/RaxwLp?editors=1011
Pad was blocked because when key is pressed acceleration is always increased, so in order to move in opposite direction first it must go to 0 which takes quite some time. I have added keyup event and when key is released acceleration is zeroed:
if(leftPressed){
acceleraton.HThrust(.01);
}else if(rightPressed){
acceleraton.HThrust(-.01);
}else{
acceleraton.Halt();
}
Let's say we have a ball that can be moved left and right around the screen. When you click space, the ball should jump.
I got the ball to move left and right in a canvas. However, when the ball is moving left (for example) and I hit the space bar before anything, the ball stops moving left.
Check out my example so far!
I am using the KeyboardJS library to handle my key events:
KeyboardJS.on("left", function () {
cc();
x -= xv;
if (x < r) {
circle(x + width, y, r);
if (x + width <= width - r) {
x = x + width;
}
}
circle(x, y, r);
});
KeyboardJS.on("space", null, function () {
console.log("space!");
});
How could I get this behavior to stop so that when the space bar is hit, the ball jumps up but at the same time still moves to the left?
One thought added to everyone else's good ideas:
Separate your user input from your drawing.
Keyboarding:
If you’re having problems with KeyboardJS, check out Keydrown: http://jeremyckahn.github.io/keydrown/
Don’t do any drawing when capturing keys…just capture the user’s input of which direction they want the circle to go.
Set up an “direction” variable to hold how many times the user has pressed [left] or [right]:
var direction=0;
When [left] is pressed:
direction--;
When [right] is pressed:
direction++;
Direction is a net number. So if the user holds down the left key for 20 strokes and the right key for 15 strokes, direction will be -5 ( -20 + 15 ).
Set up an “altitude” variable to hold how many times the user has pressed [space]:
var altitude=0;
When [space] is pressed:
altitude-=10;
Altitude is a net number also.
Drawing:
Do all your drawing in a separate animation loop. Rather than using javascript’s setInterval, use the new and improved way of creating an imation loop -- requestAnimationFrame.
// set the starting circle positions
var currentX=canvas.width;
var currentY=canvas.height-r;
function animate(){
// even as we're executing this current animation loop
// request another loop for next time
requestAnimationFrame(animate);
// change the currentX position by the accumulated direction
currentX+=direction;
direction=0;
// change the currentY position by the accumulated altitude
currentY+=altitude;
altitude=0;
// draw the circle at its current position
cc();
circle(currentX,currentY,r);
// apply gravity to the circle
// to make it fall if its in the air
if(currentY<canvas.height-r){
currentY++;
}
}
Good Luck with your project!
What the problem is, is that if you press another key after pressing the first key, it will trigger that event, and stop triggering the other keydown event. This can be seen in this simplified example:
addEventListener('keydown',function(e) {console.log(e.keyCode, e.keyIdentifier)});
If you run that script, and then press left and then up, it will first show 37 Left a bunch of times, and then it'll show 32 U+0020 once and stop logging the left keydowns.
This is simply how the browser (and most other basic programs too) work. You can try doing the same thing in for example notepad, if you press the A key first, and then press space, it'll stop adding more As. This also means that you can't rely on key events (or key event libraries) to do this for you.
What you could do though, is make a global object that holds all keys that are pressed. For example:
window.KeysDown = {
37: false, //Left
39: false, //Right
38: false, //Up
40: false, //Down
32: false, //Space
};
addEventListener('keydown', function(e) {
var keyCode = e.keyCode||e.charCode||e.which;
if (keyCode == 32 && !window.KeysDown[keyCode])
onSpace();//This will only run when the spacebar is first pressed down.
if (window.KeysDown.hasOwnProperty(keyCode))
window.KeysDown[keyCode] = true;
});
addEventListener('keyup', function(e) {
var keyCode = e.keyCode||e.charCode||e.which;
if (window.KeysDown.hasOwnProperty(keyCode)) window.KeysDown[keyCode] = false;
});
var interval = setInterval(function() {
for (var i in window.KeysDown) {
if (window.KeysDown[i]) switch (i+'') {
case '37': onLeft(); break;
//case '38': window.KeysDown[i] && onUp(); break;
case '39': onRight(); break;
//case '40': window.KeysDown[i] && onDown(); break;
}
}
}, 50);
The syntax window.KeysDown[i] && onLeft() causes the onLeft function only to run if window.KeysDown[i] is true. I hope this solution works for you.
EDIT: I've changed the code to the working one. I've also made a JSFiddle that demonstrates this. The problem in my previous code was that apparently a switch doesn't handle integer values well, so I needed to convert i to a string.
EDIT: I've also added an extra part to the script that makes the onSpace function only run when the spacebar is first pressed down, and so that it won't run again until the spacebar is released and pressed again. I've also updated my JSFiddle to include these changes.
I would create a main function, which runs at a regular interval, and each time it runs, it updates the position of the circle based on what keys are currently down.
The main function can be done like this:
var keyDown = {};
function mainLoop() {
cc();
//pseudo code
if (keyDown["left"]) {
x -= 5;
}
if(keyDown["space"]) {
y -= 10;
}
// redraw circle at new location
circle(x,y,r);
}
setInterval(mainLoop, 30) //sets the function to be called every 30 milliseconds
// key event handler, first function handles the keydown, second function handles keyup
KeyboardJS.on("left", function() {
keyDown["left"] = true;
}, function() {
keyDown["left"] = false;
});
With this example, if the user had the left arrow key and space bar pressed when the mainLoop function ran, then the circle would move to the left 5 pixels, and up 10 pixels.
jsFiddle Demo
You are going to have to manually create a framework for this. KeyboardJS just wasn't cutting it. I guess I kind of set that up here. It uses an Action "class" coupled with key event triggers.
Action "class"
function Actions(){
this.count = 0;
this.running = {};
this.interval = undefined;
}
Actions.prototype.start = function(action){
if( typeof(this.running[action]) == "undefined"){
this[action]();
this.running[action] = action;
this.count++;
}
var me = this;
if( typeof(this.interval) == "undefined"){
this.interval = setInterval(function(){
for( var act in me.running ){
me[act]();
}
},50);
}
};
Actions.prototype.stop = function(action){
this.running[action] = void 0;
delete this.running[action];
this.count--;
if( this.count == 0 ){
clearInterval(this.interval);
this.interval = void 0;
};
};
Actions.prototype.left = function(){
cc();
x -= xv;
if (x < r) {
circle(x + width, y, r);
if (x + width <= width - r) {
x = x + width;
}
}
circle(x, y, r);
};
Actions.prototype.right = function(){
cc();
x += xv;
if (x >= width - r) {
circle((x - r) - (width - r), y, r);
if ((x - r) - (width - r) > r) {
x = (x - r) - (width - r);
}
}
circle(x, y, r);
};
Actions.prototype.space = function(){
cc();
y -= yv;
circle(x, y, r);
};
key event triggers
document.onkeydown = checkKeyDown;
function checkKeyDown(e) {
e = e || window.event;
if (e.keyCode == '37') {
// left arrow
actions.start("left");
}
if (e.keyCode == '39') {
// right arrow
actions.start("right");
}
if (e.keyCode == '32') {
// space bar
actions.start("space");
}
}
document.onkeyup = checkKeyUp;
function checkKeyUp(e) {
e = e || window.event;
if (e.keyCode == '37') {
// left arrow
actions.stop("left");
}
if (e.keyCode == '39') {
// right arrow
actions.stop("right");
}
if (e.keyCode == '32') {
// space bar
actions.stop("space");
}
}