Slider transitions with GSAP and Scrolling - javascript

Hi I'm trying to replicated the transitions on this pages sections http://cuberto.com
using TweenMax.
var slides=document.querySelectorAll('.slide');
var tl=new TimelineLite({paused:true});
for(var i=slides.length;i--;){
var D=document.createElement('div');
D.className='Dot';
D.id='Dot'+i;
D.addEventListener('click',function(){ tl.seek(this.id).pause() });
document.getElementById('Dots').appendChild(D);
tl.add('Dot'+i)
if(i>0){
if(i!=slides.length-1)
{
tl.addPause()
}
tl .set(slides[i-1].getElementsByClassName("sideDetails"), {width: "0"})
.fromTo(slides[i].getElementsByClassName("sideDetails"), .3, {width:'50%'},{ width: "100%", ease: Power2.easeInOut})
.to(slides[i].getElementsByClassName("detailsText"), .3, {opacity: "0", y:"-=60", ease: Power2.easeInOut},0)
.set(slides[i],{ background: "none"})
.fromTo(slides[i].getElementsByClassName("sideDetails"), .3, {x: "0%"},{ x: "100%", ease: Power2.easeInOut}, .3)
.to('#Dot'+i,.7,{backgroundColor:'rgba(255,255,255,0.2)'},'L'+i)
.set(slides[i],{zIndex:1-i})
.set(slides[i-1],{zIndex:slides.length})
.to(slides[i-1].getElementsByClassName("sideDetails"), .3,{width: "50%",ease: Power2.easeInOut}, .6)
.fromTo(slides[i-1].getElementsByClassName("detailsText"), .3, {opacity: "0", y:"-=60" }, {opacity: "1", y:"0",ease: Power2.easeInOut},.6)
};
};
full code at codepen can be found here
I'm basically trying to transition between a bunch of sliders with a swipe animation, I have alternated the element i would like to transition on each slide in black or pink so I can see the animation.
I can seem to isolate the animation to the current slide - in essence I want the left hand div to grow to 100%, then animate off the page to the right, then switch to the next slider and animate the left hand dive to a width of 50% from an initial state of 0. I believe that is what the Cuberto site is doing.
How ever I am getting in an awful mess with the scroll event firing an animation on all the slides.
I'm not particularly competent with vanilla javascript but would like to attempt this with or without jQuery.
I have tried pagepiling.js and fullpage.js but this doesn't achieve the effect I'm looking for.
I could really do with a a resolution I can go to my client with, or at least a direction I could go in.
Thanks

