Javascript Frame Animation Flashing on Load - javascript

Here is the CodePen of the animation. It flashes for the first cycle of the frames displayed. Is there a way to stop this from happening?
Any help would be very much appreciated!
let frames = [
"http://i.imgur.com/QhvQuaG.png",
"http://i.imgur.com/VjSpZfB.png",
"http://i.imgur.com/Ar1czX0.png",
"http://i.imgur.com/ROfhCv4.png",
"http://i.imgur.com/6B32vk7.png",
"http://i.imgur.com/2t5MWOL.png",
"http://i.imgur.com/a9wLBbc.png",
"http://i.imgur.com/OBKcW8f.png",
"http://i.imgur.com/RC6wLgw.png",
"http://i.imgur.com/2HyI8yS.png"];
let startframe = 0;
function arrow(){
let start = Date.now();
let timer = setInterval(function() {
let timePassed = Date.now() - start;
if (timePassed >= 20000) {
clearInterval(timer); // finish the animation after 2 seconds
return;
}
move();
}, 200);
}
function move(){
if (startframe==(frames.length-1)){
startframe=0;
} else {
startframe++;
}
// document.getElementById('continue').style.backgroundSize = "100%";
document.getElementById('continue').style.background = "url(" + frames[startframe] +")";
document.getElementById('continue').style.backgroundSize = "100%";
}
#continue {
width: 80px;
height:40px;
}
<div onclick = "arrow()">Start</div>
<div id="continue"></div>

If you look at the network tab of your browser dev tools, you'll see that the flashing happens when the browser is loading the images.
You should preload all the images before starting the animation, like so:
let frames = [
"http://i.imgur.com/QhvQuaG.png",
"http://i.imgur.com/VjSpZfB.png",
"http://i.imgur.com/Ar1czX0.png",
"http://i.imgur.com/ROfhCv4.png",
"http://i.imgur.com/6B32vk7.png",
"http://i.imgur.com/2t5MWOL.png",
"http://i.imgur.com/a9wLBbc.png",
"http://i.imgur.com/OBKcW8f.png",
"http://i.imgur.com/RC6wLgw.png",
"http://i.imgur.com/2HyI8yS.png"
]
var startframe = 0
var images = [] // This array will contain all the downloaded images
function preloadImages() {
var loaded = 0
for (i = 0; i < frames.length; i++) {
images[i] = new Image();
images[i].onload = function() {
loaded += 1
if (loaded >= frames.length) {
arrow()
}
}
images[i].src = frames[i]
}
}
function arrow(){
let start = Date.now();
let timer = setInterval(function() {
let timePassed = Date.now() - start;
if (timePassed >= 20000) {
clearInterval(timer) // finish the animation after 2 seconds
return;
}
move()
}, 200)
}
function move() {
var c = document.getElementById('continue')
c.innerHTML = '' // remove the content of #continue
// Insert the already exiting image from the images array
// into the container instead of downloading again with css
c.append(images[startframe])
if (startframe >= frames.length - 1) {
startframe = 0
}
else {
startframe++
}
}
#continue {
width: 80px;
height:40px;
}
#continue > img {
max-width: 100%;
}
<div onclick = "preloadImages()">Start</div>
<div id="continue"></div>

This is because the images need to be loaded when viewed for the first time. It is possible to pre-load images in different ways. Here are three ways to preload images.

Related

How can I make the slideshow stop? And what ways can I improve the code?

