JS slider flickering on transform: translate - javascript

I have a Vue component that is an image slider, and I use transform: translateX to cycle through the images. I based my work off of Luis Velasquez's tutorial here: https://dev.to/luvejo/how-to-build-a-carousel-from-scratch-using-vue-js-4ki0
(needed to make a couple of adjustments, but it follows it pretty closely)
All works fairly well, except I get a strange flicker after the sliding animation completes, and I cannot figure out why.
In my component I have:
<button
:class="btnClass"
#click="prev"
#keyup.enter="prev"
#keyup.space="prev"/>
<button
:class="btnClass"
#click="next"
#keyup.enter="next"
#keyup.space="next" />
<div class="slider-wrap">
<div class="slider-cards">
<div ref="slidewrap"
v-hammer:swipe.horizontal="handleSwipe"
class="cards"
>
<div class="inner" :style="innerStyles" ref="inner">
<SliderCard v-for="(item, index) in slides"
:key="`${item.id}-${index}`"
:item="item"
/>
</div>
</div>
</div>
</div>
The navigation of those is handled via the following methods (just including the "next" navigation one to spare some redundancy here:
next () {
// translate the inner div left, push first element to last index, and slide
// inner div back to original position
if (this.transitioning) {
return;
}
this.transitioning = true;
const elementToMoveToEnd = this.slides[2];
this.moveLeft();
this.afterTransition(() => {
this.resetTranslate();
this.slides.splice(0, 1);
this.slides.push(elementToMoveToEnd);
this.transitioning = false;
});
},
resetTranslate() {
/// NOTE: this.navigationStep is just a static px value set up elsewhere in my component, based on screenwidth -- it just sets how much the slider should translate based on the card widths.
this.innerStyles = {
transition: 'none',
transform: `translateX(-${this.navigationStep})`
};
},
moveLeft() {
this.innerStyles = {
transform: `translateX(-${this.navigationStep})
translateX(-${this.navigationStep})`
};
},
afterTransition (callback) {
const listener = () => {
// fire the callback (reorganizing slides, resetting the translation),
// and remove listener immediately after
callback();
this.$refs.inner.removeEventListener('transitionend', listener);
};
this.$refs.inner.addEventListener('transitionend', listener);
},
In my CSS, I control the translation animation via:
.inner {
transition: transform 0.2s linear;
white-space: nowrap;
-webkit-transform-style: preserve-3d;
}
(the preserve-3d was a desperate attempt to fix the flickering).
My slides move, but there is a flash immediately after they move -- I suspect it has something to do with competing translations/transitions in the moveLeft and resetTranslation methods, but I've messed with those, and nothing has improved the flashing. If anyone has any ideas, I'd really appreciate it!

Related

How to use IntersectionObserver API to make navBar sticky

I am making the clone of a webpage which is made in JS but I am developing it by HTML, CSS, JS. Its navBar looks like this . Here is the link if you want to experience yourself link.
So, I have tried to implement this using IntersectionObserver API as well as by using window.addEventListener(). I don't want to implement this by using scroll event Listener because it is too heavy for end user.
const intersectionCB = ([entry]) => {
const elem = entry.target;
if (!entry.isIntersecting) {
elem.classList.add('nav__2-sticky');
// observer.unobserve(navBar);
} else {
elem.classList.remove('nav__2-sticky');
}
};
const observer = new IntersectionObserver(intersectionCB, {
root: null,
threshold: 0
});
observer.observe(navBar);
In HTML file
<div class="nav__2">
<div class="row nav__2--content">
<div class="logo-container">
<img src="img/logo-black.png" alt="" class="logo" />
</div>
........
In SCSS file
.nav {
&__2 {
top: 8rem;
position: absolute;
left: 0;
width: 100%;
&-sticky {
position: fixed;
top: 0;
}
}
}
You might understand what is happening. When navBar gets out of the view, (navBar is positioned at 8rem from top!). I append nav__2-sticky class (which is positioned fixed at 0 from top) to appear on the screen. Due to which entry.isIntersecting becomes true and elem.classList.remove('nav__2-sticky'); is executed. As a result navBar again gets out of the view and again elem.classList.add('nav__2-sticky') is executed. This cycle of adding and removing classes due to entry.isIntersecting becoming True and False is creating a problem for me. This happens in such speed that it shows abnormal behaviour.
So, is there any proper solution for this? I would also like to hear other solutions that might work.
I used scroll event after all. Here is the code, I think I don't need to explain. You will get more detailed explanation here link
const initialCords = navBar.getBoundingClientRect();
document.addEventListener('scroll', () => {
if (window.scrollY > initialCords.top) {
navBar.classList.add('nav__2-sticky');
} else {
navBar.classList.remove('nav__2-sticky');
}
});
Another angle could be to run the intersection observer on an element that is out of view (below the bottom of the screen) and not only the navbar itself

How to make autosliding carousel thumbnail change background image when active

my question has 3 parts. Any assistance with any part of this JS problem would be greatly appreciated. I am attempting to learn and comprehend JS by trial and error.
I've created this nice looking travel landing page, https://portfolioprime.github.io/Nature%20carousel/glidejs.html with a thumbnail carousel which uses Glide.js, which is really cool and works well. The carousel moves to the left and has arrow buttons to manually control the slide.
But I've been trying to implement a vanilla JS carousel slider,but I am failing miserably. Been struggling for 2 days and the best I can achieve is getting a single carousel item moving left and right. See https://portfolioprime.github.io/Nature%20carousel/.
What I'd like is to get the carousel sliding left automatically, with arrow buttons to manually control the slider.
I'm targeting all the carousel-items with querySelectorAll('.carousel-items') and adding left:-274px to the carousel container glide__slides.
Here's my JS code.
// var & event-listener buttons
document.querySelector(".left").addEventListener("click", slideLeft);
document.querySelector(".right").addEventListener("click", slideRight);
// Function slide left
function slideLeft(left) {
document.querySelector('.glide__slides').style.left = left;
}
// Function slide left
function slideRight(right) {
document.querySelector('.glide__slides').style.left = right;
}
Secondly, I'd like to have an active carousel-item, which when active automatically changes the background Image.
Right now I have the hero.style.background = var; and I've got it changing onclick with onclick = function('01.jpg') on each carousel item.
Here's the code.
// Change Hero Img
function heroChange(hmmm) {
var hero = document.querySelector('.hero');
hero.style.background = hmmm;
}
So I guess I would add EventListeners to the carousel-items and add an active class to the carousel-item like so,
var slides = document.querySelectorAll('.carousel-items');
function changeBgImg() {
slides.forEach(s => s.classList.remove('active');
this.classList.add('active');
//change the bg image === this
//But I have no idea how to do that
}
Thirdly I've got the content, background and carousel indicators using the same functions above but it seems like really dirty code. The HTML has each .carousel-item, there are ten of them, calling 4 functions each. It looks like this:
<div class="glide hero-carousel">
<div class="glide__track" data-glide-el="track">
<ul class="glide__slides">
<li class="glide__slide carousel-item"
onclick="heroChange('url(images/02.jpg) bottom/cover no-repeat');
number('01');
h4('Destination Shire');
h1('Valley<br> of Dreams');">
<div class="carousel-text">
<p>Destination Shire</p>
<h3>Valley<br> of Dreams</h3>
</div>
</li>
<li class="glide__slide carousel-item"
onclick="heroChange('url(images/03.jpg) bottom/cover no-repeat');
number('02');
h4('Destination Westwood');
h1('Misty<br> Woodlands');">
<div class="carousel-text">
<p>Destination Westwood</p>
<h3>Misty<br> Woodlands</h3>
</div>
</li>
</ul>
</div>
</div>
So it looks pretty yucky. It works though, but I would love to find a more elegant way of achieving this by putting all of these functions into one function that does each part in sequence.
Lastly, I'd want to get transition on-click animations going but that's another kettle of fish entirely.
So that's it. Whew!
Thanks for taking the time guys, I appreciate it. Any help you can provide is going to make me a better designer. There are actually a bunch of projects I have will benefit from the answers.
If you can provide help with at least Part 2 & 3: cleaning up the code into 1 function and getting the bg-image changing on the active class that would be a big big help.
There's just so much that JS can do and I'm not finding the answers on Google and youTube.
Thank you again.
An Update:
I have edited the slider by by using margin-left as shown by this question:
vanilla javascript carousel not sliding
// var & event-listener buttons
document.querySelector(".left").addEventListener("click", slideLeft);
document.querySelector(".right").addEventListener("click", slideRight);
let marginLeft = 0;
const slides = document.querySelector('.glide__slides');
// Function slide left
function slideLeft() {
marginLeft += 264;
slides.style.marginLeft = marginLeft + 'px';
console.log(getComputedStyle(slides).marginLeft);
}
// Function slide Right
function slideRight() {
marginLeft -= 264;
slides.style.marginLeft = marginLeft + 'px';
console.log(getComputedStyle(slides).marginLeft);
}
This has now got the carousel moving manually 1 slide at a time.
Still not fully understanding why my previous code above didn't work. If anyone can explain that to me that would be great.
I'm still left with some issues:
Autosliding and looping at the end of the slides.
Having the active slider change the background automatically. At this point it only changes onclick.
Finding a way to tidy up the function calls and functions.
The question asks for various ideas on how to simplify code and how to use native JavaScript to create a slider that rolls continuously.
The code originally used glider and it may be something simpler would be sufficient to get the desired result, for example using animationend event to change the background when a slide gets to the left hand side. However, eating the elephant slowly I'll tackle the yucky code (part 3) first.
Although the HTML looks rather daunting, 4 calls on a click for every li element for example, it is currently what is required so let's investigate creating it at run time. This gives us more easily maintainable code. For example, if we want to remove a slide, or alter the order of slides or add one we can just alter the slider array defined below and JavaScript will do the rest.
Part 1 of the question asked about sliding. We slide the whole ul element using CSS animation defined something like this, where 33vw is the total width of a slide (inc. margins/padding)
#keyframes sliding0 {
0% { left: 0; }
30% { left: 0; }
100% { left: -33vw; }
}
and we add an event listener to the element to trap animationend events because when the ul has slid one slide's width we want to change the hero image, and we want to put the slide that has just disappeared onto the back of the infinie sliding will work. We then set the animation running again.
See the snippet for details on how this and other events are dealt with. It also shows how the changeHero function can work which was part 2 of the question. Note, the snippet works more or less in the SO environment, though occasionally hover action is partially ignored. Running the code on your own machine it should be fine though.
<!DOCTYPE html>
<html>
<head>
<style>
#keyframes sliding0 {
0% { left: 0; }
30% { left: 0; }
100% { left: -33vw; }
}
#keyframes sliding1 {
0% { left: 0; }
30% { left: 0; }
100% { left: -33vw; }
}
body {
background-repeat: no-repeat no-repeat;
background-size: cover;
background-position: center center;
}
div .glide_track {
position: relative;
width: 100vw;
overflow-x: hidden;
}
ul {
position:relative;
left: 0;
width: 330vw;
height:100vh;
animation-name: sliding0;
animation-duration: 3s;
animation-delay: 0s;
animation-iteration-count: 1;
animation-timing-function: linear;
margin: 0;
padding: 0;
list-style-type: none;
}
li {
position: relative;
left:0;
top:0;
float:left;
width: 32vw;
height:30vw;
display: inline-block;
margin: 0;
margin-right: 1vw;
padding: 0;
background-size: cover;
background-repeat: no-repeat no-repeat;
background-position: center center;
}
</style>
</head>
<body>
<script>
// we put the two lots of text and the image url for each slide in an array in the order they are to be shown
// this makes it easier to maintain when you want to add or remove a slide or change their order
// we only have one slider at the moment but this makes it more general
// these are the offsets in the array describing a slide. Done as indexes rather than named as easier to set up sliders array
const img = 0;
const text1 = 1;
const text2 = 2;
const sliders = [
[
['https://ahweb.org.uk/boxfordmosaic.jpg','Shire','Valley<br> of Dreams'],
['https://ahweb.org.uk/gear-in-turbine-house-reading.jpg','Westwood','Misty Woodlands'],
['https://ahweb.org.uk/tricycle-in-abbey-ruins.jpg','Shire','Valley<br> of Dreams'],
['https://ahweb.org.uk/boxfordmosaic.jpg','Shire','Valley<br> of Dreams'],
['https://ahweb.org.uk/gear-in-turbine-house-reading.jpg','Westwood','Misty Woodlands'],
['https://ahweb.org.uk/tricycle-in-abbey-ruins.jpg','Shire','Valley<br> of Dreams'],
['https://ahweb.org.uk/boxfordmosaic.jpg','Shire','Valley<br> of Dreams'],
['https://ahweb.org.uk/gear-in-turbine-house-reading.jpg','Westwood','Misty Woodlands'],
['https://ahweb.org.uk/tricycle-in-abbey-ruins.jpg','Shire','Valley<br> of Dreams'],
['https://ahweb.org.uk/tricycle-in-abbey-ruins.jpg','Shire','Valley<br> of Dreams']
]
];
// go through each slider and create its outer divs and its ul element
sliders.forEach(createSlider);
function createSlider(slider,sliderno) {
const div1 = document.createElement('DIV');
const div2 = document.createElement('DIV');
const ul = document.createElement('UL');
div1.classList.add("glide","hero-carousel");
div2.classList.add("glide_track");
div2.setAttribute("data-glide-el","track");
div1.appendChild(div2);
div2.appendChild(ul);
document.body.appendChild(div1);
ul.classList.add("glide__slides");
ul.addEventListener("animationend", animationEnd);
slider.forEach(createLi);
function createLi(slide,slideNo) {
const li = document.createElement('LI');
li.classList.add("glide__slide","carousel-item");
li.style.backgroundImage='url('+slide[img]+')';
li.addEventListener("click",slideClicked);
li.addEventListener("mouseover",slideHovered);
li.addEventListener("mouseout",slideUnhovered);
li.setAttribute('data-slideno','0' + slideNo);//! needs generalising if you have >10 slides !
ul.appendChild(li);
const div = document.createElement('DIV');
const p = document.createElement('P');
const h3 = document.createElement('H3');
p.innerHTML = slide[text1];
div.appendChild(p);
h3.innerHTML = slide[text2];
div.appendChild(h3);
li.appendChild(div);
}
}
// this is for testing, in real version use whatever required (i.e. whichever element is to have the hero image)
function ahHeroChange(backgroundImage) {
document.body.style.background = backgroundImage + " bottom/cover no-repeat";
}
function slideClicked(event) {
var slide = event.target;
var slideNo = slide.getAttribute('data-slideno');
// make the hero image the same as the slide's
ahHeroChange(slide.style.backgroundImage);
/* I don't know what these functions do - they were executed in the original on a click
number(slideno);
h4(slide.firstElementChild.querySelector('p').innerHTML);// text1 of the slide is passed to h4
h1(slide.firstElementChild.querySelector('h3').innerHTML;// text2 of the slide is passed to h1
*/
}
function slideHovered(event) {
var slide = event.target;
var slider = slide.parentElement;
slider.style.animationPlayState = 'paused';
ahHeroChange(slide.style.backgroundImage);
}
function slideUnhovered(event) {
var slide = event.target;
var slider = slide.parentElement;
//restore the hero image to the first one in the slider
ahHeroChange(slider.firstElementChild.style.backgroundImage);
//get the animation running again
slider.style.animationPlayState = 'running';
}
function animationEnd(event) {
//find the element that was clicked (it will be a ul element representing a slider)
var slider = event.target;
//take the first slide off the list and put it back at the end
slider.append(this.firstElementChild);
//change the hero image to the slide which is now the leftmost - use modified heroChange in the final version
document.body.style.backgroundImage = this.firstElementChild.style.backgroundImage;
// toggle the animationName (to an identical keyframes action) to force the animation to start again
slider.style.animationName='sliding'+(Number(event.animationName.replace('sliding',''))+1)%2;
}
</script>
</body>
</html>