You could use animate.css.There are so many animation available whatever you want you can use.
Demo
function onNextClick() {
var button = document.getElementById('next'),
index = button.getAttribute('index'),
divs = document.getElementsByClassName('main-div')[0].getElementsByTagName('div');
index++;
if (index == 3) {
index = 0;
}
for (var i = 0; i < divs.length; i++) {
divs[i].classList.remove('show');
divs[i].classList.add('hide');
}
divs[index].classList.remove('hide');
divs[index].classList.add('show');
button.setAttribute('index', index);
}
.main-div {
width: 100%;
display: inline-block;
}
.next {
background: transparent;
border: 0px;
color: blue;
font-size: 28px;
text-transform: capitalize;
text-decoration: underline;
margin: 15px;
}
.main-div div {
width: 100%;
height: 250px;
color: #3c3c3c;
font-size: 56px;
text-align: center;
}
.show {
display: block;
}
.hide {
display: none;
}
.div-one {
background: pink;
}
.div-two {
background: green;
}
.div-three {
background: yellow;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.7.0/animate.min.css" rel="stylesheet" />
<div class="main-div">
<div class="div-one show animated bounceInRight">
1
</div>
<div class="div-two hide animated bounceInRight">
2
</div>
<div class="div-three hide animated bounceInRight">
3
</div>
<button id="next" onclick="onNextClick()" index="0" class="next">next</button>
</div>

Related

HTML/CSS/JS: How to make a smooth transitioning slideshow where one div fades out to reveal the next div?

I'm trying to make a slideshow with smooth transitions on a website a person requested me to make.
For example, when I click next, the current slide (a div with text and buttons) with fade out and the next slide will reveal.
Here is the HTML (edited thanks to a headstarter):
<div id="ssContainer">
<div class="slideshow" id="selected">
<img src="images/slideshow/1.jpg" />
<div class="ssText">
<h1>Welcome to White Grass</h1>
<p>Your complete solution to home building</p>
<button id="portfolioBtn">See Our Portfolio</button>
</div>
</div>
<div class="slideshow">
<img src="images/slideshow/2.jpg" />
<div class="ssText">
<h1>Custom Home Builder</h1>
<p>Customer satisfaction is our top priority</p>
</div>
</div>
<div class="slideshow">
<img src="images/slideshow/3.jpg" />
<div class="ssText">
<h1>Professional & Experienced</h1>
<p>A history of exceptional homes</p>
<button id="contactBtn">Contact Us Now</button>
</div>
</div>
<img id="prev" alt="Previous Slide" onclick="prev();" src="images/slideshow/leftarrow.png"></img>
<img id="next" alt="Next Slide" onclick="next();" src="images/slideshow/rightarrow.png"></img>
</div>
And the CSS:
.slideshow {
display: none;
height: 100%;
position: relative;
transition: display 0.2s;
}
.slideshow img {
max-width: 100%;
max-height: 100vh;
margin: auto;
}
.ssText {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.ssText * {
text-align: center;
}
.ssText h1 {
font-size: 2.5em;
text-transform: uppercase;
background-color: rgba(0, 0, 0, 50%);
padding: 5px;
}
.ssText p {
font-size: 1.1em;
background-color: rgba(0, 0, 0, 50%);
padding: 5px;
}
.ssText button {
position: absolute;
}
#prev, #next {
cursor: pointer;
position: absolute;
top: 50%;
width: auto;
margin-top: -22px;
padding: 10px;
transition: background-color 0.2s;
}
#next {
right: 0;
}
#prev:hover, #next:hover {
background-color: rgba(0, 0, 0, 50%);
}
#selected {
display: block !important;
}
#portfolioBtn {
left: 26%;
transform: translateX(26%);
}
#contactBtn {
left: 29%;
transform: translateX(29%);
}
button {
display: block;
border: none;
background-color: #0074c2;
padding: 15px;
font-size: 1em;
font-family: roboto;
color: white;
border-radius: 3px;
transition: background-color 0.2s;
}
Also, the font is Roboto. I added that in the body section of the CSS.
And here are the images:
1.jpg
2.jpg
3.jpg
leftarrow.png (Chevron Left Icon by Icons8)
rightarrow.png (Chevron Right Icon by Icons8)
I got a pretty basic concept of the JavaScript now thanks to an answer:
var slideIndex = 1;
var slides = document.getElementByClassName("slideshow");
function prev() {
if(slideindex < 1) {
slideindex = 3;
}
else {
slideindex--;
}
showSlides();
}
function next() {
if(slideIndex > 3) {
slideIndex = 1;
}
else {
slideIndex++;
}
showSlides();
}
function showSlides() {
if(slideIndex == 1) {
slides[0].id = "selected";
slides[1].id = "";
slides[2].id = "";
}
else if(slideIndex == 2) {
slides[0].id = "";
slides[1].id = "selected";
slides[2].id = "";
}
else if(slideIndex == 3) {
slides[0].id = "";
slides[1].id = "";
slides[2].id = "selected";
}
}
Now, here's the problem:
With the display transition, the images don't transition from block to none.
I even tried messing with the opacity. Gives me the animation but not the slideshow feel.
Changed code for .slideshow and #selected section but reverted:
.slideshow {
display: block;
height: 100%;
position: relative;
opacity: 0;
transition: opacity 0.2s;
}
#selected {
opacity: 1 !important;
}
How do I fix this? Also, tried messing with z-index.
Also, I have to click the previous and next button twice to change from slide 3 to 1 or slide 1 to 3. Weird. Would also want a fix for this.
No jQuery, or any external JS scripts besides my own, please.
Well, this question doesn't comply with Stackoverflow in the way that we expect you to show what you have try and show what you researched. Now you are mostly asking us to write code for you.
Some research and reading will help you get a start on the subject:
how to create transition css javascript
But hey! I've been there too, so, I'll try to give you an example.
DON'T USE THIS CODE
This is only for example purposes and it won't achieve exactly what you are asking for. This code only fades the image background and you are trying to change the whole block of code including the image and text.
The goal behind what follows is only to help you get an idea on how things work.
Let's say that you only want to fade in and fade out your slide. For that, I would use opacity CSS property.
.slideshow img {
max-width: 100%;
max-height: 100vh;
margin: auto;
opacity: 0;
transition: opacity 2s;
}
That said, you will have to add some IDs and your function to your clickable images:
<img id="slide1" src="images/slideshow/1.jpg" />
<img id="slide2" src="images/slideshow/2.jpg" />
<img id="slide3" src="images/slideshow/3.jpg" />
<img id="prev" alt="Previous Slide" onclick="fadeTransition('prev')" src="images/slideshow/leftarrow.png"></img>
<img id="next" alt="Next Slide" onclick="fadeTransition('next')" src="images/slideshow/rightarrow.png"></img>
And then, there is some javacript to help you start with it
var currentSlide = 1;//You need a var that contain the current slide that is show
function fadeTransition(side) {
if ((side === 'prev' && currentSlide === 1) || (side === 'next' && currentSlide === 3)) {return;}
if (side === 'prev') {var newSlide = currentSlide - 1;}
if (side === 'next') {var newSlide = currentSlide + 1;}
document.getElementById('slide'+currentSlide).style.opacity = 0;
document.getElementById('slide'+newSlide).style.opacity = 1;
currentSlide = newSlide;
return;
}
There us a problem with that , on load, all your image will be at opacity 0. You'll have to change the initial state of the first image. At this point, I'll use a class like
.in {
opacity: 1 !important;
}
And into the Javascript, instead of changing style.opacity I would add and remove the in class and adding it into the HTML for load purposes:
<img id="slide1" class="in" src="images/slideshow/1.jpg" />
javascript change class
So now, most of the previous Javascript code blocks are unusable. Keep it in mind that you have to store what the current displayed block is. Restrict your code so the user can't get to a point where he's going to a previous slide when the current slide is the first one.
I hope this will help you in achieving your goal.