I have a slideshow that (for testing purposes) swipes through every 1 second. I have it to stop when it gets back to the first image. But when it stops on it and I click on the next button nothing happens. And when I do mess with it it will start swiping through again.
I've tried while statements but that's really it.
My code:
var i = 0;
var images = [];
var time = 1000;
images[0] = 'michael-baird-14942-unsplash.jpg';
images[1] = 'kees-streefkerk-352781-unsplash.jpg';
images[2] = 'hakon-sataoen-1484216-unsplash.jpg';
images[3] = 'mario-silva-1492028-unsplash.jpg';
images[4] = 'will-turner-1474611-unsplash.jpg';
function changeImg() {
document.slide.src = images[i];
if (i < images.length) {
i++;
} else if (i > 4) {
document.slide.src = images[0];
} else {
i = 0;
}
if (i == 0) {
$('.next').on('click', function() {
i++;
});
}
setTimeout("changeImg()", time);
}
window.onload = changeImg;
.slideshow {
width: 100%;
height: 75%;
position: absolute;
top: 42px;
}
.slideshow img {
object-fit: cover;
width: 100%;
height: 100%;
}
<div class="slideshow">
<img name="slide" alt="Slideshow Images" width="100%" height="100%">
<a class="next">❯</a>
<a class="prev">❮</a>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="masterjs.js" charset="utf-8"></script>
As you can see in the JavaScript it goes through and stops at the first image. But as you can also see that this isn't a very good way to do this. If you guys can can you try to keep it in JavaScript/jQuery? If not or there is a better way please post it. But basically because of the way that it is it cycles through stops at the beginning and stays but if you mess with it VIA the next button it causes it to restart. I basically want it to cycle through the images and then stop at the beginning allowing the user to then cycle through them themselves. Also I want to add animations between images that's why I am asking for other ways that may be better for that too.
The issue is with this condition:
else if (i > 4) {
document.slide.src = images[0];
}
Your image updates to the first element of the images array, and your counter stops incrementing. Try mentally running through your code while keeping track of the value of i. Once this condition is met, does it ever stop being met?
The solution is simple, just reset the value of i when you reset the image:
else if (i > 4) {
document.slide.src = images[0];
i = 0;
}
You may then run into issues with your event listener as it triggers when i == 0, and therefore will be triggered when i is reset as above. There are some good reasons to use i === 0 instead, I recommend looking into it!
In addition, you should consider using a array.push() to add to the images array.
Finally I'd also recommend using console.log() as a way to keep track of what your code is actually doing -- this should aid in the debugging process.
Refer Below javascript code
<script type="text/javascript">
var i = 0;
var firstSlide = 0;
var images = [];
var time = 1000;
images[0] = 'https://i.ytimg.com/vi/SiVr9DM_EG0/maxresdefault.jpg';
images[1] = 'https://upload.wikimedia.org/wikipedia/en/d/d4/Mickey_Mouse.png';
images[2] = 'https://files.brightside.me/files/news/part_41/419860/18057510-1549810-23-0-1513767199-1513767212-650-1-1513767212-650-ce9f862cd3-1513864405.jpg';
images[3] = 'http://ichef.bbci.co.uk/news/999/mcs/media/images/80286000/png/_80286528_penisvagina.png';
images[4] = 'https://d345cba086ha3o.cloudfront.net/wp-content/uploads/2015/12/Chota-Bheem-Aur-Krishna-Drawing-e1451484119715.jpg';
function changeImg() {
if (i <= images.length && firstSlide == 0) {
document.slide.src = images[i];
if (i < images.length) {
i++;
} else if (i > 4) {
document.slide.src = images[0];
} else {
i = 0;
}
setTimeout("changeImg()", time);
}
}
$('.next').on('click', function() {
firstSlide = 1;
i++;
var ind = (i % 5);
document.slide.src = images[ind];
});
$('.prev').on('click', function() {
firstSlide = 1;
i--;
var ind = (i % 5);
document.slide.src = images[ind];
});
window.onload = changeImg;
</script>

if body has class Fade in mp3 sound else fade out

I am trying to fade the volume of an mp3 in to 1 if the body has the class fp-viewing-0
How ever this isn't working and the volume doesn't change how can I fix this?
Code:
var audio0 = document.getElementById('audio-0');
audio0.volume = 0;
setInterval( function(){
if ($("body").hasClass("fp-viewing-0")) {
audio0.animate({volume: 1}, 1000);
}
else {
audio0.animate({volume: 0}, 1000);
}
}, 100);
HTML
<audio id="audio-0" src="1.mp3" autoplay="autoplay"></audio>
I've also tried:
$("#audio-0").prop("volume", 0);
setInterval( function(){
if ($("body").hasClass("fp-viewing-0")) {
$("#audio-0").animate({volume: 1}, 3000);
}
else {
$("#audio-0").animate({volume: 0}, 3000);
}
}, 100);
Kind Regards!
I have changed the jquery animate part to a fade made by hand. For that i created a fade time and steps count to manipulate the fade effect.
var audio0 = document.getElementById('audio-0');
audio0.volume = 0;
if ($("body").hasClass("fp-viewing-0")) {
audio0.volume = 1; //max volume
var fadeTime = 1500; //in milliseconds
var steps = 150; //increasing makes the fade smoother
var stepTime = fadeTime/steps;
var audioDecrement = audio0.volume/steps;
var timer = setInterval(function(){
audio0.volume -= audioDecrement; //fading out
if (audio0.volume <= 0.03){ //if its already inaudible stop it
audio0.volume = 0;
clearInterval(timer); //clearing the timer so that it doesn't keep getting called
}
}, stepTime);
}
Better would be to place all of this in a function that receives these values a fades accordingly so that it gets organized:
function fadeAudio(audio, fadeTime, steps){
audio.volume = 1; //max
steps = steps || 150; //turning steps into an optional parameter that defaults to 150
var stepTime = fadeTime/steps;
var audioDecrement = audio.volume/steps;
var timer = setInterval(function(){
audio.volume -= audioDecrement;
if (audio.volume <= 0.03){ //if its already inaudible stop it
audio.volume = 0;
clearInterval(timer);
}
}, stepTime);
}
Which would make your code a lot more compact and readable:
var audio0 = document.getElementById('audio-0');
audio0.volume = 0;
if ($("body").hasClass("fp-viewing-0")) {
fadeAudio(audio0, 1500);
}

