I try to build a Slideshow in JS + CSS and it works pretty well except one visual glitch. The Transition to the last slides seems somehow broken.
But I couldn't figure out what the problem is. If I comment out the "offset" transition on the last slide, the error doesn't occure.
This is the codeine I am talking about: https://codepen.io/marianbreitmeyer/pen/paeYgZ
The Block of code I mentioned is this one:
const showNext = function() {
clicked = true;
for (i = 0; i <= slides.length-1; i++) {
if( parseInt(slides[i].style.zIndex) === slides.length) {
console.log(slides[i].innerHTML);
triggerAnimation(slides[i], 'offcanvas');
} else if (parseInt(slides[i].style.zIndex) === slides.length-1) {
//the line below triggers the problem
triggerAnimation(slides[i], 'offset');
}
}
};
Maybe someone with more experience could help me :)
Your code might be more simple:
const btn = document.getElementsByClassName('arrow')[0];
const slides = document.getElementsByClassName('slide');
slides[slides.length - 1].classList.add('offset', 'next');
btn.addEventListener("click", function(e) {
var o, n;
for (var i = 0; i < slides.length; i++) {
if (slides[i].classList.contains('offset')) {
slides[i].classList.remove('offset', 'next')
slides[i].classList.add('offcanvas');
o = (slides[i - 1] || slides[slides.length - 1]);
n = (slides[i - 2] || slides[slides.length + i - 2]);
}
if (slides[i].offsetLeft < -slides[i].offsetWidth) {
slides[i].classList.remove('offcanvas', 'next');
}
}
o.classList.add('offset');
n.classList.add('next');
}, false);
.container {
width: 100%;
height: 100vh;
background: brown;
position: relative;
}
body {
text-align: center;
font-size: 2rem;
}
.slide {
position: absolute;
top: 0;
left: 90%;
width: 100%;
height: 100%;
}
.slide:nth-child(1) {
background: pink;
}
.slide:nth-child(2) {
background: blue;
}
.slide:nth-child(3) {
background: green;
}
.slide:nth-child(4) {
background: grey;
}
.slide:nth-child(5) {
background: yellow;
}
.slide.next {z-index:1}
.slide.offset {
left: -10%;
z-index: 2;
transition: left .65s ease-in-out;
}
.slide.offcanvas {
left: -110%;
z-index: 2;
transition: left .65s ease-in-out;
}
.arrow {
position: absolute;
right: 5%;
top: 25px;
z-index: 9;
height: 50px;
width: 50px;
cursor: pointer;
}
.arrow:hover path {
transform: translate(16px, 0px);
}
path {
position: absolute;
top: 0;
left: 0;
transition: all .2s ease-in-out;
}
<div class="container">
<div class="slide">1 = pink</div>
<div class="slide">2 = blue</div>
<div class="slide">3 = green</div>
<div class="slide">4 = grey</div>
<div class="slide">5 = yellow</div>
<svg xmlns="http://www.w3.org/2000/svg" class="arrow"><path d="M19.443 5.17L30.138 15.5H-.095v1h30.233L19.443 26.829l.696.719L32.095 16 20.139 4.451z"/></svg>
</div>
Related
I am pretty new here and also with Javascript but I have tried for quite some time now and can't get my head around how to have individual data in a popup image gallery.
I have tried adding it in multiple ways, and I do not really need a number (1-..) but an individual URL, also an individual description (using the alt tag). So this is the first code that offered it to me and I hope there is some geeks in this community that can give me an easy and quick "fix".
Many thanks in advance!
Here is some code to make you understand better (hopefully):
HTML:
<div class="popup">
<!-- top bar -->
<div class="top-bar">
<p class="image-name">img1.png</p>
<p class="image-description">Just an image</p>
<span class="close-btn"></span>
!-- image -->
<img src="./img/ads/1.png" class="large-image" alt="Just an image">
<!-- image-index -->
<p class="index">01</p>
</div>
<div class="gallery">
<div class="gallery-image">
<img src="./img/ads/1.png" alt="Just an image" class="image">
</div>...
CSS:
.popup {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(0);
width: 80%;
max-width: 100%;
height: 90vh;
max-height: 70%;
border-radius: 20px;
background: rgba(0, 0, 0, 0.75);
display: flex;
justify-content: center;
align-items: center;
z-index: 5;
overflow: hidden;
transition: 1s;
opacity: 0;
}
.popup.active {
transform: translate(-50%, -50%) scale(1);
opacity: 1;
}
.popup.active .close-btn,
.popup.active .image-name,
.popup.active .index,
.popup.active .image-description,
.popup.active .large-image,
.popup.active .arrow-btn {
opacity: 1;
transition: opacity .5s;
transition-delay: 1s;
}
.top-bar {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 50px;
background: #000;
color: #fff;
text-align: center;
line-height: 50px;
font-weight: 300;
}
.image-name {
opacity: 0;
}
.close-btn {
opacity: 0;
position: absolute;
top: 15px;
right: 20px;
width: 20px;
height: 20px;
border-radius: 50%;
background: #f00;
cursor: pointer;
}
JAVASCRIPT:
const popup = document.querySelector('.popup');
const closeBtn = document.querySelector('.close-btn');
const imageName = document.querySelector('.image-name');
const largeImage = document.querySelector('.large-image');
const imageDis = document.querySelector('.image-description');
const imageIndex = document.querySelector('.index');
const leftArrow = document.querySelector('.left-arrow');
const rightArrow = document.querySelector('.right-arrow');
let index = 0; // will track our current image;
images.forEach((item, i) => {
item.addEventListener('click', () => {
updateImage(i);
popup.classList.toggle('active');
});
});
const updateImage = i => {
let path = `./img/ads/${i + 1}.png`;
largeImage.src = path;
imageName.innerHTML = path;
imageIndex.innerHTML = `0${i + 1}`;
index = i;
};
closeBtn.addEventListener('click', () => {
popup.classList.toggle('active');
});
leftArrow.addEventListener('click', () => {
if (index > 0) {
updateImage(index - 1);
}
});
rightArrow.addEventListener('click', () => {
if (index < images.length - 1) { updateImage(index + 1); } });```
I've written this code. It rotates two divs like a card flip using css transform every 1000ms and displays new text in the div, which is drawn from an array. It runs infinitely.
But once the array reaches its end I get an 'undefined' value because the i++ is incrementing on the final array item. I have been going crazy trying to figure out how to prevent that. Any help?
Codepen: https://codepen.io/warpigs666/pen/OJpBKdy
<style>
#flip-card {
width: 100px;
height: 50px;
}
#flip-card-inner {
position: relative;
width: 100%;
height: 100%;
transition: transform 0.6s;
transform-style: preserve-3d;
}
#textDiv1 {
background-color: lightblue;
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
text-align: center;
}
#textDiv2 {
background-color: lightcoral;
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
text-align: center;
transform: rotateX(180deg);
}
</style>
<body>
<div id="flip-card">
<div id="flip-card-inner">
<div id="textDiv1">
one
</div>
<div id="textDiv2">
set via script
</div>
</div>
</div>
<script>
var flipCard = document.getElementById('flip-card');
var flipCardInner = document.getElementById('flip-card-inner');
var textDiv1 = document.getElementById('textDiv1');
var textDiv2 = document.getElementById('textDiv2');
var wordArray = ["one", "two", "three", "four", "five"]
var i = 1;
function flipText(){
textDiv2.innerHTML = wordArray[i];
flipCardInner.style.transform = "rotateX(180deg)";
setTimeout(
function() {
textDiv1.innerHTML = wordArray[i++];
flipCardInner.style.transform = "rotateX(360deg)";
}, 1000
);
if (i<wordArray.length){
i++;
}
else {i=0;}
}
var flipIt = setInterval(flipText, 2000);
</script>
</body>
This is a job for setInterval
var flipCard = document.getElementById('flip-card');
let flipCardInner = document.getElementById('flip-card-inner');
let textDiv1 = document.getElementById('textDiv1');
let textDiv2 = document.getElementById('textDiv2');
let wordArray = ["one", "two", "three", "four", "five"];
let i = 0;
let timerVal = 1000;
function getI(i) {
if (i < wordArray.length - 1) return i + 1;
else return 0;
}
let ctr = 1,
useDiv; // ctr and useDiv will help to toggle between divs in our loop and also increment the transform number
textDiv1.innerHTML = wordArray[i]; // initialize
function flipText() {
i = getI(i);
useDiv = (useDiv == textDiv2) ? textDiv1 : textDiv2;
useDiv.innerHTML = wordArray[i];
flipCardInner.style.transform = "rotateX(" + (ctr * 180) + "deg)";
ctr++;
}
let interval
window.onload = function() {
// we'll still delay the beginning of the infinite loop, but then we set it to an interval, so it runs forever.
// if you need to stop it in your code, just use clearInterval(interval)
setTimeout(() => {
interval = setInterval(() => flipText(), timerVal);
}, timerVal);
}
#flip-card {
width: 100px;
height: 50px;
perspective: 100px;
}
#flip-card-inner {
position: relative;
width: 100%;
height: 100%;
transition: transform 0.6s;
transform-style: preserve-3d;
}
#textDiv1 {
background-color: lightblue;
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
text-align: center;
}
#textDiv2 {
background-color: lightcoral;
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
text-align: center;
transform: rotateX(180deg);
}
<div id="flip-card">
<div id="flip-card-inner">
<div id="textDiv1">
</div>
<div id="textDiv2">
</div>
</div>
</div>
The issue is as follows, you are incrementing i twice, when it should just be one time:
function() {
textDiv1.innerHTML = wordArray[i++]; ==> Here
flipCardInner.style.transform = "rotateX(360deg)";
}, 1000
);
if (i<wordArray.length){
i++; ==> And here
}
else {i=0;}
}
remove the increment on wordArray[i++], should not give you error.
Also make it i < wordArray.length -1
I don't think this is the ideal way of this job but sticking to your context perhaps like this;
var flipCard = document.getElementById('flip-card'),
flipCardInner = document.getElementById('flip-card-inner'),
textDiv1 = document.getElementById('textDiv1'),
textDiv2 = document.getElementById('textDiv2'),
wordArray = ["one", "two", "three", "four", "five"],
i = 0;
function flipText(){
textDiv2.innerHTML = wordArray[++i % wordArray.length];
flipCardInner.style.transform = "rotateX(180deg)";
setTimeout(function(){
textDiv1.innerHTML = wordArray[++i % wordArray.length];
flipCardInner.style.transform = "rotateX(360deg)";
}, 1000);
}
var flipIt = setInterval(flipText, 2000);
#flip-card { width: 100px;
height: 50px;
}
#flip-card-inner { position: relative;
width: 100%;
height: 100%;
transition: transform 0.6s;
transform-style: preserve-3d;
}
#textDiv1 { background-color: lightblue;
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
text-align: center;
}
#textDiv2 { background-color: lightcoral;
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
text-align: center;
transform: rotateX(180deg);
}
<div id="flip-card">
<div id="flip-card-inner">
<div id="textDiv1">
one
</div>
<div id="textDiv2">
set via script
</div>
</div>
</div>
I am trying to make connect4 HTML game and I know I will be better off using canvas elements instead of a grids of divs but is it possible to make transition translate type of css animation when moving HTML elements around like this (using appendChild)
const row1 = document.getElementById("row1")
const row2 = document.getElementById("row2")
const ball = document.getElementById("TheBall")
ball.addEventListener("click", (event, element) => {
let rowNum = parseInt(ball.dataset.row)
if(rowNum==1) {
row2.appendChild(ball)
ball.dataset.row = 2
} else {
row1.appendChild(ball)
ball.dataset.row = 1
}
})
#main {
left:100px;
width: 100px;
height: 100px;
display: flex;
flex-direction: column;
}
#main div {
margin: 50px 0;
width: 50px;
height: 50px;
}
#TheBall {
width: auto;
height: auto;
background-color: red;
border-radius: 100%;
}
<div id="main">
<div id="row1"> </div>
<div id="row2">
<div id="TheBall" data-row=2></div>
</div>
</div>
Click on the red dot to toggle position of ball
You can use animationend to check when the animation end and move the ball element between the divs
const row1 = document.getElementById("row1")
const row2 = document.getElementById("row2")
const ball = document.getElementById("TheBall")
ball.addEventListener('animationend', () => {
ball.classList.remove("animate-ball");
ball.style.animation = "";
let rowNum = parseInt(ball.dataset.row)
if (rowNum == 1) {
row2.appendChild(ball)
ball.dataset.row = 2
} else {
row1.appendChild(ball)
ball.dataset.row = 1
}
});
ball.addEventListener("click", (event, element) => {
let rowNum = parseInt(ball.dataset.row)
if (rowNum == 1) {
ball.style.animation = "MoveDown 1s linear";
} else {
ball.style.animation = "MoveUp 1s linear";
}
ball.classList.add("animate-ball");
})
#main {
left: 100px;
width: 100px;
height: 100px;
display: flex;
flex-direction: column;
}
#main div {
margin: 50px 0;
width: 50px;
height: 50px;
}
#TheBall {
position: relative;
width: auto;
height: auto;
background-color: red;
border-radius: 100%;
}
.animate-ball {
animation-iteration-count: 1;
position: absolute;
bottom: 0;
}
#keyframes MoveUp {
0% {
top: -50px;
}
100% {
top: -100px;
}
}
#keyframes MoveDown {
0% {
top: 0;
}
100% {
top: 50px;
}
}
<div id="main">
<div id="row1"> </div>
<div id="row2">
<div id="TheBall" data-row=2></div>
</div>
</div>
I've built a pretty basic HTML/CSS/JS slideshow. When I click the button on the right, the slide switches to the next slide, and when I click the button on the left, the slide switches to the previous slide.
However, I would like to add in a scrolling animation to make the slideshow feel less rigid; when I press the button on the right, I want the slide to 'slide' in from the right, and when I press the button on the left I want it to slide in from the left.
I've been researching possible ways to do this and came across CSS animations, specifically the w3.css framework (https://www.w3schools.com/w3css/w3css_animate.asp), but am not sure how I would implement this given that my slideshow already makes use of classes in order to switch between slides.
Here is my code:
var content = ["Slide 1", "Slide 2", "Slide 3"];
var style = ["background-color: blue;", "background-color: red;", "background-color: green;"];
var index = 0;
function next() {
if (index == (content.length - 1)) {
index = 0
} else {
index = index + 1
}
document.getElementById("slide").innerHTML = content[index];
document.getElementById("slideshow").style = style[index];
}
function back() {
if (index == 0) {
index = (content.length - 1)
} else {
index = index - 1
}
document.getElementById("slide").innerHTML = content[index];
document.getElementById("slideshow").style = style[index];
}
.slideshow {
position: relative;
height: 170px;
color: white;
background-color: blue;
}
.slideshow p {
position: absolute;
width: 100%;
margin-top: 0;
margin-bottom: 0;
text-align: center;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 20px;
}
.slideshow button {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 25px;
}
<!DOCTYPE html>
<html lang="en">
<body>
<div class="slideshow" id="slideshow">
<p id="slide">Slide 1</p>
<button onclick="back()" style="left: 0">❮</button>
<button onclick="next()" style="right: 0">❯</button>
</div>
</body>
</html>
Thanks in advance.
you can style a next and prev classes with CSS and then toggle them with js like this snippet
var content = ["Slide 1", "Slide 2", "Slide 3"];
var style = ["background-color: blue;", "background-color: red;", "background-color: green;"];
var index = 0;
function next() {
const slide = document.getElementById("slide");
if (index == (content.length - 1)) {
index = 0;
} else {
index = index + 1;
}
// wipe out class
slide.className = '';
// add next class
setTimeout(() => slide.classList.add('next'), 0);
slide.innerHTML = content[index];
slide.style = style[index];
}
function back() {
const slide = document.getElementById("slide");
if (index == 0) {
index = (content.length - 1);
} else {
index = index - 1;
}
// wipe out class
slide.className = '';
// add prev class
setTimeout(() => slide.classList.add('prev'), 0);
slide.innerHTML = content[index];
slide.style = style[index];
}
.slideshow {
position: relative;
height: 170px;
color: white;
overflow: hidden;
}
.slideshow p {
position: absolute;
width: 100%;
height: 100%;
line-height: 170px;
margin-top: 0;
margin-bottom: 0;
text-align: center;
top: 50%;
transform: translate(-50%, -50%);
font-size: 20px;
background-color: blue;
}
.slideshow button {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 25px;
}
.prev {
animation: prev 1s ease-in-out forwards;
}
.next {
animation: next 1s ease-in-out forwards;
}
#keyframes prev {
from {
left: -150%;
}
to {
left: 50%;
}
}
#keyframes next {
from {
right: -150%;
}
to {
right: -50%;
}
}
<div class="slideshow" id="slideshow">
<p id="slide" class="next">Slide 1</p>
<button onclick="back()" style="left: 0">❮</button>
<button onclick="next()" style="right: 0">❯</button>
</div>
I have a simple animation and I want to disable scrolling on the website
until this animation is done, it should be like a loader basically
UPDATE
thank you so much, but I have an issue with that sorry for not mention because I'm using a fixed position on a container to be fixed to do smooth scrolling, so when I use 'fixed' position for any element it doesn't seem to stick in the same place here is the full code
html
<main id="app">
<div id="scroll-container" class="scroll-container">
<div class="loader">
<div class="loader__block"></div>
</div>
</div>
</main>
CSS
body {
overflow-x: hidden;
overflow-y: scroll;
background: $bg-color;
user-select: none;
font-family: 'Platform Regular';
}
#app {
overflow: hidden;
position: fixed;
height: 100vh;
width: 100vw;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.scroll-container {
position: absolute;
overflow: hidden;
z-index: 10;
display: flex;
justify-content: center;
backface-visibility: hidden;
}
.loader {
width: 100vw;
height: 100vh;
position: absolute;
z-index: 999999;
overflow: hidden;
}
.loader__block {
position: absolute;
width: 0%;
height: 100vh;
background: #111111;
animation: go-left 4s cubic-bezier(.74, .06, .4, .92) forwards;
}
#keyframes go-left {
0% {
left: 0;
width: 0%;
}
50% {
left: 0;
width: 100%;
}
100% {
left: 100%;
width: 0;
}
these containers have a fixed position and overflow hidden because I'm making smooth page transition while scrolling and moving the 'y' position
here is also the js if it's going to help
function smoothScrolling() {
const html = document.documentElement;
const { body } = document;
const scroller = {
target: document.querySelector('#scroll-container'),
ease: 0.06, // <= scroll speed
endY: 0,
y: 0,
resizeRequest: 1,
scrollRequest: 0,
};
let requestId = null;
TweenLite.set(scroller.target, {
rotation: 0.01,
force3D: true,
});
function updateScroller() {
const resized = scroller.resizeRequest > 0;
if (resized) {
const height = scroller.target.clientHeight;
body.style.height = `${height}px`;
scroller.resizeRequest = 0;
}
const scrollY = window.pageYOffset || html.scrollTop || body.scrollTop || 0;
scroller.endY = scrollY;
scroller.y += (scrollY - scroller.y) * scroller.ease;
if (Math.abs(scrollY - scroller.y) < 0.05 || resized) {
scroller.y = scrollY;
scroller.scrollRequest = 0;
}
TweenLite.set(scroller.target, {
y: -scroller.y,
});
requestId = scroller.scrollRequest > 0 ? requestAnimationFrame(updateScroller) : null;
}
function onScroll() {
scroller.scrollRequest += 1;
if (!requestId) {
requestId = requestAnimationFrame(updateScroller);
}
}
function onResize() {
scroller.resizeRequest += 1;
if (!requestId) {
requestId = requestAnimationFrame(updateScroller);
}
}
function onLoad() {
updateScroller();
window.focus();
window.addEventListener('resize', onResize);
document.addEventListener('scroll', onScroll);
}
window.addEventListener('load', onLoad);
}
Use the CSS rule position: fixed; on your div with class loader which makes it to always stay in the same place even if the page is scrolled.
as so:
.loader {
width: 100vw;
height: 100vh;
position: absolute;
z-index: 999999;
overflow: hidden;
position: fixed;
}
.loader__block {
position: absolute;
width: 0%;
height: 100vh;
background: #111111;
animation: go-left 4s cubic-bezier(.74, .06, .4, .92) forwards;
}
#keyframes go-left {
0% {
left: 0;
width: 0%;
}
50% {
left: 0;
width: 100%;
}
100% {
left: 100%;
width: 0;
}
}
<main id="app">
<div id="scroll-container" class="scroll-container">
<div class="loader">
<div class="loader__block"></div>
</div>
</div>