AIM
I would like to show a faded transition among three pictures (ideally 10, I used three for this question). Each image should gradually increase its opacity until fully opaque and then the next picture should start the same process, i.e. starting from opacity 0 to 1.
PROBLEM
If you have a look at the snippet, you'd see that only the third image follows this opacity process while the first two are vaguely shown as the first image overlaps them.
Thanks for your help!
ATTEMPT
.slider {
margin: 0;
padding: 0;
}
.slide {
position: absolute;
width: 100%;
height: 100%;
background-repeat: no-repeat;
background-size: cover;
}
.slide-1 {
background-image: url("https://images.immediate.co.uk/production/volatile/sites/23/2019/10/Federico_Veronesi_Lions-cover-image-e359a4e.jpg?quality=45&crop=10px,234px,3691px,2458px&resize=620,413");
animation: fade1 10s infinite;
}
.slide-2 {
background-image: url("https://images.unsplash.com/photo-1490100886609-e401a775fb3a?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&w=1000&q=80");
animation: fade2 10s infinite;
}
.slide-3 {
background-image: url("https://images.pexels.com/photos/326055/pexels-photo-326055.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500");
animation: fade3 10s infinite;
}
#keyframes fade1 {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
#keyframes fade2 {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
#keyframes fade3 {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
<body class="slider">
<div class="slide slide-1"></div>
<div class="slide slide-2"></div>
<div class="slide slide-3"></div>
</body>
CSS animation is doable but requires some kind of trick. The reason why your code does not work is because all the CSS animations are happening at the same time. What you want is to ensure that fade1, fade2, and fade3 all start and stop at consecutive intervals, so that they will come after each other:
fade1 starts at 0% and ends at 33% (1/3)
fade2 starts at 33% (1/3) and ends at 67% (2/3)
fade3 starts at 67% (2/3) and ends at 100% (3/3)
Of course, this means that you will need to (1) declare a new keyframe for each slide added and (2) adjust the start/end points of all keyframes when a new slide is added. This can be cumbersome, and the only way to automate this (or do it programmatically) is to use JS or preprocessed CSS.
See proof-of-concept below:
.slider {
margin: 0;
padding: 0;
}
.slide {
position: absolute;
width: 100%;
height: 100%;
background-repeat: no-repeat;
background-size: cover;
}
.slide-1 {
background-image: url("https://images.immediate.co.uk/production/volatile/sites/23/2019/10/Federico_Veronesi_Lions-cover-image-e359a4e.jpg?quality=45&crop=10px,234px,3691px,2458px&resize=620,413");
animation: fade1 10s infinite;
}
.slide-2 {
background-image: url("https://images.unsplash.com/photo-1490100886609-e401a775fb3a?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&w=1000&q=80");
animation: fade2 10s infinite;
}
.slide-3 {
background-image: url("https://images.pexels.com/photos/326055/pexels-photo-326055.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500");
animation: fade3 10s infinite;
}
#keyframes fade1 {
0% {
opacity: 0;
}
33% {
opacity: 1;
}
}
#keyframes fade2 {
33% {
opacity: 0;
}
67% {
opacity: 1;
}
}
#keyframes fade3 {
67% {
opacity: 0;
}
100% {
opacity: 1;
}
}
<body class="slider">
<div class="slide slide-1"></div>
<div class="slide slide-2"></div>
<div class="slide slide-3"></div>
</body>
SCSS solution
If you are using a CSS preprocessor such as SCSS, you can simply generate slides on the fly: all you need is to remember to add the corresponding markup:
// Just some demo images
$images: [
'https://via.placeholder.com/400x300/3077FF/fff?text=Slide1',
'https://via.placeholder.com/400x300/2C9EE8/fff?text=Slide2',
'https://via.placeholder.com/400x300/3DE4FF/fff?text=Slide3',
'https://via.placeholder.com/400x300/2CE8CE/fff?text=Slide4',
'https://via.placeholder.com/400x300/30FFA8/fff?text=Slide5',
];
.slide {
position: absolute;
width: 400px;
height: 300px;
background-repeat: no-repeat;
background-size: cover;
opacity: 0;
}
#for $i from 1 through length($images) {
$image: nth($images, $i);
// Custom animation styles and background image for each slide
.slide-#{$i} {
animation: fade#{$i} 10s infinite;
background-image: url(#{$image});
}
// Generate keyframe for each slide
#keyframes fade#{$i} {
#{(($i - 1) / length($images)) * 100%} {
opacity: 0;
}
#{($i / length($images)) * 100%} {
opacity: 1;
}
}
}
See example below (the CSS has been precompiled):
.slider {
margin: 0;
padding: 0;
}
.slide {
position: absolute;
width: 400px;
height: 300px;
background-repeat: no-repeat;
background-size: cover;
opacity: 0;
}
.slide-1 {
animation: fade1 10s infinite;
background-image: url(https://via.placeholder.com/400x300/3077FF/fff?text=Slide1);
}
#keyframes fade1 {
0% {
opacity: 0;
}
20% {
opacity: 1;
}
}
.slide-2 {
animation: fade2 10s infinite;
background-image: url(https://via.placeholder.com/400x300/2C9EE8/fff?text=Slide2);
}
#keyframes fade2 {
20% {
opacity: 0;
}
40% {
opacity: 1;
}
}
.slide-3 {
animation: fade3 10s infinite;
background-image: url(https://via.placeholder.com/400x300/3DE4FF/fff?text=Slide3);
}
#keyframes fade3 {
40% {
opacity: 0;
}
60% {
opacity: 1;
}
}
.slide-4 {
animation: fade4 10s infinite;
background-image: url(https://via.placeholder.com/400x300/2CE8CE/fff?text=Slide4);
}
#keyframes fade4 {
60% {
opacity: 0;
}
80% {
opacity: 1;
}
}
.slide-5 {
animation: fade5 10s infinite;
background-image: url(https://via.placeholder.com/400x300/30FFA8/fff?text=Slide5);
}
#keyframes fade5 {
80% {
opacity: 0;
}
100% {
opacity: 1;
}
}
<div class="slider">
<div class="slide slide-1"></div>
<div class="slide slide-2"></div>
<div class="slide slide-3"></div>
<div class="slide slide-4"></div>
<div class="slide slide-5"></div>
</div>
To do a faded transition you need to add percentages to the keyframes. This depicts the percentage of time you would like the image to appear on screen.
For this example you picked three so it would be 100/3 which unfortunately is 33.33333(you get the idea.)
Anyway, here's the updated keyframes for your three images. To adjust these over 10 images you would simply need to 100/10 (10% for each image).
All should start with opacity 0 at 0% then opacity 0 at the starting percentage(in the first image's case it's 33%. Then 0 opacity at 100%.
#keyframes fade1 {
0% {
opacity: 0;
}
33% {
opacity: 1;
}
100% {
opacity: 0;
}
}
#keyframes fade2 {
0% {
opacity: 0;
}
33% {
opacity: 0;
}
66% {
opacity: 1;
}
100% {
opacity: 0;
}
}
#keyframes fade3 {
0% {
opacity: 0;
}
66% {
opacity: 0;
}
100% {
opacity: 1;
}
}
If you would like further explanation let me know.
Related
I tested with the following answers:
Pure CSS rotate animation broken while in infinite loop
Stop infinite CSS3 animation and smoothly revert to initial state
CSS Image Fade Animation Only Runs First Time,
but the animation duration and timeline (for example, from step by step, from start to end) did not work. The three images need to be in the same place at once.
I wanted to use https://codepen.io/jay-bee-why/pen/Htejl, but unfortunately I do not want to use jQuery. I am CSS and JavaScript purist.
An image is worth a thousand words. You will understand easily the image. I also provide very small snippet code box.
.flipping-images
{
align-items: center;
display: flex;
flex-flow: row nowrap;
height: 80%;
justify-content: center;
/* opacity: 0; */
position: relative;
transform: translateX(100%);
width: 22%;
}
.show-l
{
animation: show-image 5s ease-in-out 300ms infinite;
left: 0;
position: absolute;
transform-origin: left;
}
.hide-l
{
animation: hide-image 5s ease-in-out 800ms infinite;
position: absolute;
transform-origin: left;
}
.hide-l2
{
animation: hide-image 5s ease-in-out 600ms infinite;
position: absolute;
transform-origin: right;
}
#keyframes hide-image
{
0%
{
left: 0;
transform: rotateY(0deg);
}
30%
{
left: 10%;
transform: rotateY(0deg);
}
50%
{
opacity: 1;
}
100%
{
left: -100%;
opacity: 0;
transform: rotateY(90deg);
}
}
#keyframes show-image
{
0%
{
left: 100%;
transform: rotateY(90deg);
}
30%
{
left: 110%;
transform: rotateY(90deg);
}
100%
{
left: 0%;
transform: rotateY(0deg);
}
}
<div class="flipping-images">
<img class="show-l" src="https://via.placeholder.com/432x864/fdc34f/FEFEFE?text=1">
<img class="hide-l2" src="https://via.placeholder.com/432x864/3e72ff/FEFEFE?text=2">
<img class="hide-l" src="https://via.placeholder.com/432x864/222222/FEFEFE?text=3">
</div>
I'm not sure I understand your image since it says the second image should disappear but it also says the animation is infinite. I hope it's working as you intended, if not just leave a comment on what needs to be fixed.
I'm using the animationend event to control the animations.
var counter = 1;
var div = document.querySelector('.flipping-images');
var images = document.querySelectorAll('.flipping-images img');
var showNext = function () {
counter++;
if (counter > 3) counter = 1;
div.classList.remove('image1', 'image2', 'image3')
div.classList.add('image'+counter);
};
for (var img of images) {
img.addEventListener('animationend', showNext);
img.addEventListener('click', showNext);
}
document.querySelector('#next').addEventListener('click', showNext);
.flipping-images {
perspective: 300px;
}
.flipping-images img {
display: none;
animation: rotate 5s linear 1;
}
.flipping-images.image1 img:nth-child(1),
.flipping-images.image2 img:nth-child(2),
.flipping-images.image3 img:nth-child(3) {
display: block;
}
.flipping-images.image2 img:nth-child(2) {
animation: rotate 5s linear infinite;
}
#keyframes rotate {
0% { transform: rotateY(-45deg); }
100% { transform: rotateY(45deg); }
}
button {
margin: 1em;
}
<div class="flipping-images image1">
<img src="https://via.placeholder.com/100x100/fdc34f/FEFEFE?text=1">
<img src="https://via.placeholder.com/100x100/3e72ff/FEFEFE?text=2">
<img src="https://via.placeholder.com/100x100/222222/FEFEFE?text=3">
</div>
<button id="next">Next</button>
I want to make the first square appear after 3s and then it needs to disappear. After it disappears, the second square becomes visible after 11s. How to make the second square appear only after the first one has disappeared after 11 seconds?
.one, .two{
background-color: black;
height: 50px;
width: 50px;
}
.one{
animation: fadein 3s, fadeout 7s ;
}
.two{
animation: fadein 11s, fadeout 17s ;
}
#keyframes fadein {
from { opacity: 0; }
to { opacity: 1; }
}
#keyframes fadeout {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
<div class="one"></div>
<br>
<div class="two"></div>
Use animation-delay maybe?
Also note that you cannot animate the same css attribute in multiple keyframes on the same element. The css defined in the last keyframe will always override the earlier ones. You can try to use % to achieve something similar to what you want.
.one, .two{
background-color: black;
height: 50px;
width: 50px;
opacity: 0;
}
.one{
animation: fadeinout1 10s;
}
.two{
animation: fadeinout2 28s ;
animation-delay: 10s ;
}
#keyframes fadeinout1 {
0%, 100% { opacity: 0; }
30% { opacity: 1; } /*Simulate 3s, out of the whole animation of 10s*/
}
#keyframes fadeinout2 {
0%, 100% { opacity: 0; }
39% { opacity: 1; } /*Simulate 11s, out of the whole animation of 28s*/
}
<div class="one"></div>
<br>
<div class="two"></div>
$(function() {
$('.text1').delay(1000).fadeIn(1500);
$('.text1').delay(600).fadeOut(1500);
$('.text2').delay(5000).fadeIn(1500);
$('.text2').delay(600).fadeOut(1500);
$('.text3').delay(10000).fadeIn(1500);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="text1">Lorem Ipsem</div>
<div class="text2">Lorem Ipsem</div>
<div class="text3">Lorem Ipsem</div>
Above is my simple jQuery animation; simple delay -> fadeIn -> delay -> fadeOut. However I find when trying to create a loop, for my animations to run continuously my code becomes way to large and bulky. I am wondering if it's at all possible to rewrite what I have above with CSS3 only, and then using the infinite option CSS allows.
I've gotten close with CSS below code however, I need to completely hide or fadeOut each line of text before new text shows.
#-webkit-keyframes slider {
0% { opacity: 0.4; }
100% { opacity: 1; }
}
#-moz-keyframes slider {
0% { opacity: 0.4; }
100% { opacity: 1; }
}
#-ms-keyframes slider {
0% { opacity: 0.4; }
100% { opacity: 1; }
}
.slider {
-webkit-animation: slider 1s alternate infinite;
-moz-animation: slider 1s alternate infinite;
-ms-animation: slider 1s alternate infinite;
}
<div class="slider">Lorem Ipsum</div>
As stated in other answers you can not achieve what you are asking using pure CSS solutions.
You can a solution like to an extensible approach (in case you want have many more child elements).
$("#fadeinout div").on("animationend", function() {
_this = jQuery(this);
// remove animation class
_this.removeClass("animate");
// If there is no next element then go to first one otherwise choose next element
var next = (_this.next().length < 1) ? _this.prevAll(':first-child') : _this.next();
// Add class to the new element
next.addClass("animate");
});
#fadeinout div {
width: 100px;
height: 100px;
background: red;
opacity: 0;
margin: 5px;
}
.animate {
animation-name: fadeinout;
animation-duration: 4s;
animation-delay: 0s;
}
#keyframes fadeinout {
50% {
opacity: 1;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="fadeinout">
<div class='animate'></div>
<div></div>
<div></div>
<div></div>
</div>
This is the same example with many child elements animated: https://jsfiddle.net/j4zdgopr/1/
You can't really time the animation of multiple elements in css only. Well you could probably fake it with something like:
div {
width: 50px;
height: 50px;
background: #f00;
opacity: 0;
}
#d1 {
animation: d1 10s infinite;
}
#d2 {
animation: d2 10s infinite;
}
#d3 {
animation: d3 10s infinite;
}
#d4 {
animation: d4 10s infinite;
}
#keyframes d1 {
0% { opacity: 0; }
5% { opacity: 1; }
20% { opacity: 1; }
25% { opacity: 0; }
}
#keyframes d2 {
25% { opacity: 0; }
30% { opacity: 1; }
45% { opacity: 1; }
50% { opacity: 0; }
}
#keyframes d3 {
50% { opacity: 0; }
55% { opacity: 1; }
70% { opacity: 1; }
75% { opacity: 0; }
}
#keyframes d4 {
75% { opacity: 0; }
80% { opacity: 1; }
95% { opacity: 1; }
100% { opacity: 0; }
}
<div id="d1"></div>
<div id="d2"></div>
<div id="d3"></div>
<div id="d4"></div>
...but I would recommend against it. First of all I don't think the timing will be reliable - ie. it will get out of sync. Second your code will most likely be even more bulky than what you have.
So I would recommend a combination of simple CSS transitions and JS like this:
var curslide = 0;
var slides = $("#slider div");
var nextslide = function() {
slides.removeClass('shown');
if (curslide >= slides.length) curslide = 0;
slides.eq(curslide).addClass('shown');
curslide++;
setTimeout(nextslide, 3000);
}
nextslide();
#slider div {
width: 50px;
height: 50px;
background: #f00;
opacity: 0;
transition: opacity .5s linear;
}
#slider div.shown {
opacity: 1;
transition: opacity .5s .5s linear;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="slider">
<div id="d1"></div>
<div id="d2"></div>
<div id="d3"></div>
<div id="d4"></div>
</div>
With this solution you can add as many frames as needed without modifying the css or js.
I'd try playing with animation-delay, but I think it only applies to the first time the animation is run. After that, unless you write one animation for each .text element, they'll all fade in/out with the same frequency.
Would:
setInterval(function(){
$('.text1').delay(5000).fadeIn(1500);
$('.text2').delay(5000).fadeIn(1500);
$('.text3').delay(5000).fadeIn(1500);
$('.text1').delay(1000).fadeOut(1500);
$('.text2').delay(5000).fadeOut(1500);
}, 11500);
get you somewhere close?
Im trying to animate the gold blocks in the JSFiddle.
All gold blocks should be invisible, then the first one fades in, followed by the second, then the third fourth and fifth, giving a tower of gold blocks.
#keyframes twinkle {
0% {
opacity: 0;
}
10% {
opacity: 1;
}
100% {
opacity: 1;
}
}
It works fine for the first round of the animation. But when it loops all gold blocks are on.
Any ideas where I am going wrong?
You werent resetting your loop before it queued again. here is a working fiddle with the css changes below.
#keyframes twinkle {
0% {
opacity: 0;
}
20% {
opacity: 1;
}
30% {
opacity: 1;
}
40% {
opacity: 1;
}
60% {
opacity: 1;
}
70% {
opacity: 1;
}
80% {
opacity: 1;
}
90% {
opacity: 0;
}
}
edit 1:
So I had to modify a few things within the fiddle. First there is now a small bit of JS so it resets.
setInterval(function (){
window.elm = document.getElementById('main-block'),
window.newone = elm.cloneNode(true);
elm.parentNode.replaceChild(window.newone, window.elm);
}, 5000);
Here is the modified HTMl markup.
<div id="main-block">
<div class="block one"></div>
<div class="block two"></div>
<div class="block three"></div>
<div class="block four"></div>
<div class="block five"></div>
</div>
And the final CSS
.block{
background: gold;
margin: 1rem;
width: 30px;
height: 30px;
opacity: 0;
}
#keyframes twinkle {
0% {
opacity: 0;
}
25% {
opacity: 1;
}
100% {
opacity: 1;
}
}
.one{
animation: twinkle 5s ease-in-out infinite;
}
.two{
animation: twinkle 5s 1s ease-in-out infinite;
}
.three{
animation: twinkle 5s 2s ease-in-out infinite;
}
.four{
animation: twinkle 5s 3s ease-in-out infinite;
}
.five{
animation: twinkle 5s 4s ease-in-out infinite;
}
https://jsfiddle.net/kx7bf19g/4/
You must reset the opacity to 0.
Try this, played around with key frames :)
5 different keyframes were created
Just adjust them a little bit.
https://jsfiddle.net/y7q1jj7g/2/ - working fiddle
This is the first keyframe:
#keyframes twinkle {
9% { opacity: 0; }
10% { opacity: 1; }
20% { opacity: 1; }
99% { opacity: 1; }
100% { opacity: 0; }
}
According to mozilla the delay is only applied initially, when the animation is applied. This is done on page load by the browser, applying the css. Thus, the initial delay is not loopable.
There is a scripted solution that toggles a class in order to start and stop the animation using a toggle.
Here's a fiddle demonstrating the principle.
However, removing and adding a CSS class in two consecutive statements does not work - it gets optimized away by a rendering path. I've tried setTimeout and requestAnimationFrame but to no avail.
The Issue
I have two css keyframe animations which I am running on a single element:
.fade-bg {
animation-name: fade-bg-1, fade-bg-2;
animation-delay: 0, 6s;
animation-iteration-count: infinite;
animation-direction: alternate;
}
The animations are defined as such:
#keyframes fade-bg-1 {
from {
opacity: 0;
background-image: url(image-1.jpg);
}
50% {
opacity: 1;
background-image: url(image-1.jpg);
}
to {
opacity: 0;
background-image: url(image-1.jpg);
}
}
#keyframes fade-bg-2 { /* Same as fade-bg-1 only with image-2.jpg */ }
The above works but when it gets to the second animation, it keeps repeating only that animation and does not loop back to fade-bg-1.
I've tried many different combinations of animation-direction but to no avail.
The Question
How do I make it so that the animation returns to fade-bg-1 and repeats itself?
The Example
EXAMPLE
Without javascript I don't think you can. However you can achieve the same effect using a single keyframe animation.
.fade-bg {
animation-name: fade-bg;
animation-delay: 0;
animation-iteration-count: infinite;
animation-direction: forward;
}
#keyframes fade-bg {
0% {
opacity: 0;
background-image: url('image-1.jpg');
}
25% {
opacity: 1;
background-image: url('image-1.jpg');
}
50% {
opacity: 0;
background-image: url('image-1.jpg');
}
51% {
opacity: 0;
background-image: url('image-2.jpg');
}
75% {
opacity: 1;
background-image: url('image-2.jpg');
}
100% {
opacity: 0;
background-image: url('image-2.jpg');
}
}
EXAMPLE
I'm not sure this is possible with just css, but if you set up a setInterval method in JS cleverly, you could probably simulate the same thing by splitting the class into two.
var index = 1;
function switchBackground() {
if (index == 1) {
//this switches to the first background
var div = document.getElementById("yourDiv");
div.className = "fade-bg-1";
index = 0;
}
else {
//this switches to the second background
var div = document.getElementById("yourDiv");
div.className = "fade-bg-2";
index = 1;
}
}
setInterval(switchBackground(), 6000);
With .fade-bg-1 and .fade-bg-2 being the two animation classes.
Here's a jsfiddle if you want to play with it.
I asked this question almost 6 years ago, much has changed but no real solution with pure css.
The closest I could come up with was using a pseudo element to apply the second animation to.
Possible Solution:
Use a pseudo element like ::after and apply the second animation to it.
Code:
.animation--fade-bg, .animation--fade-bg::after {
width: 500px;
height: 500px;
background-size: cover;
background-position: 50%;
background-repeat: no-repeat;
animation-iteration-count: infinite;
animation-duration: 3s;
}
.animation--fade-bg::after {
content: "";
display: block;
animation-direction: reverse;
}
.animation--fade-bg-1 {
animation-name: fade-bg-1;
}
.animation--fade-bg::after {
animation-name: fade-bg-2;
}
#keyframes fade-bg-1 {
from {
opacity: 0;
background-image: url('http://i.imgur.com/sRnvs0K.jpg');
}
50% {
opacity: 1;
background-image: url('http://i.imgur.com/sRnvs0K.jpg');
}
to {
opacity: 0;
background-image: url('http://i.imgur.com/sRnvs0K.jpg');
}
}
#keyframes fade-bg-2 {
from {
opacity: 0;
background-image: url('http://i.imgur.com/wL4RT1w.jpg');
}
50% {
opacity: 1;
background-image: url('http://i.imgur.com/wL4RT1w.jpg');
}
to {
opacity: 0;
background-image: url('http://i.imgur.com/wL4RT1w.jpg');
}
}
<div class="animation--fade-bg animation--fade-bg-1"></div>