Execute function IF another function is complete NOT when

I am having trouble creating a slider that pauses on hover, because I execute the animation function again on mouse off, if I flick the mouse over it rapidly (thereby calling the function multiple times) it starts to play up, I would like it so that the function is only called if the other function is complete, otherwise it does not call at all (to avoid queue build up and messy animations)
What's the easiest/best way to do this?
$(document).ready(function() {
//get variables
var slide_width = $('.slider_container').width();
var number_of_slides = $('.slider_container .slide').length;
var slider_width = slide_width*number_of_slides;
//set element dimensions
$('.slide').width(slide_width);
$('.slider').width(slider_width);
var n = 1;
$('.slider_container').hover(function() {
//Mouse on
n = 0;
$('.slider').stop(true, false);
}, function() {
//Mouse off
n = 1;
if (fnct == 0) sliderLoop();
});
//Called in Slide Loop
function animateSlider() {
$('.slider').delay(3000).animate({ marginLeft: -(slide_width * i) }, function() {
i++;
sliderLoop();
});
}
var i = 0;
var fnct = 0
//Called in Doc Load
function sliderLoop() {
fnct = 1
if(n == 1) {
if (i < number_of_slides) {
animateSlider();
}
else
{
i = 0;
sliderLoop();
}
}
fnct = 0
}
sliderLoop();
});
The slider works fine normally, but if I quickly move my mouse on and off it, then the slider starts jolting back and forth rapidly...been trying to come up with a solution for this for hours now..
Here's what fixed it, works a charm!
$(document).ready(function() {
//get variables
var slide_width = $('.slider_container').width();
var number_of_slides = $('.slider_container .slide').length;
var slider_width = slide_width*number_of_slides;
//set element dimensions
$('.slide').width(slide_width);
$('.slider').width(slider_width);
var n = 1;
var t = 0;
$('.slider_container').hover(function() {
clearInterval(t);
}, function() {
t = setInterval(sliderLoop,3000);
});
var marginSize = i = 1;
var fnctcmp = 0;
//Called in Doc Load
function sliderLoop() {
if (i < number_of_slides) {
marginSize = -(slide_width * i++);
}
else
{
marginSize = i = 1;
}
$('.slider').animate({ marginLeft: marginSize });
}
t = setInterval(sliderLoop,3000);
});

Javascript - loading images

