I'm trying to make a page that changes background image when you click a button. Code pen: http://codepen.io/meek/pen/EPLZpW
body {
background: url('https://s3.eu-central-1.amazonaws.com/meek-img/1.jpg') no-repeat center center fixed;
background-size: cover;
-webkit-transition: background .5s ease-in-out;
-moz-transition: background .5s ease-in-out;
-o-transition: background .5s ease-in-out;
transition: background .5s ease-in-out;
}
The two big problems with the way I'm doing this currently are:
It flat out doesn't work in Mozilla Firefox.
The animation isn't smooth at all. It switches to a blank background for an instant, then the animation runs. Is there a way to prevent this, or is it unavoidable due to loading times? If so are there other ways to implement this function that would circumvent this? At first, I thought of animating with jQuery, but was told that this wasn't optimal.
Doing a transition on the full property background could possibly create some wonky effects. Especially when images are involved. Directly doing these transitions on background images is very demanding for your browser. And, as mentioned by you, does not behave consistently in different browsers.
A better idea is to do a transition or animation on opacity of an element (like a div) having that background property. This way the browser would not have to worry about transitioning the image, only about transitioning the div, which would result in a much easier task for your browser. If you want to change the background of your page, you would then simply add a div with that background image.
I've included a snippet, base on your code, to demonstrate the general idea. You'll also notice, I only preload the image when the image is requested as background image. This way the user only downloads the images he gets to see. My demo uses jQuery, but it could easily be integrated using vanilla JS.
function preload_img (src, callback) {
var img = new Image();
img.onload = function() {
if( callback && typeof callback === 'function' ) {
callback( img );
}
};
img.src = src;
}
var imgs = [
"https://s3.eu-central-1.amazonaws.com/meek-img/1.jpg",
"https://s3.eu-central-1.amazonaws.com/meek-img/2.jpg",
"https://s3.eu-central-1.amazonaws.com/meek-img/3.jpg",
"https://s3.eu-central-1.amazonaws.com/meek-img/4.jpg",
"https://s3.eu-central-1.amazonaws.com/meek-img/5.jpg",
"https://s3.eu-central-1.amazonaws.com/meek-img/6.jpg",
"https://s3.eu-central-1.amazonaws.com/meek-img/7.jpg",
"https://s3.eu-central-1.amazonaws.com/meek-img/8.jpg",
"https://s3.eu-central-1.amazonaws.com/meek-img/9.jpg"
];
function change_bg() {
var random_index = Math.floor(Math.random() * imgs.length);
preload_img( imgs[random_index], function( img ) {
$('<div>')
.addClass('bg-image')
.css('background-image', 'url(' + img.src + ')')
.appendTo('body')
;
} );
}
$( 'button' ).click( function() {
change_bg();
} );
change_bg();
#keyframes fadein
{
from { opacity: 0; }
to { opacity: 1; }
}
.bg-image
{
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
animation: fadein 1s;
background-size: cover;
}
.mimi
{
z-index: 1;
bottom: 0;
height: 100px;
left: 0;
margin: auto;
position: absolute;
top: 0;
right: 0;
width: 100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div class="mimi">
<button class="btn btn-default btn-lg">click me</button>
</div>
</body>
I'd consider loading the new image on top of the current image with 0 opacity and then transition the opacity to 1. After that you can remove the image below.
Also make sure that the image is fully loaded before starting the transition.
Adding the new image on change can be done with append() and then when the new image has loaded you can add a class to it with opacity 1.
Related
By cobbling together bits and pieces of various other answers from SO and elsewhere I have something that almost does what I want, but not quite:
I have to reload the page to get a new image.
Each new image fades in, but the previous image cuts out instantly rather fading across
Clicking on the image skips to the next one the same way.
The url I'm using returns a different image each time the server receives a request. I can change the URL to something else and it will still work. Is there a way to fade smoothly between images served in this way without refreshing the page? The other solutions I've found for this involve specifying a list of images somewhere in the page and rotating through them, which doesn't seem to work when every image has the same url.
I am trying to do this using only html + css + js.
<html>
<head>
<script type = "text/JavaScript">
<!--
function AutoRefresh( t ) {
setTimeout("location.reload(true);", t);
}
//-->
</script>
<style>
* {
margin: 0;
padding: 0;
}
.fade-in{
-webkit-animation: fade-in 2s ease;
-moz-animation: fade-in ease-in-out 2s both;
-ms-animation: fade-in ease-in-out 2s both;
-o-animation: fade-in ease-in-out 2s both;
animation: fade-in 2s ease;
visibility: visible;
-webkit-backface-visibility: hidden;
max-width: 100%;
max-height: 100vh;
margin: auto;
}
.imgbox {
display: grid;
height: 100%;
}
#-webkit-keyframes fade-in{0%{opacity:0;} 100%{opacity:1;}}
#-moz-keyframes fade-in{0%{opacity:0} 100%{opacity:1}}
#-o-keyframes fade-in{0%{opacity:0} 100%{opacity:1}}
#keyframes fade-in{0%{opacity:0} 100%{opacity:1}}
</style>
</head>
<body onload = "JavaScript:AutoRefresh(10000);">
<div class="imgbox">
<a href="javascript:location.reload(true)">
<img class = "fade-in" src="http://192.168.1.151:6600" style="object-fit:contain;display:block;margin-left:auto;margin-right: auto;" />
</a>
</div>
</body>
</html>
It is quite possible to use just JS/CSS/HTML to fade in and out between images without needing to reload the page.
To fade in and out between two images you'll need to have them both loaded. But in this case we cannot have two img elements with the same URL as the browser would assume the images were the same and therefore not ask the server for another one.
We get round this by adding a random query string to the end of the URL each time to persuade the browser to get another img. I'm using picsum and using their way of doing this, the method may differ for your service, though normally adding a random query to a URL doesn't upset the server, and it's only the browser we need to convince to send another request.
We load a new img with opacity: 0 so there isn't a sudden jump, and then we gradually fade it in while fading the other one out using your animation.
I have put the inline img styling into the head to tidy things up as we now have two images. I've removed the system-dependent prefixes just to make things easier to read, but of course put them back if you need them.
UPDATE: there was an additional requirement to not send requests to the image server too often. A pause flag has been added to indicate we cannot respond to the user's click which is unset after 2 seconds.
const imgs = [document.getElementById('img1'),document.getElementById('img2')];
let back=0; //0 or 1 depending on which img is faded out
let pause = false; //a flag to say it's too soon to get another image
let randomiser = 1;//we add this to the end of the img src to persuade the browser it really does need to get another image from the image server
function imgLoaded() {
imgs[back].style.animationDirection = 'normal';// will now fade in
back = (back + 1)%2;//make the other img the back
imgs[back].style.animationDirection = 'reverse'; // will now fade out
imgs[0].style.animationName = 'fade-in';
imgs[1].style.animationName = 'fade-in';
}
function next() {
if (pause) { return; }
imgs[0].style.animationName ='nothing';
imgs[1].style.animationName = 'nothing';
imgs[back].style.opacity = 0;// these should already be there, but just in case animation still on the go
imgs[(back+1)%2].style.opacity = 1;
randomiser++;
imgs[back].src = 'https://picsum.photos/1024/768.jpg?random='+ randomiser;
pause = true;
setTimeout(function () { pause = false; },2000);//dont ask for another image until 2 seconds is up
}
* {
margin: 0;
padding: 0;
}
body {
width:100vw;
height: 100vh;
}
.imgbox {
display: grid;
height: 100%;
}
#keyframes fade-in{0%{opacity:0} 100%{opacity:1}}
#keyframes nothing {}
.imgbox img {
object-fit:contain;
display:block;
animation-fill-mode: forwards;
animation-duration: 2s;
animation-iteration-count: 1;
position: absolute;
opacity:0;
width: 100%;
height: 100%;
margin-left: auto;
margin-right: auto;
}
<div class="imgbox" onclick="next();">
<img id="img1" onload="imgLoaded();" src="https://picsum.photos/1024/768.jpg?random=0"/>
<img id="img2" onload="imgLoaded();" src="https://picsum.photos/1024/768.jpg?random=0"/>
</div>
I am having trouble transitioning the width and height of an image. The image is supposed to appear small and then grow in size. However, when I add the second class, to transition the height and width, the image jumps to the next width and height without transitioning the properties. Any help is appreciated. Thanks in advance for any help!
I delayed the addition of the second class containing the adjusted CSS rules as I thought that adding the classes back to back was causing an error. After setting the delay with setTimeout() the function seems to appear slightly better as the image appears to move some but not in the intended way.
<div><img src="Some String"></div>
.lightbox-img { width: 100%; }
.light-box-small {
width: 20vw;
height: 10vh;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
transition: width 1s ease-in;
transition: height 1s ease-in; }
.lightbox-container { width: 60vw; height: 30vh; }
const images = document.querySelectorAll('.img');
const backdrop = document.querySelector('.backdrop');
const lightboxImg = document.createElement('img');
const container = document.createElement('div');
images.forEach(image => {
image.addEventListener('click', () => {
lightboxImg.src = image.src;
container.appendChild(lightboxImg);
container.classList.add('light-box-small');
lightboxImg.classList.add('lightbox-img');
backdrop.classList.add('dim');
document.body.appendChild(container);
setTimeout(() => {
container.classList.add('lightbox-container');
}, 500);
});
});
The expected result should be as follows:
1. The image appears in the center of the viewport
2. The image grows from the smaller size to the larger size.
The error is occurring on step 2 and jumps to the next width instead of transitioning.
Thank you C14L for offering help. Unfortunately applying the transition rules to the lightbox-container didn't work.
The problem turned out to be that when I declared the 2 CSS transition rules the second rule would overwrite the first rule. By declaring the rule in one declaration they wont override each other. So the solution is:
.light-box-small {
transition: width 1s ease-in, height 1s ease-in;
}
I cant use Jquery (just javascript).
I have a simple array of images that I find a random index values and appendChild(MyRandomImages) (3 images all at once) to the page. I need it to be appended with a transition animation ease-in 0.5s, ease-in 1s, ease-in 1.5s.
images = ["image/1.png",
"image/2.png",
"image/3.png",
"image/4.png",
"image/5.png",
"image/6.png"];
var img = document.createElement("img");
var img1 = document.createElement("img");
var img2 = document.createElement("img");
var randomVal = Math.floor(Math.random()*images.length);
var randomVal1 = Math.floor(Math.random()*images.length);
var randomVal2 = Math.floor(Math.random()*images.length);
img.src = images[randomVal];
img1.src = images[randomVal1];
img2.src = images[randomVal2];
var result = document.getElementsByClassName("result");
result[0].appendChild(img);
result[1].appendChild(img1);
result[2].appendChild(img2);
So result[0].appendChild(img) needs somehow to have preventDefault() and be animated into the scene, I tried invoking css3 animation by adding a new class that animates it. So if class:
.result {
width:0;
height:0;}
And I did
el.classList.add("animate"); where el is result class:
.animate{
-webkit-transition: 0.5s;
-moz-transition: 0.5s;
-ms-transition: 0.5s;
-o-transition: 0.5s;
transition: 0.5s;
width: 300px;
height: 300px;
}
but it didnt work. and I had the logical problem of how applying three classes to each one since I need them to be appended at different times (0.5, 1, 1.5s).
I would very much like to somehow invoke a css3 animation to do this and use no Jquery.
Thanks In advance.
To append images sequentially you can do:
use JS's .forEach(element, index) that:
creates a random image
appends the image to DOM
use setTimeout(()=> {/*job*/}, time * index ) where time*index will result in N timeouts like: 0, 500, 1500 .... having time set to 500ms and the job is to add your .animate class.
const images = [
"//placehold.it/300x300/0bf",
"//placehold.it/300x300/f0b",
"//placehold.it/300x300/bf0",
"//placehold.it/300x300/0fb",
"//placehold.it/300x300/b0f",
"//placehold.it/300x300/fb0",
];
const createImg = (el, i) => {
const img = document.createElement("img");
img.src = images[~~(Math.random() * images.length)];
el.appendChild(img);
setTimeout(()=> img.classList.add("animate"), i*500);
}
document.querySelectorAll(".result").forEach(createImg);
body {
display: flex;
}
.result img{
max-width: 100%;
transform: scale(0);
transition: 0.5s;
}
.result img.animate{
transform: scale(1);
}
<div class="result"></div>
<div class="result"></div>
<div class="result"></div>
If you want to append your images with a specified delay you could use setTimeout like this:
// append
setTimeout(function(){
// append
setTimeout(function(){
// append
}, 500)
}, 500)
This will append a image and will wait 500ms till the next will be appended. Then you still use the same duration time for your transition.
Regarding your animation issue:
Your image should have the transition property before you change the property to be animated.
So your img style has the transition property by default. After you appended your element in javascript you give it a class (in your case with the new property values of the height and width) with the different values in it, e.g.:
.result{
// with "all" you apply a transition to all css properties
transition: all 0.5s;
width: 0;
height: 0;
}
.visible{
width: 300px;
height: 300px;
}
So just then add the visible class to the image after you applied it
I'm working on a project where I need to make a dynamically loaded full-screen image background that crossfades new images every so many seconds.
The script works now fine in most browsers, but on Safari the transition effects don't apply. In stead of nicely transitioning to the next image, the image disappears for 1.5s (the time it should do over the transition).
Does anyone know how I can fix this? I've tried to solve it using webkits on the transition of the opacity, but it didn't work out.
Here some of the code
Script.js
const images = {
init(imageArray) {
elements.image_holders.forEach(function(image, index) {
image.src = `${config.imageUrl}${imageArray[index]}`;
config.atImage ++;
});
elements.image_groups[0].style.opacity = 1;
config.imageArray = imageArray;
this.startInterval();
},
startInterval() {
imageInterval = setInterval(images.changeImage, config.interval);
},
}
In the interval I change opacity every time again and load the new picture dynamically from the image Array.
_image_groups.scss
.__image_wrapper {
opacity: 0;
transition: opacity 1.5s linear;
-webkit-transition: opacity 1.5s linear;
-webkit-backface-visibility: hidden;
display: block;
width: 100%;
height: 100%;
img {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
}
That is the styling I use for the image to do it's transition effect. I work from opacity: 0 to opacity: 1.
Has anyone a idea why the transition doesn't work on Safari? I've looked around on the internet but couldn't find the answer. Thanks!
I have some CSS that scales images, which works fine.
The problem is it gets applied to every image on the page. What I want is to apply it ONLY to an image if I mouseover or onclick.
Because images are inserted by a CMS used by non-tech writers, they don't have the skills to get into the image tag itself to insert a class. This is why I want the scaling CSS triggered by mouseover or onclick.
I've struggled to get a piece of javascript to do the job and would appreciate some help
You just bind the event to the tag and use this:
var images = document.getElementsByTagName("img");
for (var i=0;i<images.length;i++) {
images[i].onmouseover = imgScale;
images[i].onclick = imgScale;
}
function imgScale(e) {
this.style.width = 500px; //whatever
this.setAttribute("class", "image-scale");
}
If jQuery:
$('img').on('hover click', function() {
$(this).css('width','500px');
$(this).addClass('image-scale');
});
Even better, if you only need on hover you can use just CSS:
img:hover {
width: 500px;
}
You could try using
img:active
To detect a click, but I believe it will only make changes while the mouse is pressed down, as soon as they let up it is no longer :active
You may try using CSS 3 for scaling. Have a look at this fiddle.
HTML:
<img src="http://lorempixel.com/200/200/sports"/>
<img src="http://lorempixel.com/200/200/nature"/>
<img src="http://lorempixel.com/200/200/city"/>
<img src="http://lorempixel.com/200/200/sports"/>
<img src="http://lorempixel.com/200/200/animals"/>
CSS:
img {
-webkit-transition: all 300ms 0ms ease-in-out;
transition: all 300ms 0ms ease-in-out;
width: 50px;
}
img:hover {
-webkit-transform: scale(1.4);
transform: scale(1.4);
}