Vanilla javascript, not CSS or jQuery, fade in, fade out image change

I know this is fairly easy in jQuery, but I want to do this in plain 'ol "will be around forever" javascript.
I have a dropdown select on my page. I choose one of 8 options. There is a default image showing on the page. When I select an option, the image changes to that pic. It all works fine.
But I want to make the image change a fade out, fade in switch over because I, like most of you, can't leave well alone. We have to keep fiddling.
The javascript that I have, which is triggered by an onchange="setPicture()" on the select dropdown is:
function setPicture(){
var img = document.getElementById("mySelectTag");
var value = img.options[img.selectedIndex].value;
document.getElementById("myImageDiv").src = value;
}
This works fine. The value of the selected index is a string with the path for each image. I just want a fade out then fade in stuck in there somewhere. I have fiddled about a bit, calling another function before changing the src but no luck.
Can someone point me in the right direction?
The easier way would be to use css keyframes alone.
But from javascript there is the web animation api made for that.
Here is a quick modif from the example, to match your case.
function setPicture(){
alice.animate(
[
{ opacity: 1 },
{ opacity: .1},
{ opacity: 1 }
], {
duration: 3000,
iterations: Infinity
}
)
}
<button onclick="setPicture()">
OPACITY ANIMATION
</button>
<img id="alice"
src="https://mdn.mozillademos.org/files/13843/tumbling-alice_optimized.gif"
>
</img>
How about setting the image default CSS with the opacity of 0 and with transition time
then in JavaScript just add a class that will make the opacity set to 1
HTML:
<img class="img1" src="sampleimg.jpg">
CSS:
.img1 {
opacity: 0;
transition: all .3s;
}
.img1.show {
opacity: 1;
}
JS:
function setPicture() {
var img = document.querySelector('.img1');
img.src = 'urlofnewimage';
img.classList.add('show');
}
Hope this helps.
Juste one function for all :
function fadeOutEffect(target) {
var fadeTarget = document.getElementById(target);
fadeTarget.style.opacity = 1;
fadeTarget.style.transition = "opacity 2s";
fadeTarget.style.opacity = 0;
setTimeout(function(){
fadeTarget.style.display = "none";
}, 2000);;
}

