JS/CSS Animation Triggers Twice After First Loop - css

I've been working on this carousel page (that gets started by an "enter" button from the landing page). It flips through just fine until after the first loop, after which the animations stack (until the page overloads).
It's basically an unordered list <ul> within a main <div> that lists images and videos.
IN A NUTSHELL, my animations trigger properly and my classes are transferred well during the first cycle. But by the second loop, something happens. The first page shows up properly, but the second item disappears because the animation that pulls it in is triggered twice (verified by adding an 'animationstart' console log).
THE FUNCTIONS:
Upon entry from the landing page's enterPage() function:
the first item, which is a video, is displayed and played; then
an 'ended' event listener that then triggers a flipCurrentToNext() function.
The flipCurrentToNext() function:
updates the currentPage and a nextPage variables;
pauses the currentPage if it's a video (the firstPage is a video);
adds a transition class that flips the currentPage to the left;
displays and animates the nextPage to flip into the viewport from the right;
it plays the video if it's a video, and just displays, if it's a photo;
adds an 'animationend' eventlistener to the nextPage, which then:
a) removes transition and .current-page class from flipped out currentPage;
b) adds .current-page class to nextPage;
c) updates currentPage = nextPage;
d) handles flipped in page as currentPage;
e) calls a function that autoFlip()s through the carousel.
flipLastToFirst() function does the same as No. 2, except:
nextPage is swapped with firstPage and;
the autoFlip() is not triggered because somehow the enterPage() function still triggers the video 'ended' event listener.
There are click handlers on transparent divs that users can use. I'm still not sure how I want these to override the autoFlip()s. (not really part of my question, but if you do have the answer, I'd love to know it!)
There are also other functions like flipToPrevious(), flipFirstToLast(), but they're basically the same as above.
THE CODE:
function enterPage() {
//some code that deals with the landing page
let cover = document.querySelector('.firstPage');
cover.style.display = "block";
cover.play();
cover.addEventListener('ended', () => {
flipCurrentToNext();
return;
})
return;
//I thought adding return would somehow close the eventlistener.
//I initially explored the possibility that 'ended' was also
//simultaneously calling flipToNext(), causing the
//animation overload.
}
let currentPage =
content.querySelector('.current-page');
let nextPage =
currentPage.parentElement.nextElementSibling.childNodes[1];
function autoFlip() {
currentPage = content.querySelector('.current-page');
if (currentPage.parentElement.nextElementSibling == null) {
flipLastToFirst();
} else {
flipCurrentToNext();
}
clearTimeout();
return;
}
function flipCurrentToNext() {
currentPage =
content.querySelector('.current-page');
nextPage =
currentPage.parentElement.nextElementSibling.childNodes[1];
//updates variables to stay current
if (currentPage.childNodes[1].type == "video/mp4") {
currentPage.pause();
//I suspected if this triggers the 'ended'
//eventlistener. Will it? If it does, how do I go around it?
//I couldn't figure out how to integrate
//a removeEventListener.
currentPage.currentTime = 0;
}
currentPage.classList.add('flipOutLeft')
//This transition class flips the current page out.
nextPage.style.display = "block";
nextPage.style.animation = "flipInRight 0.77s";
//THIS is the animation that gets triggered twice after
//the first loop. Then it stacks. I've console logged
//nextPage, its classes and its IDs. It is one and the same.
//
//This animation is what flips the next page into the viewport.
nextPage.addEventListener('animationend', () => {
currentPage.style.display = "none";
currentPage.classList.remove('flipOutLeft');
currentPage.classList.remove('current-page');
nextPage.classList.add('current-page');
currentPage = nextPage;
//above is an initial solution to handling fast clicks
//not final
if (currentPage.childNodes[1].type == "video/mp4") {
currentPage = content.querySelector('.current-page');
currentPage.play();
currentPage.addEventListener('ended', () => {
autoFlip(); //<--AUTOFLIP CALL for videos
return;
})
} else {
setTimeout(autoFlip, 5555); //<--for photos
}
return;
})
return;
}
//This is the CLICK handler.
flipRight.addEventListener('click', () => {
clearTimeout(autoFlip());
currentPage = content.querySelector('.current-page');
currentPage.removeEventListener('ended', flipCurrentToNext());
//I can't tell if this helped.
if (currentPage.classList.contains('lastPage') == true) {
flipLastToFirst();
} else {
flipCurrentToNext();
}
})
//I've console-logged the hell out of the code.
//I couldn't find where I'm going wrong.
//JUST IN CASE you need to see the LastToFirst function:
function flipLastToFirst() {
currentPage = content.querySelector('.current-page');
let firstPage = currentPage.parentElement.parentElement.firstElementChild.childNodes[1];
if (currentPage.childNodes[1].type == "video/mp4") {
currentPage.pause();
currentPage.currentTime = 0;
muteButton.style.visibility = "hidden";
}
currentPage.classList.add('flipOutLeft');
firstPage.style.display = "block";
firstPage.style.animation = "flipInRight 0.77s";
firstPage.addEventListener('animationend', () => {
currentPage = content.querySelector('.current-page');
currentPage.style.display = "none";
currentPage.classList.remove('flipOutLeft');
currentPage.classList.remove('current-page');
firstPage.classList.add('current-page');
currentPage = content.querySelector('.current-page');
firstPage.play();
muteButton.style.visibility = "visible";
return;
})
return;
}
.carousel__content { /*Unordered List*/
padding: 0;
margin: 0;
list-style-type: none;
overflow: hidden;
height: 100vh;
}
.carousel__page { /*List Item*/
height: 100vh;
float: left;
}
.carousel__page-item {
position: absolute;
display: none;
height: 100vh;
transition: all 0.55s ease-out;
animation: all ease-out;
animation-iteration-count: 1; /*I tried - didn't work*/
}
.flipOutLeft { /*Applied transition*/
transform: translate(-150%, 0);
opacity: 0;
#keyframes flipInRight { /*Applied animation*/
0% {transform: translate(200%, 0)};
100% {transform: translate(0, 0)};
<main>
<!--transparent navigation <buttons>-->
<div>
<ul class="carousel__content">
<li class="carousel__page">
<!--THIS VIDEO is what starts the carousel-->
<video class="carousel__video carousel__page-item
firstPage current-page" preload="auto" autoplay="true">
<source src="" type="video/mp4">
</video>
</li>
<li>
<picture class="carousel__image carousel__page-item">
<img /></picture>
</li>
<!--More are added just like this last picture.-->
</ul>
</div>
</main>
As much as I wanted to keep this short, I also didn't want to withhold relevant information. I obviously can't find the hole. I've been doing a lot of research but I feel like I simply do not see the problem properly.
I started coding a month ago and I definitely feel like I'm in over my head.
P.S. I'm 100% sure there are unnecessary lines in the code - feel free to point them out.

Related

Animate row background with css when data has changed

I am using Telerik Grid for Blazor WASM.
When data has changed on the server. I get notified via a SignalR connection.
I would like the affected rows to change background color and then return to the normal background color.
Could be a transition to red and fade back to the white or gray color.
I have seen many examples using hover and transitions. But this should be shown without user interaction and preferably delayed on items not in the current view. So when you scroll the grid and the items become visible, the animation starts.
Can AOS https://github.com/michalsnik/aos be used? Or will it only trigger on scroll?
The easiest way for me would be to set a class on the row in the row render event. But it’s a razor page so I can code a custom template.
Whatever can be done using :hover can be done if you add a class (then remove it after the transition). As for the appear only after scroll, you can check for the element is in view using the provided function.
function isScrolledIntoView(el) {
// from https://stackoverflow.com/a/22480938/3807365
var rect = el.getBoundingClientRect();
var elemTop = rect.top;
var elemBottom = rect.bottom;
// Only completely visible elements return true:
var isVisible = (elemTop >= 0) && (elemBottom <= window.innerHeight);
// Partially visible elements return true:
// isVisible = elemTop < window.innerHeight && elemBottom >= 0;
return isVisible;
}
var el = document.querySelector(".row")
window.addEventListener("scroll", function() {
if (isScrolledIntoView(el)) {
if (el.getAttribute("data-did-it")) {
return;
}
el.setAttribute("data-did-it", "true")
el.classList.add("active")
setTimeout(function() {
el.classList.remove("active")
}, 500)
}
})
.row {
transition: 500ms;
background: white;
}
.active {
background: yellow;
}
<div style="height: 400px">
scroll down
</div>
<div class="row">
this is a row
</div>
<div style="height: 400px">
scroll up
</div>

Why mousewheel event is not working some times with my code?

I'm trying to make a fullpage component in Angular :
I want each sections take a 100% height of my screen, and when I scroll in a section (up or down) it goes to the next / previous section. I declared this function to do that, and it works :
#HostListener('wheel', ['$event'])
onScroll(event: WheelEvent) {
if (event.deltaY < 0) {
this.slidePrev();
} else {
this.slideNext();
}
}
Now, I want to update a little to slide to the previous slide only if the scrollbar is on the top, or slide to the next only if the scrollbar is to the bottom. I Used JQuery to do that, this way :
#HostListener('mousewheel', ['$event'])
onScroll(event: WheelEvent) {
if (this.isSliding) {
event.preventDefault();
return;
}
let $section = $(event.target).closest(".section");
//$section.css("opacity", "0.99");
//setTimeout(() => {
// $section.css("opacity", "1");
//}, 100);
if (event.deltaY < 0) {
if ($section.scrollTop() == 0) this.slidePrev();
} else {
if ($section.scrollTop() == $section.prop("scrollHeight") - $section.height()) this.slideNext();
}
}
It works the first time, but if I slide down to the next slide, when I want to sroll to move my scrollbar, it doesn't move.
I noticed that after the website trigger an update (example : css :hover event that update style) the scrollbar move again. So, if I uncomment my 4 commented lines, it works again because the style is updated.
Can someone tell me why ? And is there a better way to fix that issue ?
EDIT :
in slideNext() and slidePrev() I'm using $().animate("scrollTop", ...) function, and it's the function that breaks my scroll

Is it possible to arrows on a pageable container (visual composer)?

I'm working on my WordPress website with Visual Composer.
I need to include a pageable container but it would be great if it can be like a slideshow.
This is my pageable container
Thanks in advance,
Regards :)
Based upon the current version of WP Bakery Page Builder the below works for me:
To build it I created a row with 3 columns, with the pageable container in the middle column and the left and right arrow images in the columns on either side.
Both arrow images and the pageable container were given IDs. In my example the IDs of the arrows were #arrow_prev and #arrow_next respectively. You can give your pageable container any unique ID.
(function ($) {
$(document).ready(function(){
$( '#arrow_prev' ).click( function( e ) {
var pageable_container = $(this).closest(".vc_row").find(".vc_tta-panels-container");
move_pageable_container(pageable_container,'prev');
});
$( '#arrow_next' ).click( function( e ) {
var pageable_container = $(this).closest(".vc_row").find(".vc_tta-panels-container");
move_pageable_container(pageable_container,'next');
});
function move_pageable_container(pageable_container,direction){
// Make a list of the panel IDs
var panel_ids = $(pageable_container.find(".vc_tta-panel"))
.map(function() { return this.id; }) // convert to set of IDs
.get();
// Find position of the active panel in list
var current_active_pos = panel_ids.indexOf($(pageable_container).find(".vc_tta-panel.vc_active").attr('id'));
var new_pos = 0;
switch(direction) {
case 'prev':
if (current_active_pos > 0){
new_pos = current_active_pos-1;
}else{
new_pos = panel_ids.length-1;
}
break;
case 'next':
if (current_active_pos < panel_ids.length-1){
new_pos = current_active_pos+1;
}else{
new_pos = 0;
}
break;
}
// Clear active panels
$(pageable_container.find(".vc_tta-panel")).each(function(i,a) {
$(this).removeClass("vc_active");
});
var new_active_panel = $(pageable_container).find('#'+ panel_ids[new_pos]);
$(new_active_panel).addClass("vc_animating");
$(new_active_panel).addClass("vc_active");
setTimeout(
function(){
$(new_active_panel).removeClass("vc_animating");
}, 350);
}
}
);
})(jQuery);
If you want a pseudo fading-in effect then you can use this additional CSS in your style sheet:
#id_of_pageable_container .vc_tta-panel.vc_animating {
opacity: 0!important;
}
Where #id_of_pageable_container is the ID that you gave your pageable container
A simpler solution with vanilla js only:
The idea is to find the target page button and press it programmatically, so that there is no need to mimic the plugin's animations as in Chaz's solution.
Add js (via Raw JS widget / other means):
function prevSlide () {
const slides = document.getElementsByClassName('vc_pagination-item');
for (let i = 0; i < slides.length; i++) {
if (slides[i].className.includes('vc_active')) {
if (i - 1 < 0) return;
slides[i - 1].firstChild.click();
return;
}
}
}
function nextSlide () {
const slides = document.getElementsByClassName('vc_pagination-item');
for (let i = 0; i < slides.length; i++) {
if (slides[i].className.includes('vc_active')) {
if (i + 1 >= slides.length) return;
slides[i + 1].firstChild.click();
return;
}
}
}
Add button widgets and set href to call js:
For left arrow button,
javascript:prevSlide();
For right arrow button,
javascript:nextSlide();
Hope this helps.
I prefer to use the Post Grid widget for that. Keep in mind that the pageable container is not totally responsive, it doesn't react to swipe touching, but the Post Grid does.
Post Grid is really powerful, although it also has its caveouts. You can create your content with posts and pages, or a custom post type and then filter what you want to show in your slider from the widget options.
In "advanced mode" you can use the Grid Builder to create your own template and control the output.
The only problems that I've found with this method is to set a variable height in sliders and that sometimes it is slow loading content and is not possible to do a lazyload.