Animation not easing

i have some problem with my transitioning. here is the javascript/jquery
function moveProgressBar(v, a) {
var getPercent = v / 100;
var getProgressWrapWidth = $('.progress-wrap').width();
var progressTotal = getPercent * getProgressWrapWidth;
var animationLength = a;
$('.progress-bar').stop().animate({
left: progressTotal
}, animationLength, function(){
if (getPercent === 1) {
$('.progress').css('height','auto');
$('.progress_checkout').text('Proceed to checkout!');
} else {
$('.progress').css('height','2rem');
$('.progress_checkout').text('');
}
});
}
.progress_checkout{
text-align: center;
margin: auto 0;
display: block;
color: #fff;
font-weight: bold;
padding: 2rem 0;
transition: ease-in-out 0.6s;
font-size: 200%;
}
.progress_checkout:hover{
background-color: white;
color: #C6DA80;
cursor: pointer;
}
.progress {
width: 100%;
height: 2rem;
}
.progress-wrap {
background: #C6DA80;
margin: 20px 0;
overflow: hidden;
position: relative;
}
.progress-bar {
background: #F5F5F5;
left: 0;
position: absolute;
top: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="progress-wrap progress" data-progress-percent="0">
<a class="progress progress_checkout"></a>
<div class="progress-bar progress"></div>
</div>
What i want to do is that when this progress bar is full display the text and make the bar bigger. It does that but the animation is instant instead of over 0.5s or so. I have tried it with addClass and removeClass and it does exactly the same. I've even added transition on ever element that it has possible contact with and it will still be instant.
NOTE: If something seems missing please let me know because i might
have not pasted all the code. Though as far as I'm concerned this
should be everything related too the animations
jQuery's animate uses it's own easing parameter. Unfortunately, only swing and linear are available
The only easing implementations in the jQuery library are the default, called swing, and one that progresses at a constant pace, called linear. More easing functions are available with the use of plug-ins, most notably the jQuery UI suite.
Documentation.
You have two options.
The first is CSS3 Animations, with which you can time and combine multiple animations. So I would suggest switching back to classes and using CSS.
The second is using jQuery UI, which has the following list of easing options:
linear
swing
_default
easeInQuad
easeOutQuad
easeInOutQuad
easeInCubic
easeOutCubic
easeInOutCubic
easeInQuart
easeOutQuart
easeInOutQuart
easeInQuint
easeOutQuint
easeInOutQuint
easeInExpo
easeOutExpo
easeInOutExpo
easeInSine
easeOutSine
easeInOutSine
easeInCirc
easeOutCirc
easeInOutCirc
easeInElastic
easeOutElastic
easeInOutElastic
easeInBack
easeOutBack
easeInOutBack
easeInBounce
easeOutBounce
easeInOutBounce
What you choose or prefer is up to you.
Thanks for the help but this ended up being my fix. Using opacity and having the a tag contain " " avoided the sudden jump from the text insert making the transition smooth.
if (getPercent === 1) {
$('.progress').animate({height: "4rem"}, 1000);
$('.progress_checkout').text('Proceed to checkout!');
$('.progress_checkout').animate({opacity: 1}, 800);
} else {
$('.progress').animate({height: "2rem"}, 1000);
$('.progress_checkout').text(' ');
$('.progress_checkout').animate({opacity: 0}, 800);
}

How to make a box transition smoother in html?

I've been trying to find some information about this, but I cant find some good information.
I want to make a div, that says "Contact Us" and when you click on the div, a layer shows up smoothy with input types.
I know that I can make some quick javascript to change from display:none to display:block, but how can I do it smooth?
for an example (just a quick example, the actual one will be better)
<div id="contact-us" onClick="showContactUs()">
<div id="contact-us-content" style="display: block;">
Name - <input type="text" name="name">
Email- <input type="text" name="email">
</div>
</div>
And then javascript is
function showContactUs(){
var r = document.getElementById("contact-us-content");
r.style.display = "block";
}
If any of you have any tips, or a link I can check I would appreciate it.
I am not that good with jquery, but can absolutely try some if you think its better.
There is no way to make the appearing smooth by setting display: block. You can, however, transition opacity. I suggest adding a class by javascript and solving the rest by css.
Check it out here: https://jsfiddle.net/3wLrfk3d/
$(document).on('click', '#contact-us', show_contact_form)
function show_contact_form () {
$('#contact-us-content').addClass('shown')
}
css:
#contact-us-content {
opacity: 0;
transition: opacity .5s ease;
&.shown {
opacity: 1;
}
}
My example uses jquery and sass, I am sure you will be able to rewrite it to vanilla and css.
I usually transition the opacity, but that will mean the element will be there even if it's invisible, taking up space and blocking mouse events. I solve this by having two classes, one to fade the element and one to hide it completely when it's done fading out:
$(function() {
var $box = $('.box');
$('.toggle').on('click', function() {
if (!($box).hasClass('visible')) {
$box.addClass('transitioning');
setTimeout(function() {
$box.addClass('visible');
}, 1)
} else {
$box.removeClass('visible');
setTimeout(function() {
$box.removeClass('transitioning');
}, 400)
}
})
})
body {
font-family: Helvetica, Arial, sans-serif;
}
.box {
background-color: #333;
color: white;
border: 5px solid white;
padding: 50px 10px;
box-shadow: 0 0 10px 0 rgba(0, 0, 0, .4);
text-align: center;
transition: opacity .4s ease-in-out;
display: none;
opacity: 0;
}
.transitioning {
display: block;
}
.visible {
opacity: 1;
}
.toggle {
margin-bottom: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="toggle">Toggle Box</button>
<div class="box">
<h1>Hi there</h1>
</div>
Note: this is a quick and dirty example, it kinda freaks out if you spam clicks on the toggle button, I'll leave that up to you.

jQuery animate on scroll backgroundColor not changing

My fiddle: https://jsfiddle.net/jzhang172/owkqmtcc/5/
What I'm trying to accomplish: When I scroll anywhere in the div, the background color of the div "content" will change. When the scroll rests at top of the div, it reverts back to its original color. When I added height instead of background color, it works fine, but not sure why background color ain't working:
$(function(){
var content = $(".content");
$(".box").scroll(function(event){
var positionofscroll = $(".content").scrollTop();
if(positionofscroll == 0){
content.stop().animate({
backgroundColor:"rgba(105, 63, 63, 0.69)"
},500);
}else {
content.stop().animate({
backgroundColor:"red"
},500);
}
}); //scroll
});
.box{
width:100%;
height:500px;
background:gray;
overflow:auto;
}
.content{
color:white;
display:flex;
justify-content:center;
align-items:center;
height:1000px;
background:red;
font-size:30px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.1/jquery.min.js"></script>
<!--Shadow Box when user scrolls -->
<div class="box">
<div class="content">
I'm content
</div>
</div>
Try including jQuery UI , as .animate() does not animate color without modifications or a color plugin; see .animate()
Animation Properties and Values
All animated properties should be animated to a single numeric
value, except as noted below; most properties that are non-numeric
cannot be animated using basic jQuery functionality (For example,
width, height, or left can be animated but background-color
cannot be, unless the jQuery.Color plugin is used).
also adjusting selector at positionofscroll to $(this).scrollTop(); changing comparison operator to > at if
$(function() {
var content = $(".content");
$(".box").scroll(function(event) {
var positionofscroll = $(this).scrollTop();
console.log(positionofscroll);
if (positionofscroll > 0) {
content.stop().animate({
backgroundColor: "rgba(105, 63, 63, 0.69)"
}, 500);
} else {
content.stop().animate({
backgroundColor: "red"
}, 500);
}
}); //scroll
});
.box {
width: 100%;
height: 500px;
background: gray;
overflow: auto;
}
.content {
color: white;
display: flex;
justify-content: center;
align-items: center;
height: 1000px;
background: red;
font-size: 30px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.0-beta.1/jquery-ui.min.js"></script>
<!--Shadow Box when user scrolls -->
<div class="box">
<div class="content">
I'm content
</div>
</div>
Try this:
var content = $(".content");
$(".box").scroll(function(event)
{
var positionofscroll = $(this).scrollTop();
if (positionofscroll > 0)
{
$(".content").css('background-color','rgba(105, 63, 63, 0.69)');
}
else
{
$(".content").attr('style','');
}
});
Fiddle: https://jsfiddle.net/4fc6pook/
If you want to animate the change of the background color, add some css to .content:
transition: all 0.3s ease-out;
or something similar.
Hope this helps!

List rotation with limited elements

I have div container with list (cards) inside. When I hover it, cards start to moving (translateX animation). container's width is 300px, elements count in container:3, each element width:100px.
So you can see 3 elements in container together overflow:hidden. What I want to make?, is that when there is no element to show translateX animation -100px = 100px blank space after third element, it start from 1 elements in the list immediately after last, with no blank space.
For now, I have no idea how it could be done without duplicates and etc.
Here is what I have at the moment:
Fiddle (Hover cards to see translation animation)
UPD 1:
The code and data (cards count, container size) was taken for example, i'll try to explain better what i want: My goal is to built list of cards and after button was pressed, the list will start moving (like in example with translateX animation) for some time (for example translateX: 12491px, animation-duration: 15s;) and stops. But problem is that amount of crads in the list would be in range of 3-40 cards (each card is 100px width & height). So, when i'll set translateX: 12491px for example, it will be out of range and after the last card in the list would appear blank space. I want first and last card to be tied somehow and after the last card immediately appears first card in the list and etc.. Maybe i am searching for solution in a wrong way, but i guess you understand the main idea.
UPD 2:
I found that cs:go uses animation that i wanted to write on html\css\js. Here is video: youtube.com
html:
<div class="container">
<div class="cards">
<div class="card">
1
</div>
<div class="card">
2
</div>
<div class="card">
3
</div>
</div>
</div>
css:
.container
{
width:300px;
height: 100px;
border: 2px solid black;
overflow: hidden;
}
.card
{
float:left;
height: 100px;
width: 100px;
background-color:blue;
box-sizing: border-box;
border: 2px solid red;
color: white;
font-size: 23px;
}
.cards:hover
{
transform: translateX(-100px);
transition-duration: 3s;
animation-duration: 3s;
animation-fill-mode: forwards;
}
start from 1 elements in the list immediately after last, with no
blank space
This is beyond CSS and you will need Javascript for that. Because, you have tagged the question with Javascript and not jQuery, my answer would be limited to pure Javascript only. Look ma, no JQuery ;)
I have no idea how it could be done without duplicates
Here is a DIY (do it yourself) idea..
The main trick is to show at least one item less than the total you have. If you have 3 cards, show only 2. If you have 4 cards, show only 3. Why, because you need to re-position a card when it goes out of view and wrap it back at the end. If you show exactly the same number of cards that you have, then you cannot break half-a-card and wrap it and you will see some blank space until the first one goes out of view. You get the idea?
Do not use translate or you will end up complicating things for yourself while scripting it out. Keep things simple.
Do not use a wrapper for your cards. Why? Because, we will be re-positioning the cards which have gone out of view. When we do that, the next card will take up its place and immediately go out of view making things further difficult for you.
To keep things simple, arrange your cards with absolute positioning relative to its container. To start with, let all cards stack up at top:0; and left: 0;.
Next wire-up Javascript to position the left property based on the width of each card and arrange them linearly.
Use requestAnimationFrame to control the animation.
Keep track of the left-most card and its left position. When this goes out of view (which is 0 minus width), appendChild this card to its container. This will move the card to the end of cards. Also, change the left property to it based on the last card in the list.
That' all there is to it.
Below is a demo. To make it easy for you to experiment, I have used a settings object to keep the configurable properties which you can easily tweak and see. Look closely at the code and you will find it simple to understand. You can set the iterations settings to 0 to make the animation infinite.
Also, note that you do not need to duplicate or fake the cards. Try the demo and add as many cards you want to.
The inline code comments in the snippet, will further help you understand each line of code and relate to the steps above.
Snippet:
var list = document.querySelector('.cardList'), // cache the container
cards = document.querySelectorAll('.card'), // cache the list of cards
start = document.getElementById('start'), // buttons
stop = document.getElementById('stop'),
reset = document.getElementById('reset'),
raf, init = 0, counter = 0, lastCard, currentIteration = 0, // general purpose variables
settings = { // settings object to help make things configurable
'width': 100, 'height': 100, 'speed': 2,
'iterations': 2, 'count': cards.length
}
;
start.addEventListener('click', startClick); // wire up click event on buttons
stop.addEventListener('click', stopClick);
reset.addEventListener('click', resetClick);
initialize(); // initialize to arrange the cards at start
function initialize() {
// loop thru all cards and set the left property as per width and index position
[].forEach.call(cards, function(elem, idx) {
elem.style.left = (settings.width * idx) + 'px';
});
init = -(settings.width); // initialize the view cutoff
lastCard = cards[settings.count - 1]; // identify the last card
counter = 0; currentIteration = 0; // reset some counters
settings.speed = +(document.getElementById('speed').value);
settings.iterations = +(document.getElementById('iter').value);
}
function startClick() {
initialize(); raf = window.requestAnimationFrame(keyframes); // start animating
}
function stopClick() { window.cancelAnimationFrame(raf); } // stop animating
function resetClick() { // stop animating and re-initialize cards to start again
window.cancelAnimationFrame(raf);
document.getElementById('speed').value = '2';
document.getElementById('iter').value = '2';
initialize();
}
// actual animation function
function keyframes() {
var currentCard, currentLeft = 0, newLeft = 0;
// iterate all cards and decrease the left property based on speed
[].forEach.call(cards, function(elem, idx) {
elem.style.left = (parseInt(elem.style.left) - settings.speed) + 'px';
});
currentCard = cards[counter]; // identify left-most card
currentLeft = parseInt(currentCard.style.left); // get its left position
if (currentLeft <= init) { // check if it has gone out of view
// calculate position of last card
newLeft = parseInt(lastCard.style.left) + settings.width;
list.appendChild(currentCard); // move the card to end of list
currentCard.style.left = newLeft + 'px'; // change left position based on last card
lastCard = currentCard; // set this as the last card for next iteration
counter = (counter + 1) % settings.count; // set the next card index
if ((settings.iterations > 0) && (counter >= (settings.count - 1))) {
currentIteration++; // check settings for repeat iterations
}
}
if (currentIteration >= settings.iterations) { return; } // when to stop
raf = window.requestAnimationFrame(keyframes); // request another animation frame
};
* { box-sizing: border-box; padding: 0; margin: 0; }
.cardList {
position: relative; height: 100px; width: 300px;
margin: 10px; border: 2px solid #33e;
overflow: hidden; white-space: nowrap;
}
.card {
position: absolute; left: 0; top: 0; text-align: center;
height: 100px; width: 100px; line-height: 100px;
background-color: #99e;
font-family: monospace; font-size: 2em; color: #444;
border-left: 1px solid #33e; border-right: 1px solid #33e;
}
div.controls, button { margin: 10px; padding: 8px; font-family: monospace; }
div.controls input { width: 48px; padding: 2px; text-align: center; font-family: monospace; }
<div class="controls">
<label>Speed <input id="speed" type="number" min="1" max="8" value="2" />x</label>
|
<label>Iterations <input id="iter" type="number" min="0" max="8" value="2" /></label>
</div>
<div class="cardList">
<div class="card">1</div>
<div class="card">2</div>
<div class="card">3</div>
<div class="card">4</div>
</div>
<button id="start">Start</button>
<button id="stop">Stop</button>
<button id="reset">Reset</button>
Fiddle: http://jsfiddle.net/abhitalks/1hkw1v0w/
Note: I have left out a few things in the demo. Especially, although width and height of the cards is part of the settings object, but currently it left fixed. You can easily use the settings object to make the dimensions of the cards configurable as well.
Edit:
(as per Op's comment)
If you want a greater control over distance to scroll, duration and timing-functions (easing), then you could implement those yourself using a library. A couple of such good libraries are the Robert Penner's Easing Functions and a jQuery plugin from GSGD. Although you can implement all of that with pure Javascript, it would be easier if you use a library like jQuery.
Catch here is that in order to do so effectively, you must then duplicate the cards. You can do so easily by cloning the entire list a couple of times.
Although you have not tagged this question with jQuery, here is a small demo (using jQuery to get it done quickly) where you can configure the speed and the distance.
Snippet 2:
var $cardList = $('.cardList').first(),
$cards = $('.card'),
$speed = $('input[name=speed]'),
width = 100,
randomize = true,
distance = 20 * width
;
for (var i = 0; i < 50; i++) {
$cards.clone().appendTo($cardList);
}
function spin() {
var newMargin = 0, newDistance = distance,
speed = +($speed.filter(':checked').val());
if (randomize) {
newDistance = Math.floor(Math.random() * $cards.length * 5);
newDistance += $cards.length * 5;
newDistance *= width;
}
newMargin = -(newDistance);
$cards.first().animate({
marginLeft: newMargin
}, speed);
}
$('#spin').click(function() {
$cards.first().css('margin-left', 0);
spin();
return false;
});
* { box-sizing: border-box; padding: 0; margin: 0; }
.cardList {
height: 100px; width: 302px; position: relative;
margin: 10px; border: 1px solid #33e;
overflow: hidden; white-space: nowrap;
}
.card {
display: inline-block; text-align: center;
height: 100px; width: 100px; line-height: 100px;
background-color: #99e;
font-family: monospace; font-size: 2em; color: #444;
border-left: 1px solid #33e; border-right: 1px solid #33e;
}
.cardList::before, .cardList::after {
content: ''; display: block; z-index: 100;
width: 0px; height: 0px; transform: translateX(-50%);
border-left: 8px solid transparent;
border-right: 8px solid transparent;
}
.cardList::before {
position: absolute; top: 0px; left: 50%;
border-top: 12px solid #33e;
}
.cardList::after {
position: absolute; bottom: 0px; left: 50%;
border-bottom: 12px solid #33e;
}
div.controls, button { margin: 10px; padding: 8px; font-family: monospace; }
div.controls input { width: 48px; padding: 2px; text-align: center; font-family: monospace; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="controls">
<label>Speed: </label>
|
<label><input name="speed" type="radio" value='6000' />Slow</label>
<label><input name="speed" type="radio" value='5000' checked />Medium</label>
<label><input name="speed" type="radio" value='3000' />Fast</label>
</div>
<div class="cardList"><!--
--><div class="card">1</div><!--
--><div class="card">2</div><!--
--><div class="card">3</div><!--
--><div class="card">4</div><!--
--></div>
<button id="spin">Spin</button>
Fiddle 2: http://jsfiddle.net/abhitalks/c50upco5/
If you don't want to modify the dom elements you could take advantage of flex-item's order property;
to do this you'd still need a little JS to add this property after animation has ended;
I also changed to animation instead of transition so it automatically resets the transform property at the end of animation.
$('.cards').mouseenter(function() {
setTimeout(function() {
$('.card').first().css("order", "2");
}, 3000);
});
$('.cards').mouseleave(function() {
$('.card').first().css("order", "-1");
});
.container {
width: 300px;
height: 100px;
border: 2px solid black;
overflow: hidden;
}
.card {
float: left;
/* height: 100px;
width: 100px;*/
background-color: blue;
box-sizing: border-box;
border: 2px solid red;
color: white;
font-size: 23px;
flex: 0 0 25%;
}
.cards:hover {
animation: trans 3s;
}
/**/
.cards {
width: 400px;
height: 100%;
display: flex;
transition: transform 3s;
}
#keyframes trans {
0% {
transform: translateX(0)
}
100% {
transform: translateX(-100px)
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div class="container">
<div class="cards">
<div class="card">1</div>
<div class="card">2</div>
<div class="card">3</div>
</div>
</div>
fiddle
But if you're OK to use JS I suggest you manipulate the order of DOM elements directly,taking the first child element of .cards and appending it to the end of list at the end of each animation;
try this:
var anim;
$('.cards').mouseenter(function(){
anim = setInterval(function(){
$('.cards').append($('.card').first())
},3000)
});
$('.cards').mouseleave(function(){
clearInterval(anim)
});
.container{
width:300px;
height: 100px;
border: 2px solid black;
overflow: hidden;
}
.card{
float:left;
/* height: 100px;
width: 100px;*/
background-color:blue;
box-sizing: border-box;
border: 2px solid red;
color: white;
font-size: 23px;
/**/
flex:0 0 25%;
}
.cards:hover{
animation: trans 3s infinite;
}
/**/
.cards{
width:400px;
height:100%;
display:flex;
}
#keyframes trans {
0% {
transform: translateX(0)
}
100% {
transform: translateX(-100px)
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div class="container">
<div class="cards">
<div class="card">
1
</div>
<div class="card">
2
</div>
<div class="card">
3
</div>
</div>
</div>
in case you want one card to be present at same time both at the beginning and at the end of card-list you'll need to make a deep-copy / clone of the element;
here's an example;
Update 2:
I wrote a jquery plugin that may act the way you want:
you can add as many cards as you want, right now the "translateX" is random (the script will choose randomly the final card)
link to the demo
Update:
I know, I used duplicates, but now my code works on three cards:
I added three "fake" cards
Each "real" card has it's own animation
the "fake" cards will be overlapped by the real ones once their cycle is finished ("when there is no element to show" as you asked)
check the snippet:
.container {
width: 300px;
height: 100px;
border: 2px solid black;
overflow: hidden;
}
.card {
float: left;
height: 100px;
width: 100px;
background-color: blue;
box-sizing: border-box;
border: 2px solid red;
color: white;
font-size: 23px;
}
.cards {
width: 600px;
}
.container:hover .card1{
animation: 1600ms slide1 infinite linear;
}
.container:hover .card2{
animation: 1600ms slide2 infinite linear;
}
.container:hover .card3{
animation: 1600ms slide3 infinite linear;
}
.fakecard{z-index:-1000;}
.container:hover .fakecard{
animation: 1600ms fakeslide infinite linear;
}
#keyframes slide1 {
0% { transform: translateX(0px); }
33% { transform: translateX(-100px); }
33.1% { transform: translateX(+200px); }
100% { transform: translateX(0px); }
}
#keyframes slide2 {
0% { transform: translateX(0px); }
66% { transform: translateX(-200px); }
66.1% { transform: translateX(100px); }
100% { transform: translateX(0px); }
}
#keyframes slide3 {
0% { transform: translateX(0px); }
99% { transform: translateX(-300px); }
99.1% { transform: translateX(+300px); }
100% { transform: translateX(0px); }
}
#keyframes fakeslide {
0% { transform: translateX(0px); }
99% { transform: translateX(-300px); }
99.1% { transform: translateX(+300px); }
100% { transform: translateX(0px); }
}
<div class="container">
<div class="cards">
<div class="card card1">
1
</div>
<div class="card card2">
2
</div>
<div class="card card3">
3
</div>
<div class="card fakecard">
1 (fake)
</div>
<div class="card fakecard">
2 (fake)
</div>
<div class="card fakecard">
3 (fake)
</div>
</div>
</div>
Previous answer:
Is this what you are trying to achieve?
I don't think you can do it without duplicates...
If not, can you explain better what you are trying to achieve here?
[snipped code removed]
Here is the same effect that you mentioned, with a little tweak on your CSS and a helpful hand from jQuery.
CSS
Change your selector for the translateX animation to apply on each of the .card boxes when their immediate parent is hovered, and not the .cards (which is the immediate parent of the .cards). This is because you'd want the cards to move to the left, and not the window through which they appear while making the movement.
That is,
.cards:hover .card {
transform: translateX(-100px);
transition-duration: 1.5s;
animation-duration: 1.5s;
animation-fill-mode: forwards;
}
jQuery
var $container = $('.container');
var cardWidth = 100;
$container.on('mouseenter', function (e) {
e.preventDefault();
var $card0Clone = $('.card').eq(0).clone(); // clone of the first .card element
$('.cards').append($card0Clone);
updateWidth();
});
$container.on('mouseleave', function (e) {
e.preventDefault();
var $cards = $('.card');
$cards.eq(0).remove(); // remove the last .card element
});
function updateWidth() {
$('.cards').width(($('.card').length) * cardWidth); // no of cards in the queue times the width of each card would result in a container fit enough for all of them
}
Code Explained
As you move in the mouse pointer, a clone of the first card is created, and appended to the end of the cards collection. Further, as you move the mouse out of the hover area, the original .card (which was cloned earlier) will be removed from the head of the queue - hence, producing a cyclic effect.
The real trick though is with the updateWidth function. Every time the mouse enters the .container the width of the .cards' immediate parent (i.e. .cards div) is updated, so that .cards div is wide enough to fit in all the .cards, and therefore, making sure that each of the cards push against each other and stay in one line at the time the translation animation is being done.
Here is a simple technique that manipulates the Dom to create your desired effect
Javascript:
document.querySelector('.cards').addEventListener('mousedown', function(e) {
if (e.clientX < (this.offsetWidth >> 1)) {
this.appendChild(this.removeChild(this.firstElementChild));
} else {
this.insertBefore(this.lastElementChild, this.firstElementChild);
}});
then in you css use the nth-of-type selector to position elements as required.
Here is your fiddle
If you are using mouseover you might need to wait for transitionend event before firing again.
Check out this demo
Here I used JQuery, you can configure your animation using two variables
var translateX = 1000; //adjust the whole distance to translate
var stepSpeed = 100; //adjust the speed of each step transition in milliseconds
After setting your variables, on the click event of the cards do the following:-
Get the number of the steps required based on translateX
Loop for the number of steps
Inside each loop (each step) move the cards 1 step to the left, then put the first card to the end of the cards to form the connected loop, then return back the cards to it's initial position
Here is the code:
var stepsNumber = translateX/100;
for(var i=0; i< stepsNumber; i++)
{
$('.cards').animate({'left' : -100}, stepSpeed,function(){
$('.cards div:last').after($('.cards div:first'));
$('.cards').css({'left' : '0px'});
});
}

Categories