Single Button to scrollTop(); though sections on webpage

I have a webpage that is broken into different sections.
I've came across a lot of help and tutorials on how to create a navigation/side navigation to animate the html body to animate and scroll to the top of a div once the button is clicked.
What I haven't found is a way to combine all the functions into one nice compact button. (I'm thinking an arrow)
html will look something like this
<div class="section" id="section-one">
</div>
<div class="section" id="section-two">
</div>
<div class="section" id="section-three">
</div>
<div class="section" id="section-four">
</div>
<div class="section" id="section-five">
</div>
<span class="arrow">↑</span>
Css will look a little like this
.section{
height:800px;
width:100%;
display:block;
position:relative;
}
.arrow{
display:block;
position:fixed;
top:25px;
right:25px;
transition: opacity 0.5s, transform 1s;
transform: rotate(180deg);
-webkit-transition:opacity 0.5s, transform 1s;
-webkit-transform:rotate(180deg);
}
.arrow.is-up{
transform: rotate(0deg);
-webkit-transform: rotate(0deg);
}
I'm hoping to be able to click the arrow and scroll to each section one after the other. Then once it is on the last section section-five I can addClass('is-up'); which will animate the arrow 180deg and add a new function which will then scroll to the very top of the webpage.
// get the offset of your '.section' container
var bodyOffset = $('body').offset().top;
// listen for click events
$('.arrow').on('click', function(){
// count sections and assume that current section is the first section
var sectionCount = $('.section').length;
var currentSection = 0;
// iterate thru elements to determine what section you are in
for(var i = 0; i < sectionCount; i++){
var currentScrollPosition = $(document).scrollTop();
var thisSectionOffset = $(".section").eq(i).offset().top - bodyOffset;
if(currentScrollPosition >= thisSectionOffset){
currentSection = i;
}
}
// determine what the new section should be
var newSection = (currentSection+1 > sectionCount-1) ? 0 : currentSection+1;
// animate arrow
if(newSection == sectionCount-1){
$('.arrow').addClass('is-up')
} else {
$('.arrow').removeClass('is-up')
}
// scroll to the new section
$('html, body').animate({
scrollTop: $(".section").eq(newSection).offset().top
}, 400);
});
Here is a fiddle that uses a background gradient to help demonstrate the scrolling effect: https://jsfiddle.net/yotaajop/1/
https://jsfiddle.net/h4ja54uh/3/
On arrow click, determines which section is the next section and animates scroll to it. If we're on the last section, flip the arrow. If there is no next section, go back to top.
$('.arrow').click(function() {
$(document.body).animate({
scrollTop: (function() {
// Get the next section (top of section is below current scroll)
var $next = $('.section').filter(function(i, e) {
return $(document.body).scrollTop() < $(e).offset().top;
});
// Reset the arrow
$('.arrow').removeClass('is-up');
// See how many sections we have left
switch ($next.length) {
// We're past the last section, go to top.
case 0:
return $('.section').first().offset().top;
// Last section, flip that arrow!
case 1:
$('.arrow').addClass('is-up');
// Falls through on purpose.
// We flip the arrow, but we still want to scroll.
// Scroll to next section.
default:
return $next.first().offset().top;
}
})()
});
});
$(document.body).animate({
scrollTop: $('.section').first().offset().top
});
If you are going to have full screen pages, have you consider using my fullpage.js library?
The plugin provides plenty of functions and within them you can one to make your button moveSectionDown or moveSectionUp.

