I'm trying to build out a super simple Super Mario-style game using just HTML, CSS and JS.
I'm not using <canvas> just building the whole thing out in simple divs, and getting the movement of the background and characters with CSS Animations.
The part where I'm running into issues is getting collision detection between the character and the monsters. Basically what I want to happen is if the character at all touches the monster, the game is over, and you avoid the monsters by jumping.
I have tried different snippets of JS for div collision detection, but the issue there is the code returns the static X and Y positions of the div, and not that of each keyframe in the CSS animation.
Doing some digging, I found a way for the code to track the X and Y of each keyframe by adding an event listener to my monster and character divs:
shadow1.addEventListener('animationiteration', function (event) {
shadowDiv = this.getBoundingClientRect();
console.log(shadowDiv);
});
cont.addEventListener('animationiteration', function (event) {
charDiv = this.getBoundingClientRect();
console.log(charDiv);
});
The above returns all the X and Y positions as the animation is running into the console for both the monster and the character, but the issue I'm running into with this, is that it only returns the keyframes if it's inside the scope of the event listener. I have tried every which way to get the same thing to happen in a global scope, but everytime I do, it returns a value of undefined, and from there I am basically stuck. and not sure how to move forward.
Any help would be appreciated!
Below is the relevant code:
HTML
<div id="char-container"></div>
<div id="shadow-container-1"> </div>
<div id="btn"> Jump! </div>
CSS
#keyframes rtol {
0% { left: 800px; opacity: 1;}
80% { opacity: 1;}
85%{ opacity: 0.75;}
90% { left: -220px; opacity: 0;}
100% { left: -220px; opacity: 0;}
}
#shadow-container-1{
width: 100px;
height: 160px;
position: absolute;
display: block;
bottom: 450px;
left: 800px;
opacity: 1;
animation-name: rtol;
animation-iteration-count: infinite;
animation-direction: normal;
animation-timing-function: linear;
animation-fill-mode: backwards;
#keyframes jump {
from { bottom: -50px; left: 0px; }
to { bottom: 400px; left: 150px; }
}
#char-container{
width: 150px;
height: 200px;
position: relative;
bottom: -50px;
}
JS
let btn = document.getElementById('btn');
let cont = document.getElementById('char-container');
btn.addEventListener('click', function(e){
if(e.target = btn){
cont.style.webkitAnimationPlayState = 'running';
cont.style.webkitAnimation = 'jump 1.25s ease 2';
cont.style.webkitAnimationDirection = 'alternate';
}
});
cont.addEventListener("webkitAnimationEnd", function(e){
cont.style.webkitAnimation = '';
});
let shadow1 = document.getElementById('shadow-container-1');
shadow1.style.webkitAnimationDuration = Math.floor(Math.random() * 15) + 3 + 's';
shadow1.addEventListener('animationiteration', function (event) {
shadowDiv = this.getBoundingClientRect();
console.log(shadowDiv);
});
cont.addEventListener('animationiteration', function (event) {
charDiv = this.getBoundingClientRect();
console.log(charDiv);
});
Related
I have a bouncing down arrow at the bottom of the screen of my website (the button part isn't completed yet, but the visual part is there) that stays in the same spot when you scroll down the page.
This is the code for the button:
#keyframes bouncing {
0% {bottom: 0;}
50% {bottom: 10px;}
100% {bottom: 0;}
}
.arrow {
animation: bouncing 1s infinite ease-in-out;
bottom: 0;
display: block;
height: 50px;
left: 50%;
position: absolute;
width: 50px;
}
<div>
<img class="arrow" src="https://cdn3.iconfinder.com/data/icons/google-material-design-icons/48/ic_keyboard_arrow_down_48px-128.png"/>
</div>
Is there a way to make the arrow fade out when you scroll down the page in JS or CSS?
thanks
You can listen to window.onscroll to listen which offset position you are when there is an scroll event.
here is a solution for hiding you image when scrolled to down with javascript:
var prevScrollpos = window.pageYOffset;
window.onscroll = function () {
console.log("scrolled");
var currentScrollPos = window.pageYOffset;
if (prevScrollpos >= currentScrollPos) {
console.log(prevScrollpos, currentScrollPos);
document.getElementById("arrow").style.display = "block";
} else {
console.log(prevScrollpos, currentScrollPos);
document.getElementById("arrow").style.display = "none";
}
};
note: You need to add an id with value of arrow to your image tag.
also here is the link for sample code.
To add to #Mahdi-Jafaree's answer, which is fine but only 'hides' and 'shows' the arrow instead of fading it, I tweaked the code a bit.
let opacityPercentage = 100;
document.addEventListener('DOMContentLoaded', () => {
const arrowElement = document.getElementById('arrow');
window.onscroll = () => {
opacityPercentage = Math.max(100 - window.pageYOffset, 0);
arrowElement.style.opacity = opacityPercentage / 100;
}
});
#keyframes bouncing {
0% {bottom: 0;}
50% {bottom: 10px;}
100% {bottom: 0;}
}
.arrow {
animation: bouncing 1s infinite ease-in-out;
bottom: 0;
display: block;
height: 50px;
left: 50%;
position: absolute;
width: 50px;
}
<div style="height: 200vh;">
<div>
<img class="arrow" id="arrow" src="https://cdn3.iconfinder.com/data/icons/google-material-design-icons/48/ic_keyboard_arrow_down_48px-128.png"/>
</div>
</div>
first time post here. a beginner.
What I'm trying to do here is that:
I'm trying to display an image of a silhouette of a person. And with every click, the current image fades out and another image of the same person (different position/location) fades in. kind of like an animation except very bare bones.
I made a variable that supposedly controls when certain images fade in or out(var currentscene).The button that calls the function covers the entire page and is invisible because I want the animation to move frame by frame with a click anywhere from the page.
('stand' is an image of a boy standing and 'sit' is an image of a boy sitting) edit: in the example it becomes squareblue and squarered.
(fade-in and fade-out are classes that I add to the image so the transition triggers)
And yet the problem is that a single click will fade in both pictures, as if the number on the variable (currentscene) doesn't matter or the onclick event registered infinite number of clicks or something like that.
Im trying to make the blue square fade in on the first click and the blue fade out and the red fade in on the second click of the button.
If there is a fix to this that would be helpful. If there is a whole another way to do what I'm trying to accomplish, I'm all ears. Although I've tried looking into arrays and EventListeners but I haven't been successful.
heres a recreation of the problem:
var currentscene = 0;
function next() {
currentscene++;
if (currentscene = 1) {
var element = document.getElementById("blue");
element.classList.add("fade-in");
}
if (currentscene = 2) {
var element = document.getElementById("blue");
element.classList.add("fade-out");
var element = document.getElementById("red");
element.classList.add("fade-in");
}
}
.squareblue {
height: 50px;
width: 50px;
top: 50px;
background-color: blue;
position: absolute;
opacity: 0;
animation-fill-mode: forwards;
}
.squarered {
height: 50px;
width: 50px;
top: 100px;
background-color: red;
position: absolute;
opacity: 0;
animation-fill-mode: forwards;
}
.fade-out {
animation: fadeOut ease 2s
}
#keyframes fadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
.fade-in {
animation: fadeIn ease 2s
}
#keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
<div2 id="blue" class="squareblue"></div2>
<div2 id="red" class="squarered"></div2>
<button class="button" onclick="next()">next</button>
I am trying to apply 'opacity / sliding to left' for my automatic slider but I couldn't make it. Please help me with this.
let images = ["images/a.jpg", "images/b.jpg", "images/c.jpg", "images/d.jpg", ];
let i = 0;
function changeImg() {
document.querySelector("#slider img").src = images[i];
if (i < images.length - 1) {
i++;
} else {
i = 0;
}
setTimeout("changeImg()", 1000);
}
onload(changeImg());
#slider img {
width: 100vw;
height: 60vh;
transition: opacity 2s ease-out;
}
<section id="slider">
<img name="slide" width="400" height="200" />
</section>
The initial requirement is for the image to fade and another one be shown.
This can be achieved by placing the images one on top of the other (position absolute) and opacity 0 with a transition set on opacity. When it is a particular images turn to be shown the image before it is set to opacity 0 and the new image has its opacity set to 1.
let images = ["https://picsum.photos/id/1015/300/300", "https://picsum.photos/id/1016/200/300", "https://picsum.photos/id/1018/300/200.jpg", "https://picsum.photos/id/1022/400/300", ];
const slider = document.querySelector('#slider');
const imgEls = [];
images.forEach(image => {
let imgEl = document.createElement('img');
imgEl.src = image;
imgEls.push(imgEl);
slider.appendChild(imgEl);
});
let i = 0;
function changeImg() {
imgEls[i].style.opacity = 0;
if (i < images.length - 1) {
i++;
} else {
i = 0;
}
imgEls[i].style.opacity = 1;
setTimeout("changeImg()", 1000);
}
window.onload = changeImg;
#slider {
width: 100vw;
height: 60vh;
position: relative;
isplay: felx;
justify-content: center;
align-items: center;
}
#slider img {
display: inline-block;
width: 100vw;
height: 60vh;
object-fit: contain;
transition: opacity 2s ease-out;
opacity: 0;
position: absolute;
top: 0;
left: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section id="slider">
<https://stackoverflow.com/questions/69870264/how-to-add-css-transitions-to-automatic-slider#img name="slide" width="400" height="200" />
</section>
While the action seems to correspond to what was discussed in the comments it doesn't make a real 'slider' and the question also talks of sliding to the left using CSS.
This snippet takes a slightly different approach to the question's code in that once the initial set up of the slides has been done using Javascript everything else is done via CSS. This has the advantage that it's possible the system can just hand the animation to the GPU without needing to interrupt back to the main program on a timeout.
Each image is located at left 100% (ie just off screen to the right) and it is given the same animation as the other - move to the center, wait and then move out to the left - opacity fading in and out respectively. The images however start animating at staggered times so the sliding looks continuous.
const slider = document.querySelector('#slider');
const images = ["https://picsum.photos/id/1015/300/300", "https://picsum.photos/id/1016/200/300", "https://picsum.photos/id/1018/300/200", "https://picsum.photos/id/1022/400/300"];
let div;
let count = 0;
images.forEach(image => {
div = document.createElement('div');
div.style.animationDelay = (count * 2) + 's';
count++;
div.style.setProperty('--img', 'url(' + image + ')');
slider.appendChild(div);
});
slider.style.setProperty('--n', count);
#slider {
overflow: hidden;
width: 100vw;
height: 60vh;
position: relative;
}
#slider div {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
opacity: 0;
display: inline-block;
background-size: contain;
background-position: center center;
background-repeat: no-repeat;
background-image: var(--img);
animation-name: slide;
animation-iteration-count: infinite;
animation-timing-function: linear;
animation-duration: calc(var(--n) * 2s);
animation-fill-mode: forwards;
}
#keyframes slide {
0% {
opacity: 0;
transform: translateX(100vw);
}
12.5% {
opacity: 1;
transform: translateX(0);
}
25% {
opacity: 1;
transform: translateX(0);
}
37.5%,
100% {
opacity: 0;
transform: translateX(-100vw);
}
}
<div id="slider"></div>
Note that the keyframes settings assume 4 images. The % values would have to be changed for more or fewer. Unfortunately CSS does not allow variables as % values in keyframes so the JS would ideally find the number of images and create the correct keyframes.
I am trying to build a simple web element that resembles the wheel contestants spin on The Price is Right (sample video). My question is: is there a more efficient way to achieve this effect than what I've cobbled together below?
My first attempt at this functionality basically decrements the translateY property of an element that contains the elements to be spun [codepen]:
setInterval(shiftCards, 100)
var translateY = -60,
cardIdx = 0,
startCards = 60,
wrap = document.querySelector('.wrap');
for (var i=0; i<startCards; i++) {
addCard()
}
function addCard() {
var div = document.createElement('div');
div.className = 'card';
div.style.top = (cardIdx * 12) + 'px';
wrap.appendChild(div);
cardIdx++;
}
function shiftCards() {
wrap.style.transform = 'translateY(' + translateY + 'px)';
translateY -= 12;
var cards = wrap.querySelectorAll('.card');
if (cards.length >= startCards) {
cards[0].parentNode.removeChild(cards[0]);
addCard();
}
}
.cards {
width: 80px;
height: 200px;
overflow: hidden;
background: #aaa;
}
.wrap {
position: relative;
transition: transform .25s;
}
.card {
width: 100%;
height: 10px;
margin: 2px 0;
background: red;
position: absolute;
left: 0;
right: 0;
}
<div class='cards'>
<div class='wrap'>
</div>
</div>
Is there a more efficient way to achieve this functionality? Can I create an element with n children and actually just spin them in the Z-dimension, rather than create an artificial spin as I've done above? Any guidance others can offer on this question will be very appreciated!
You can use css-animations for this: (They are probably more efficient)
/* only alignment */
body, html {
height: 100%;
margin: 0;
padding: 0;
}
.wrapper {
display:flex;
justify-content:center;
align-items:center;
height: 100%;
}
/* actual spinning */
img {
animation: spin 5s infinite linear;
/*
spin: the keyframes (=animation) we want to have
5s: how long it should take for one iteration
infinite: how often should it repeat
linear: the easing between the different key frames You can also try 'ease'
*/
}
#keyframes spin {
0% {transform: translateY(-60px);} /* Starting point */
100% {transform: translateY(-360px);} /* end point */
}
#-webkit-keyframes spin {
0% {transform: translateY(-60px);}
100% {transform: translateY(-360px);}
}
<div class="wrapper">
<img src="https://www.placecage.com/300/100" class="spin">
</div>
I want to apply a random animation on my slideshow image. First, I tried adding an animation such as scale but it didn't work as I wanted it to.
Things I want to fix:
Smoothness on fadein
Random animation (can be anything at this point, I just want to see how it's done)
Fiddle: http://jsfiddle.net/jzhang172/e7cLtsg9/1/
$(function() {
$('img').hide();
function anim() {
$("#wrap img").first().appendTo('#wrap').fadeOut(3500).addClass('transition').addClass('scaleme');
$("#wrap img").first().fadeIn(3500).removeClass('scaleme');
setTimeout(anim, 3700);
}
anim();
});
body,
html {
margin: 0;
padding: 0;
background: black;
}
#wrap img {
position: absolute;
top: 0;
display: none;
width: 100%;
height: 100%;
}
.transition {
transition: 10s;
}
.scaleme {
transition: 10s;
transform: scale(1.3);
}
.box {
height: 300px;
width: 500px;
position: relative;
overflow: hidden;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="box">
<div id="wrap">
<img src="http://elegantthemes.com/preview/InStyle/wp-content/uploads/2008/11/s-1.jpg" />
<img src="http://elegantthemes.com/preview/InStyle/wp-content/uploads/2008/11/s-5.jpg" />
<img src="http://elegantthemes.com/preview/InStyle/wp-content/uploads/2008/11/s-3.jpg" />
</div>
</div>
Here is a sample using CSS animations and jQuery (for achieving the randomness of animations). If you don't wish to use CSS animations and want to stick to transitions + jQuery effects (like fadeIn), you can still adapt this code to support it because the base idea will still remain the same. I am not too comfortable with jQuery effects and have hence stuck to using CSS animations.
Below is an overview of how it is being done (refer inline comments for more details):
Inside a wrapper there are a group of images that are part of the slide-show (like in your demo).
Using CSS #keyframes, a list of animations (one of which would be used randomly) is created in addition to the default fade-in-out animation. This list is also maintained in an array variable (in JS for picking up a random one from the list).
On load, the default fade-in-out animation and one random animation is added to the 1st element.
An animationend event handler is added to all of the images. This event handler will be triggered when the animation on an element ends. When this is triggered, animation on the current element is removed and the default fade-in-out + a random animation is added to the next element.
The animations are added using inline styles because if we add multiple CSS classes each with one different animation, then the animation in the latest class will override the others (that is, they will not happen together).
A loop effect is achieved by checking if the current element has any other img sibling elements. If there are none, the animation is added back to the 1st element.
$(window).load(function() {
$img = $('img'); // the images
var anim = ['zoom', 'shrink', 'move-down-up', 'move-right-left']; // the list of random animations
var rand = Math.floor(Math.random() * 4) + 1; // random number
$img.each(function() { // attach event handler for each image
$(this).on('animationend', function(e) { // when animation on one image has ended
if (e.originalEvent.animationName == 'fade-in-out') { // check the animation's name
rand = Math.floor(Math.random() * 4) + 1; // get a random number
$(this).css('animation-name', 'none'); // remove animation on current element
if ($(this).next('img').length > 0) // if there is a next sibling
$(this).next('img').css('animation-name', 'fade-in-out, ' + anim[rand - 1]); // add animation on next sibling
else
$img.eq(0).css('animation-name', 'fade-in-out, ' + anim[rand - 1]); // else add animation on first image (loop)
}
});
});
$img.eq(0).css('animation-name', 'fade-in-out, ' + anim[rand - 1]); //add animation to 1st element on load
})
#wrapper {
height: 250px;
width: 300px;
position: relative;
}
img {
position: absolute;
z-index: 1;
bottom: 20px;
left: 10px;
opacity: 0;
transform-origin: left top; /* to be on the safe side */
animation-duration: 3s; /* increase only if you want duration to be longer */
animation-fill-mode: backwards; /* fill mode - better to not change */
animation-iteration-count: 1; /* no. of iterations - don't change */
animation-timing-function: ease; /* better to leave as-is but can be changed */
}
#keyframes fade-in-out {
0%, 100% {
opacity: 0;
}
33.33%, 66.66% { /* duration is 3s, so fade-in at 1s, stay till 2s, fade-out from 2s */
opacity: 1;
}
}
#keyframes zoom {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(1.5);
}
}
#keyframes shrink {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(.5);
}
}
#keyframes move-down-up {
0%, 100% {
transform: translateY(0px);
}
50% {
transform: translateY(50px);
}
}
#keyframes move-right-left {
0%, 100% {
transform: translateX(0px);
}
50% {
transform: translateX(50px);
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="wrapper">
<img src="https://placehold.it/200/000000/ffffff" />
<img src="https://placehold.it/200/ff0000/ffffff" />
<img src="https://placehold.it/200/00ff00/ffffff" />
<img src="https://placehold.it/200/0000ff/ffffff" />
</div>