Fade between multiple images from same URL without refreshing page - javascript

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>

Related

Cant control the Numerical variable trigger condition with the OnClickEvent. OnClickEvent triggering multiple times

first time post here. a beginner.
What I'm trying to do here is that:
I'm trying to display an image of a silhouette of a person. And with every click, the current image fades out and another image of the same person (different position/location) fades in. kind of like an animation except very bare bones.
I made a variable that supposedly controls when certain images fade in or out(var currentscene).The button that calls the function covers the entire page and is invisible because I want the animation to move frame by frame with a click anywhere from the page.
('stand' is an image of a boy standing and 'sit' is an image of a boy sitting) edit: in the example it becomes squareblue and squarered.
(fade-in and fade-out are classes that I add to the image so the transition triggers)
And yet the problem is that a single click will fade in both pictures, as if the number on the variable (currentscene) doesn't matter or the onclick event registered infinite number of clicks or something like that.
Im trying to make the blue square fade in on the first click and the blue fade out and the red fade in on the second click of the button.
If there is a fix to this that would be helpful. If there is a whole another way to do what I'm trying to accomplish, I'm all ears. Although I've tried looking into arrays and EventListeners but I haven't been successful.
heres a recreation of the problem:
var currentscene = 0;
function next() {
currentscene++;
if (currentscene = 1) {
var element = document.getElementById("blue");
element.classList.add("fade-in");
}
if (currentscene = 2) {
var element = document.getElementById("blue");
element.classList.add("fade-out");
var element = document.getElementById("red");
element.classList.add("fade-in");
}
}
.squareblue {
height: 50px;
width: 50px;
top: 50px;
background-color: blue;
position: absolute;
opacity: 0;
animation-fill-mode: forwards;
}
.squarered {
height: 50px;
width: 50px;
top: 100px;
background-color: red;
position: absolute;
opacity: 0;
animation-fill-mode: forwards;
}
.fade-out {
animation: fadeOut ease 2s
}
#keyframes fadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
.fade-in {
animation: fadeIn ease 2s
}
#keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
<div2 id="blue" class="squareblue"></div2>
<div2 id="red" class="squarered"></div2>
<button class="button" onclick="next()">next</button>

How to quickly change classes twice on one button click

What I'm trying to do:
Make a transition between Link pages in React, so when one clicks on a nav button, a slider animation pops in from the right, quickly slides over the window, and then back, in one swift motion, and then goes to the next page.
What I've tried:
A lot. Before I was trying to use a useState function, but couldn't figure out how to make it switch back to the original class, or remove the class. Currently I have this.
const sliderHandler = () => {
const slider = document.getElementById('slider');
slider.addClass('slider-close');
setTimeout(RemoveClass, 1000)
}
function RemoveClass() {
slider.removeClass('slider-close')
}
/More Code/
<div id="slider" ></div>
Styles
#slider {
position: absolute;
top: 0;
right: 0;
min-height: 100vh;
background-color: #202c3f;
transition: all .4s ease-in-out;
z-index: 1001;
}
#keyframes slide {
0% {width: 0vw;}
40% {width: 100vw;}
60% {width: 100vw;}
100% {width: 0vw;}
}
.slider-close {
transition: all .1s ease-in-out;
animation-name: slide;
animation-duration: 0.4s;
}
In the above code I'm trying to make it so on button click, it adds the class .slider-close, waits, and then removes it. When I attempt it comes up with either "slider.addClass is not a function" or "slider is null".
This seems like a simple function that I just cannot wrap my head around, sp any help appreciated.

Crossfade images in Safari not working

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!

CSS - Play animation while hovering, but not with an abrupt end

I am trying to create a sort of loading animation, with 3 bars that are below eachother that all have seperate keyframes.
The 3 bars are div elements, located inside a parent div.
<div id="menu">
<div id="menubox1"></div>
<div id="menubox2"></div>
<div id="menubox3"></div>
</div>
The animation properties are assigned to the individual menubox ids.
#menubox1:after {
content: '';
position: absolute;
bottom: 0px;
transform: translateY(-50%);
border-top: 1px solid #FFDADA;
animation: menukeyframes1;
animation-duration: 2000ms;
animation-iteration-count: infinite;
animation-timing-function: ease-in-out;
animation-play-state: inherit;
}
#keyframes menukeyframes1 {
0% { width: 100%; left:0;}
...
}
My goal is to play the animation while the cursor is hovering over the parent div.
My attempt was to play around with animation-play-state which was set to running or paused, depending if the parent div was hovered.
The problem is that the animation is immediatly paused, before the animation is complete, which looks kind of bad if it stops mid-motion.
Is there a good fix for this, preferrably without JavaScript/jQuery, and across all browsers?
As you see it can't be done with just CSS at this moment, and as good jquery answers are already referenced, it's worth to mention that it could be solved in few lines of vanillaJS:
var dur = 2000;
document.querySelectorAll('.smooth').forEach(el=>{
var t;
el.addEventListener('mouseover',_=>{t = performance.now();el.style.animationPlayState = 'running'})
el.addEventListener('mouseout',_=>window.setTimeout(()=>el.style.animationPlayState = 'paused',dur-(performance.now()-t)%dur));
})
working pen
non-es6: BABEL
You can always fade out the animated divs using transitions.
Something like this might work for you:
#menubox1 {
opacity: 0;
transition: opacity .5s linear;
}
#menu:hover {
#menubox1 {
opacity: 1;
transition: opacity .5s linear;
}
}