CSS3 and Javascript: fade text out and then in using the same function

I'm trying to create a fading out and fading in effect using JavaScript and CSS3. The goal is to have a div shrink in width when clicked and have the text contained within it simultaneously fade out. Then when it is clicked again, the div expands back to its normal width, and the text fades back in.
Here is the HTML:
<div id="box1" onclick="slide1()">
<p class="fader">Lorem ipsum.</p>
</div>
Here is the CSS:
#box1 {
position:relative;
left:0%;
top:0%;
width: 70%;
height: 100%;
background-color: #666;
z-index:4;
}
Here is the javascript:
var box1
var fader
window.onload = function() {
box1 = document.getElementById('box1');
fader = document.getElementsByClassName('fader');
}
function slide1(){
if(box1.style.width=='10%'){
box1.style.width='70%';
fader[0].style.opacity='1';
fader[0].style.transition='opacity 0.25s ease-in';
}
else {
box1.style.width='10%';
fader[0].style.opacity='0';
fader[0].style.transition='opacity 0.75s ease-in';
}
}
It's working for the fade-out, but for the fade-in it is immediately transitioning from 0 opacity to 1 opacity... there's no fade-in. Any ideas?
I actually asked a very similar question with the same issue a while back: Opacity effect works from 1.0 to 0, but not 0 to 1.0. Check the out and see if it works for you.
Otherwise, try adding a class to the fader element instead of adding a style declaration. Then, in your actual CSS, write the code for the fader element transition.
I guess you use even firefox or opera? I think your code won't work on safari or chrome since transition needs webkit-prefix on those browsers. You can use following code to get transition support:
var transform = (function () {
var transforms = [
'OTransform',
'MozTransform',
'msTransform',
'WebkitTransform'
], transform = 'transform';
while (transform) {
if (document.body.style[transform] === undefined) {
transform = transforms.pop();
} else {
return transform;
}
}
return false;
}());
When im using CSS-transition, sometimes I change transition style and then let browser update changes before changing other styles. You can do this with timeout. On some browsers I have noticed that animation is not working unless doing that (some firefox browsers).
fader[0].style.transition='opacity 0.75s ease-in';
setTimeout(function () {
box1.style.width='10%';
fader[0].style.opacity='0';
}, 4);

Categories

Resources