I have a javascript which uses many images, and I want to load them all before the user starts clicking around. The images are a little bit big, so it takes a while. Here's my code:
startTest();
function preloadImages() {
console.log("preload images");
for(var i = 1; i <= 132; i++) {
var img = new Image();
images[i-1] = "images/"+i+".png";
img.src = images[i-1];
if(i == 132) {
img.load = doneLoading();
}
}
}
function doneLoading() {
console.log("done loading");
document.getElementById("laading").style.display = "none";
console.log("loading gone");
showReady();
}
function startTest() {
console.log("start test");
trialCount = 0;
document.getElementById("laading").style.display = "block";
preloadImages();
var hammertime = Hammer(document.body).on("touch", function(event) {
registerTouch(event);
});
startTestTime = +new Date();
console.log("end start test");
}
As you can see, startTest() is first called, which calls preload images. Here's the issue:
When I load the page in the browser, the javascript console indicates that "done loading" has been printed, however the spinny wheel on the tab in the browser shows that the webpage is still loading...
What's going on here? How can I figure out once all my images are done loading?
You need to check on each image load that the image is the last one to load. Check using a count variable (loaded).
function preloadImages() {
var loaded = 0;
for(var i = 0; i <= 132; i++) {
var img = new Image();
images.push("images/"+(i+1)+".png");
img.src = images[i];
img.onload = function(){
loaded++;
if(!(loaded < 132)){
// all images have loaded
doneLoading();
}
}
}
}
In order to do a pre-loading, you will have to keep checking the complete status for each image that is being downloaded. Since, you can't know beforehand how much time it will take for each image to download (depending on latency or size), you will have to implement a timer which will keep polling the complete status on each image.
Here is one possible algorithm:
var timer = null,
numImages = 132,
allImages = new Array;
function preLoadImages() {
document.getElementById('loadcaption').innerHTML = 'Loading images... ';
for (var i = 0; i < numImages; i++) {
var tmp = "images/" + i + ".png";
allImages[i] = new Image();
allImages[i].onLoad = imgOnLoad(tmp);
allImages[i].src = tmp;
}
timer = window.setInterval('checkLoadComplete()', 250);
}
function imgOnLoad(imgName) { window.status = 'Loading image... ' + imgName; }
function checkLoadComplete() {
for (var i = 0; i < allImages.length; i++) {
if (!allImages[i].complete) {
imgOnLoad(allImages[i].src);
document.getElementById('loadcaption').innerHTML = 'Loading images... ' + Math.round((i/allImages.length)*100,0) + ' %';
return;
}
}
window.clearInterval(timer);
document.getElementById('loadcaption').innerHTML = 'Completed.';
window.status = '';
}
Where loadcaption is a simple HTML element to show progress. (You can also use an animated gif for loading animation).
The idea is to keep checking the complete status on each image by calling checkLoadComplete by a timer. Once all images are (load) complete, the for loop exits and the timer is cleared.
You start the pre-loading process by calling preLoadImages(); function.

Preloading set of images using jQuery

I have a set of images that are frames in an animation like: frame-1.jpg, frame-2.jpg and I have about 400 images.
What I want to do is preload these 400 images before the animation starts.
Usually when preloading images I use the following:
var images = [
'img/nav-one-selected.png',
'img/nav-two-selected.png',
'img/nav-three-selected.png',
'img/nav-four-selected.png'
];
$(images).each(function() {
var image = $('<img />').attr('src', this);
});
But in this instance, listing the images in the var isn't feasible, and I also wish to fire off the start animation once the images have all been loaded.
So far I have the following:
$spinner_currentFrame = 1;
$numFrames = 400;
function preloadImages() {
$($images).each(function() {
var image = $('<img />').attr('src', this);
});
startAnimation();
}
function startAnimation() {
$spinner_loadingAnim = setInterval(function () {
UpdateSpinner();
}, 140);
}
function UpdateSpinner() {
$spinner_currentFrame = $spinner_currentFrame + 1;
if($spinner_currentFrame > $numFrames) {
$spinner_currentFrame = 1;
}
console.log($spinner_currentFrame);
$('#spinner').css("background-image", "url(frame-" + $spinner_currentFrame + ".jpg)");
}
$(document).ready(function() {
preloadImages();
});
So the plan is that I preload images that are from 1 to 400 and then once that's completed then start the animation. How would I build the $images var though?
I've thought about something like:
$images = [];
$frame = 1;
$numFrames = 400;
$($frame).each(function() {
$frame = $frame + 1;
if($frame <= $numFrames) {
$images =+ 'frame-' + $frame + '.jpg';
}
});
But I'm not sure how a) efficient this is and b) how to do the callback once all images have loaded successfully.
You should use a standard javascript for loop instead of the jQuery's foreach. Foreach is wonderful for looping over an array or set of objects, but not in this case. Here is an example, please note that you have to bind the onload event handler before you set the Image object's src property.
UPDATE: added more functions to complete the entire example.
var loaded_images = 0;
var frames = 400;
var images = [];
function preloadImages() {
for (i=0; i < frames; i++) {
images[i] = new Image();
images[i].onload = function() {
loaded_images += 1;
checkLoadingFinished();
}
images[i].src = 'frame-' + i + '.jpg';
}
}
function checkLoadingFinished() {
if (loaded_images >= frames) {
startAnimation();
}
}
function startAnimation() {
var frameNumber = 0;
var timer = setInterval(function() {
$('#img-dom-element').attr('src', images[frameNumber]);
if (frameNumber > frames) {
frameNumber = 0;
else
frameNumber++;
}, (1000/30)); // (1000/30) = 30 frames per second
}
$(document).ready(function() {
preloadImages();
});
I dont know if it fits to your special case but I use
http://thinkpixellab.com/pxloader/
to preload images. You can add the paths and get one callback if all images are loaded. Afterwards you can start animation.

Categories

Resources