Slide Card Style HTML CSS JS

I'm looking to do a slide-card style website with html/css/js.
I have seen some nice examples like:
http://www.thepetedesign.com/demos/onepage_scroll_demo.html
http://alvarotrigo.com/fullPage/
However, what these DON'T seem to do is slide a page out WHILE the page underneath is visible, as if they were a stack of index cards. Parallax scrolling does this, but it typically will wipe the existing area, rather then scroll/move it off screen. Any ideas?
Here is a fiddle using JQuery that does something like what you are looking for, you could implement it with that one scroll effect of the card sliders and have it animate in probably.
http://jsfiddle.net/d6rKn/
(function(window){ $.fn.stopAtTop= function () {
var $this = this,
$window = $(window),
thisPos = $this.offset().top,
setPosition,
under,
over;
under = function(){
if ($window.scrollTop() < thisPos) {
$this.css({
position: 'absolute',
top: ""
});
setPosition = over;
}
};
over = function(){
if (!($window.scrollTop() < thisPos)){
$this.css({
position: 'fixed',
top: 0
});
setPosition = under;
}
};
setPosition = over;
$window.resize(function()
{
bumperPos = pos.offset().top;
thisHeight = $this.outerHeight();
setPosition();
});
$window.scroll(function(){setPosition();});
setPosition();
};
})(window);
$('#one').stopAtTop();
$('#two').stopAtTop();
$('#three').stopAtTop();
$('#four').stopAtTop();
See the fiddle for HTML and CSS.
Not my fiddle just grabbed it with a quick google search.