Removing classes from element without affecting css transitions in progress

Ok, I have a situation where I have basically built a little notification dropdown box that happens when the user does something, at the end it transitions to a opacity: 0; state.
However, because the user may click something else that will trigger this notification box again I am trying to come up with a way to reset it back to normal without affecting any in-progress transitions and attempting to keep the animation done by CSS rather than JavaScript.
CodePen:http://codepen.io/gutterboy/pen/WoEydg
HTML:
Open Notify Window
<div id="top_notify" class="top-notify">
<div class="container-fluid">
<div class="row">
<div class="content col-xs-12">
<div class="alert" role="alert"></div>
</div>
</div>
</div>
</div>
SCSS:
body {
text-align: center;
padding-top: 150px;
}
.top-notify {
position: fixed;
top: 0;
width: 100%;
z-index: 9999;
.content {
text-align: center;
background-color: transparent;
transform-style: preserve-3d;
}
.alert {
display: inline-block;
transform: translateY(-100%);
min-width: 250px;
max-width: 500px;
border-top-left-radius: 0;
border-top-right-radius: 0;
&.visible {
transform: translateY(0%);
transition: 0.8s 0s, opacity 1s 3.8s;
opacity: 0;
}
}
}
JS:
$('a').on('click', function(e){
e.preventDefault();
myFunc();
});
function myFunc() {
// Set file to prepare our data
var loadUrl = "https://crossorigin.me/http://codepen.io/gutterboy/pen/ObjExz.html";
// Run request
getAjaxData(loadUrl, null, 'POST', 'html')
.done(function(response) {
var alert_el = $('#top_notify').find('.alert');
// Update msg in alert box
alert_el.text(response);
alert_el.addClass('alert-success');
// Slide in alert box
alert_el.addClass('visible');
})
.fail(function() {
alert('Problem!!');
});
// End
}
function getAjaxData(loadUrl, dataObject, action, type) {
return jQuery.ajax({
type: action,
url: loadUrl,
data: dataObject,
dataType: type
});
}
I know I can reset it back to normal by doing this in JS:
$('#top_notify').find('.alert').removeClass().addClass('alert'); // The classes it ends up with vary
...however doing this removes the classes before the transition is finished fading out the opacity and it just vanishes straight away.
I know I can do a delay in JS to counteract the CSS delay but doing it that way just doesn't seem a very good way to do it since you have the timings in 2 different places.
Is there any way I can accomplish this whilst keeping the animation done by CSS or will I have to move to using jQuery's animate so I can run the reset procedure once the animation is complete?
Ok, I came up with a simple solution after coming up with a convoluted one ha.
Simple solution I should have come up with in the first place was removing any additional added classes before the ajax call; I got too focused on doing it within the ajax block and of course that didn't work, but until I started playing around with the other solution I never tried it.
Any way, the simple solution is simply moving this code:
var alert_el = $('#top_notify').find('.alert');
...above the ajax call, rather than being inside of it.
Then adding this directly under it:
alert_el.removeClass('visible alert-success alert-info alert-danger alert-warning');
With the full function code being:
function myFunc() {
// Set file to prepare our data
var loadUrl = "https://crossorigin.me/http://codepen.io/gutterboy/pen/ObjExz.html";
var alert_el = $('#top_notify').find('.alert');
alert_el.removeClass('visible alert-success alert-info alert-danger alert-warning');
// Run request
getAjaxData(loadUrl, null, 'POST', 'html')
.done(function(response) {
// Update msg in alert box
alert_el.text(response);
alert_el.addClass('alert-success');
// Slide in alert box
alert_el.addClass('visible');
})
.fail(function() {
alert('Problem!!');
});
// End
}
CodePen: http://codepen.io/gutterboy/pen/xRXbXy
The other solution I came up with, whilst not really needed now, I thought I would post it anyway in-case it comes in handy for me (or someone) else in the future.
It doesn't remove the visible class after the animation is finished (as there is no way that I know of to alert JS when it's done) but the visible class - which I would change the name of if you use this method - doesn't add any new styles, it just runs the animation.
Here is how I did it:
The JavaScript remains the same as the solution above, it's all in the CSS.
TLDR;
Basically uses multiple CSS animations to control different states during the effect runtime; CodePen at bottom.
The changes being in the .visible class and the addition of some #keyframes.
.visible class:
&.visible {
animation: slideDown 0.8s 0s, keepThere 3s 0.8s, fadeAway 1s 3.8s;
}
As you can see we have gotten rid of any additional styling here - this means when the animation is done, it essentially resets back to normal, which is what we want.
Now, let's break down this code:
We are running 3 different animations here and it's important to note they don't run one after the other - meaning they don't wait until one is finished until it starts the next one, hence why we needed to include delay settings.
So first up we start with the slideDown animation:
slideDown 0.8s 0s
If you are new to animations in CSS then basically what this does is sets a delay of 0s before it starts running and the animation runs for 0.8s, and this is the animation:
#keyframes slideDown {
0% {
transform: translateY(-100%);
}
100% {
transform: translateY(0%);
}
}
So, pretty simple, just slides it down with transform from -100% to 0% and this animation takes 0.8s as we set in our call to this animation.
Now, I wanted this to stay visible for 3 seconds before it started to fade away, but we have a problem; once the animation ends then it goes back to it's standard styling, which in our case means it vanishes as it goes back to transform: translateY(-100%) since we have no extra styles in the .visible class, and we can't put any extra styles in there as then we won't be able to reset it back to it's original state (style wise).
But what do we do? The fadeAway animation doesn't start for another 3 seconds and at the moment it doesn't have anything to fade away (well it does, but you can't see it as it's hidden).
The solution to that was adding another animation - which technically doesn't really animate anything, it just keeps it visible until the fadeAway animation starts.
That's where we get to:
keepThere 3s 0.8s
Now, remembering the settings of our fadeAway animation are: fadeAway 1s 3.8s this means that we have 3 seconds before this animation is going to start and hence before we can control any of the styling with it.
So that's where these parameter values comes in - we set the delay to 0.8s so the keepThere animation doesn't start until the slideDown one has finished; then we set the duration for 3s to counter for the wait time until the fadeAway animation starts, and this is the keepThere animation:
#keyframes keepThere {
0%, 100% {
transform: translateY(0%);
}
}
Since it has the same start and end styling we combine it into one selector of 0%, 100% and as you can see, this does just what it says it does, keeps the element visible for the set duration of 3s until we can control the styling with the fadeAway animation.
I guess technically you could combine this functionality into the fadeAway animation if you wanted to do the math at what % equals 3 seconds and hence know when to start fading the element away.
Lastly we have the fadeAway animation:
fadeAway 1s 3.8s
Now as we have discussed above, we already know why we have set the delay to 3.8s, the 0.8s offset to allow the slideDown animation to run and an additional 3s delay as that's how long we want the element to be visible for until it starts fading away and then of course the fade takes 1s to complete.
The animation for this is:
#keyframes fadeAway {
0%, 100% {
transform: translateY(0%);
}
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
Now, since the keepThere animation has completed, we have to make sure to keep the element visible so the fade has something visible to actually fade away, that's why we make sure to include the style transform: translateY(0%); as a value from start to finish; after that it's quite obvious what it's doing I think.
Put it all together and you get:
.top-notify {
position: fixed;
top: 0;
width: 100%;
z-index: 9999;
.content {
text-align: center;
background-color: transparent;
transform-style: preserve-3d;
}
.alert {
display: inline-block;
transform: translateY(-100%);
min-width: 250px;
max-width: 500px;
border-top-left-radius: 0;
border-top-right-radius: 0;
&.visible {
animation: slideDown 0.8s 0s, keepThere 3s 0.8s, fadeAway 1s 3.8s;
}
}
}
#keyframes slideDown {
0% {
transform: translateY(-100%);
}
100% {
transform: translateY(0%);
}
}
#keyframes keepThere {
0%, 100% {
transform: translateY(0%);
}
}
#keyframes fadeAway {
0%, 100% {
transform: translateY(0%);
}
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
CodePen: http://codepen.io/gutterboy/pen/QGqwBg
Then of course to be able to run it again the class has to be re-added and hence that was the purpose of removing the .visible class at the start of each run (before the ajax call) and then when it gets re-added during the ajax call it runs again.
Thanks to #Nathaniel Flick for sharing the link that led me down this path to begin with :)
Well, hopefully that comes in handy for someone seeing as I am no longer going to use that option ha!

Categories

Resources