Make the scroll go after popup windows height, not the main contents height

Long question: I have seen on Facebook that a single popup window (like those when you opens a question from someones feed) can affect the scroll behavior so it don't scroll the main content but only the content in the popup window according to the height of it, of course. How can I do something like this?
Short question: How can I make the scroll "focus" on the contents height in the welcome DIV?
I've made it this far: http://jsfiddle.net/y3qV5/506/. I hope you understand what I mean :)
Thanks in advance.
You can add overflow: hidden to the body.
http://jsfiddle.net/y3qV5/520/
body.freeze { overflow: hidden; }
$('#pop').click( function() {
$('.welcome').fadeIn();
$('body').addClass('freeze');
});
$('#close').click( function() {
$('.welcome').fadeOut();
$('body').removeClass('freeze');
});
Old answer:
http://jsfiddle.net/y3qV5/519/
//bind on show
$('.welcome').fadeIn().bind('mousewheel DOMMouseScroll', function(e) {
console.log('scroll');
var scrollTo = null;
if (e.type == 'mousewheel') {
scrollTo = (e.originalEvent.wheelDelta * -1);
}
else if (e.type == 'DOMMouseScroll') {
scrollTo = 40 * e.originalEvent.detail;
}
if (scrollTo) {
console.log('prevent');
e.preventDefault();
$(this).scrollTop(scrollTo + $(this).scrollTop());
}
});
//unbind on close
$('.welcome').fadeOut().unbind('mousewheel DOMMouseScroll');

Resources