replacing images inplace - javascript

I have a array of static images that I am using for an animation.
I have one frame image that I want to update the image of and I have seen a lot of tutorials on animating images with javascript just simply update the source of an image.
frame.src = animation[2].src; etc
When I look at the resource tracking in chrome, it doesnt look like they are getting cached even thought the web browser does download the image more than once but not once for each time it is displayed, so there is still some browser caching going on.
What is the best way to replace the frame image object with another image?

Well, you can either position all images absolute and give them a z-index, then use jQuery/JS to shuffle their z-indexes, bringing a new one to the top in a cross fader style,
or you can take all the id's and fadeone in slightly faster than the last one fades out.
Like so:
function fader(func) {
var currID = $('#mainimg ul').data('currLI');
var currLiStr = '#mainimg ul li#' + currID;
img = $(currLiStr).find('img').attr('src');
nextID = (currID == 'six') ? 'one' : $(currLiStr).next().attr('id');
nextLiStr = $('#mainimg ul li#' + nextID);
$(currLiStr).fadeOut(3000);
$(nextLiStr).fadeIn(2000).find('div.inner').delay(3000).fadeIn('slow').delay(6000).fadeOut('slow');
$('#mainimg ul').data('currLI',nextID);
}
Note 'six' is the id of the last li, reseting it back to one, but if you do $('#mainimg ul li:last').attr('id'); and $('#mainimg ul li:first').attr('id') to get the last and first id's, you can allow it to cope with any amount of images (obviously this is with li's given id's one, two and so on, but if you are finding out the last and first id you could use any structure.
Or you can set a ul width a width of all the li's multiplied, and give the li's the width of the images, and set overflow to hidden, then use JS to pull the li's left by the width of 1 li on each iteration in a slider like I have done here: http://www.reclaimedfloorboards.com/
There are loads of options

I ended up using jquery's replaceWith command and gave all the frames a class "frame" that i could select with $('.frame') which happened to only select visible frames.
<script type="text/javascript">
var animation = [];
var firstFrame = 1;
var lastFrame = 96;
var animationFrames = 16;
var loadedImageCount = 0;
$(function() {
$("button, input:submit",'#forecastForm').button();
$("#progressbar").progressbar({
value: 0
});
$('#id_Species').attr('onChange', 'loadAnimation($(\'#id_Species\').val(),$(\'#id_Layer\').val(),$(\'#id_StartTime\').val(),$(\'#id_EndTime\').val())' )
$('#id_Layer').attr('onChange', 'loadAnimation($(\'#id_Species\').val(),$(\'#id_Layer\').val(),$(\'#id_StartTime\').val(),$(\'#id_EndTime\').val())' )
$('#id_StartTime').attr('onChange', 'loadAnimation($(\'#id_Species\').val(),$(\'#id_Layer\').val(),$(\'#id_StartTime\').val(),$(\'#id_EndTime\').val())' )
$('#id_EndTime').attr('onChange', 'loadAnimation($(\'#id_Species\').val(),$(\'#id_Layer\').val(),$(\'#id_StartTime\').val(),$(\'#id_EndTime\').val())' )
});
if (document.images) { // Preloaded images
loadAnimation('Dry_GEM',1,1,96);
}
function rotate(animation, frame)
{
if (frame >= animation.length)
frame = 0;
$('.frame').replaceWith(animation[frame]);
window.setTimeout('rotate(animation,'+eval(frame+1)+')',150);
}
function loadAnimation(species, layer, startTime, endTime)
{
layer = Number(layer);
startTime = Number(startTime);
endTime = Number(endTime);
if (startTime > endTime)
{
swap = startTime;
startTime = endTime;
endTime = swap;
delete swap;
}
for (i=0;i<animation.length;i++)
delete animation[i];
delete animation;
animation = []
$('#progressbar').progressbar({value: 0});
loadedImgCount = 0;
animationFrames = endTime - startTime + 1;
for(i=0;i < animationFrames;i++)
{
animation[i] = new Image();
animation[i].height = 585;
animation[i].width = 780;
$(animation[i]).attr('class','frame');
animation[i].onload = function()
{
loadedImgCount += 1;
$('#progressbar').progressbar({value: eval(loadedImgCount / animationFrames * 100)});
};
animation[i].src = 'http://[[url]]/hemi_2d/' + species + '_' + layer + '_' + eval(i+startTime) + '.png';
}
}
</script>

The easiest way to do it is create a separate hidden image for each frame. Something like this:
var nextImage = (function(){
var imagePaths='basn0g01.png,basn0g02.png,basn0g04.png,basn0g08.png'.split(','),
imageHolder=document.getElementById('custom-header'),
i=imagePaths.length, imageIndex=i-1, img;
for (;i--;) {
img=document.createElement('img');
img.src='http://www.schaik.com/pngsuite/' + imagePaths[i];
if (i) img.style.display='none';
imageHolder.appendChild(img);
}
return function(){
imageHolder.childNodes[imageIndex].style.display='none';
if (++imageIndex >= imageHolder.childNodes.length) imageIndex=0;
imageHolder.childNodes[imageIndex].style.display='inline-block';
}
}());
Try this example on this page; paste it in the console and then call nextImage() a few times. Watch the top of the page.
edit
If you already have all the images in your HTML document, you can skip most of the above and just do something like this:
var nextImage = (function(){
var imageHolder=document.getElementById('custom-header'),
images=imageHolder.getElementsByTagName('img'),
i=images.length, imageIndex=0, img;
for (;i--;) if (i) images[0].style.display='none';
return function(){
imageHolder.childNodes[imageIndex].style.display='none';
if (++imageIndex >= imageHolder.childNodes.length) imageIndex=0;
imageHolder.childNodes[imageIndex].style.display='inline-block';
}
}());

Related

getJSON issue with loading images across Safari and iOS

I'm trying to load a series of images that disappear at an interval of 300ms on page load.
The images are chosen at random from a JSON file, based on the users screen dimensions.
This works in Chrome but seems to fail randomly, and does not work at all in Safari (pauses on a random image) or on iOS (fails to load any images at all).
Here is my code:
// get the screen orientation based on css media query
var orientation = window.getComputedStyle(document.querySelector('body'), ':after').getPropertyValue('content').replace(/['"]+/g, '');
window.slides = [];
// function to randomise the order of an array
function shuffle(a) {
var j, x, i;
for (i = a.length - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i + 1));
x = a[i];
a[i] = a[j];
a[j] = x;
}
return a;
}
$(document).ready(function() {
$( 'html' ).addClass( 'js' ).removeClass( 'no-js' );
// define the category and number of slides
var imageDirs = { "lovers": 16 };
// get slide urls from json file
$.getJSON('slides/slides.json', function (data) {
for (var thisDir in imageDirs) {
var theseSlides = [];
theseSlides = data[thisDir][orientation];
shuffle(theseSlides)
slides = theseSlides.slice(0, imageDirs[thisDir]);
}
})
.done(function() {
// randomise order of slides
shuffle(slides);
// have one blank slide at the beginning of the animation
slides.push("initial-slide.png");
if ( slides.length ) {
for (var i = 0; i < slides.length; i++) {
$('#wrapper').append('<img class="slide" src="slides/' + slides[i] + '">');
}
}
});
});
$(window).on('load', function(){
// wait for images to load before displaying animation
$('#wrapper').waitForImages(true).done(function() {
$('#preloader').remove();
var currentSlides = [];
$('.slide').each(function(){
currentSlides.push($(this));
});
// remove slides at an interval of 300ms
var timer = setInterval(function(){
if (currentSlides.length){
currentSlides.pop().remove();
} else {
clearInterval(timer);
}
}, 300);
});
});
JSFiddle: https://jsfiddle.net/robertirish/6g41vwnL/59/
Live site: http://robertirish.com
Thanks in advance!
Thanks for the fiddle. Basically your problem is that in your load event the images haven't been appended to be the DOM yet. You can see this by logging currentSlides: https://jsfiddle.net/1fb3gczq/
So, you need to ensure the DOM is ready before you manipulate it. I've moved your function to the ajax success which works in this case, but on slower connections you'll want to only run the effect once all the images have loaded in the DOM. Here's a working fiddle: https://jsfiddle.net/rcx3w48b/

Cannot add eventListener to img elements before they are created

I have a blank page, if you dblclick anywhere, an image will fade in where the dblclick event happened. This works fine.
I have a function appear which can be called to fade in elements or fade out, depending on parameter passed into it. I call that from the getItOut function, passing eventTarget as first parameter.
But, now I want a functionality to remove the images on click(our other event, does not really matter).
I try this:
var allImages = []; // initializing array to iterate over img node list
var displayedImages = document.getElementsByTagName('IMG'); get img node list
allImages.push(displayedImages); // push the displayed imgs to the array
// here I want to run this function by iterating with forEach over the array
allImages.forEach(function(el) {el.addEventListener('click', getItOut)}); // getting type error, el.addEventListener is not a function
function getItOut(event){ // this should be called during the above forEach iteration
var getOut = event.target;
appear(getOut, 100, -5, 40);
}
Here is the codepen link:
https://codepen.io/damPop/pen/RqMxex
I have commented out the above, as the thrown error would not let anything run.
What am I doing wrong here? Should I check if an img element exists in the DOM at some place?
Well, yes!
You CANNOT hook a listener to something, that does not exist.
There are 2 ways to go about it.
Wait for the elements to be created (document.ready)
Hook your listener to the document, and then parse to see what has been clicked. jQuery has got you covered: $(document).on('EVENT', 'SELECTOR', function(){})
Because you are pushing Images as HTMLCollection not a DOM element to allImages and when you iterate through you can't bind EventListener to a Collection.
allImages.push(displayedImages); // result is [HTMLCollection(1)]
Also wait for images or DOM content to load then add eventlistener
It works.I created a jsfiddle test it in jsfiddle
Code snippet:
var images = ['//unsplash.it/500/500', '//unsplash.it/400/500','//unsplash.it/500/300'];
function getItOut(event){
var getOut = event.target;
appear(getOut, 100, -5, 40);
//Fixing bug for click same position of disappeared image
// to not appear again
// and fix fadeOut opacity
var timerId = setInterval(function () {
const opacity = Number(getOut.style.opacity);
if(opacity == 0) {
getOut.parentNode.removeChild(getOut);
clearInterval(timerId);
}
}, 40)
}
document.addEventListener('dblclick', function(event) {
var currentImg;
var ix = event.clientX; // Get the coordinates
var iy = event.clientY;
var x = document.createElement("IMG");
x.setAttribute("src", images[0]);
x.setAttribute("width", "304");
x.setAttribute("height", "228");
x.style.position="absolute";
x.style.top= iy + 'px';
x.style.left= ix + 'px';
x.style.opacity = 0.1;
document.body.appendChild(x);
x.addEventListener('click', getItOut)
//x.classList.add("fadeIn");
var ix = "";
var iy = "";
console.log(event.currentTarget);
appear(x, 0, 5, 40);
});
function appear(elm, i, step, speed){
var t_o;
//initial opacity
i = i || 0;
//opacity increment
step = step || 5;
//time between opacity increments in ms
speed = speed || 50;
t_o = setInterval(function(){
//get opacity in decimals
var opacity = i / 100;
//set the next opacity step
i = i + step;
if(opacity > 1 || opacity < 0){
clearInterval(t_o);
//if 1-opaque or 0-transparent, stop
return;
}
//real browsers
elm.style.opacity = opacity;
//IE
elm.style.filter = 'alpha(opacity=' + opacity*100 + ')';
}, speed);
}

Custom Lazy Load - IE9 Memory Leak

I'm currently developing a basic image gallery that dynamically loads new images in the following order (on document.ready):
Uses an ajax call to get JSON which contains all the information needed to dynamically render images.
Iterates over the JSON object to create proper divs/img elements which are then appended to the page.
$.ajax({
url: '/wp-content/themes/base/library/ajax/posts-json.php',
type: 'get',
//dataType: 'json',
success: function(data) {
// turn string response to JSON array
window.responseArray = JSON.parse(data);
window.lastPhotoIndex = 0;
// make sure there is a response
if (responseArray.length > 0) {
// get container
var container = document.getElementById("photos-container");
var ulElement = document.createElement('ul');
ulElement.className = "rig columns-3";
ulElement.setAttribute("id", "photo-list");
// iterate over each response
window.photoCount = 0;
for (var i = 0; i < responseArray.length; i += 1) {
// Only load first 10 images
if (responseArray[i]["post-type"] == "photo" && photoCount < 20) {
// Set the last photo index to this photo
lastPhotoIndex = i;
// create the li
var liElement = document.createElement("li");
liElement.className = liElement.className + responseArray[i]["day"];
//create class name string from WP tags
if (responseArray[i].tags.length > 0) {
for (var ii = 0; ii < responseArray[i].tags.length; ii += 1) {
nospaceTagName = responseArray[i].tags[ii].split(' ').join('');
liElement.className += " " + nospaceTagName;
}
}
//create image element and append to div
var imgTag = document.createElement("img");
imgTag.src = responseArray[i]["thumb-url"];
liElement.appendChild(imgTag);
//Add modal class info to outer div
liElement.className += " md-trigger";
//Add data-modal attribute to outer div
liElement.setAttribute("data-modal", "photo-modal");
ulElement.appendChild(liElement);
//next slide
photoCount++;
}
}
//append ul to container
container.appendChild(ulElement);
}
},
error: function(xhr, desc, err) {
console.log(xhr);
console.log("Details: " + desc + "\nError:" + err);
}
});// end ajax call
After the ajax call, I add a window scroll event that will be called while there are still more photos in the JSON object.
// Window scroll event
$(window).scroll(function () {
var trigger = $(document).height() - 300;
if (trigger <= $(window).scrollTop() + $(window).height()) {
//Call function to load next 10
loadNextPhotos();
}
});
The function called by the scroll even simply starts off at the previously left off index (lastPhotoIndex variable set at the beginning of ajax call - 'window.lastPhotoIndex'). The function looks like this:
function loadNextPhotos() {
if (photoCount < getPhotoCount()) {
var photosOutput = 0;
var startingIndex = lastPhotoIndex + 1;
var photoList = $('#photo-list');
for (var i = startingIndex; i < responseArray.length; i += 1) {
if (responseArray[i]["post-type"] == "photo" && photosOutput < 10) {
lastPhotoIndex = i;
photosOutput++;
// create the li needed
var element = document.createElement("li");
element.className = responseArray[i]["day"];
//create class name string from tags
if (responseArray[i].tags.length > 0) {
for (var ii = 0; ii < responseArray[i].tags.length; ii += 1) {
nospaceTagName = responseArray[i].tags[ii].split(' ').join('');
element.className = element.className + " " + nospaceTagName;
}
}
//create image element and append to li
var imgTag = document.createElement("img");
imgTag.src = responseArray[i]["thumb-url"];
element.appendChild(imgTag);
//Add modal class info to li
element.className = element.className + " md-trigger";
//Add data-modal attribute to outer div
element.setAttribute("data-modal", "photo-modal");
photoList.append(element);
// Keep track of photo numbers so modal works for appropriate slide number
photoCount++;
}
}
}
}
Bear in mind, this code is stripped down a lot from the full application. It works fine in Chrome, Safari, Firefox, IE10+.
When loaded in IE9, I'm experiencing crazy memory leaks as I hit the scroll event and append more items to the UL.
My guess is that I'm not following best practices when creating new items to be appended and they're staying in memory longer than they should. The only issue is I'm not sure how to solve it/debug it because the page crashes so quickly in IE9.
Any help would be awesome. Thanks!
EDIT:
I've tried implementing Darmesh's solution with no real luck. As I said in his comment it only delays the rate at which memory is leaked. I've also added jquery.visible.js on top of a scroll event so it looks like this:
$(window).scroll(function () {
if($('#lazy-load-trigger').visible() && window.isLoadingPhotos != true) {
console.log("VISIBLE!");
loadNextPhotos();
}
});
But it also only delays the memory leak. I still believe there are issues with Garbage Collection in IE9, but am not sure how to troubleshoot.
I think this is due to the browser calling loadNextPhotos function multiple times at the same time every time you scroll. This might work, give it a try,
function loadNextPhotos() {
// Add flag to indicate new photos adding started
window.isLoadingPhotos = true;
....
....
....
....
// Indicate new photos adding completed
window.isLoadingPhotos = false;
}
And,
$(window).scroll(function () {
var trigger = $(document).height() - 300;
if (trigger <= $(window).scrollTop() + $(window).height()) {
if(!window.isLoadingPhotos) {
//Call function to load next 10
loadNextPhotos();
}
}
});

javascript loop image slideshow

var imag = document.getElementById('contentimage');
var imagarr = ["images/contimgtwo.jpg","images/contimg.jpg"]
//var convertimg = imagarr[i].toString();
var runtext = function(){
for (i=0;i<imagarr.length;i++){
imag.src=imagarr[i];
}
}
setTimeout(runtext,5000);
CSS:
#contentimage {
display:block;
top:1600px;
width:500px;
height:400px;
position:absolute;
}
#contentimage:hover {
opacity:0.5;
cursor:crosshair;
}
I'm trying to make a image slideshow using a for loop, the idea is that it will provide the .src path with the image path from the array, however the problem is the .src=" " method requires you to " " so I can't call the array and so it doesn't find the image, any way around this?
You should not loop the whole array in your runtext function. Doing that you actually apply only the last value of the array and always the last image is shown. Here is a modification of your script which may work:
var imag = document.getElementById('contentimage');
var imagarr = ["images/contimgtwo.jpg", "images/contimg.jpg"];
var index = 0, interval;
var runtext = function(){
imag.src = imagarr[index];
if(index < imagarr.length-1) {
index += 1;
} else {
index = 0;
}
interval = setTimeout(runtext, 5000);
}
var stopText = function() {
clearTimeout(interval);
}
interval = setTimeout(runtext, 5000);
I added a function which stops the slideshow. You still go through all the elements of the array, but the index is incremented on every runtext call.

Rotate Background Image of Body by Time

Can someone help me make a simple javascript to get element body so I can alternate its style to change background images every X seconds?
There are three things you'll need (two of which have been mentioned by other answers):
The builtin function setInterval(), to fire off a handler function every X seconds.
The expression document.body, which gets you a direct reference to the DOM object for the body element.
A function (to be passed to setInterval()) which will switch between images. This will probably require some data structure to remember the list of images to switch between.
For example:
var images = ['./image1.jpg', './image2.jpg'];
var curImage = 0;
function switchImage()
{
curImage = (curImage + 1) % images.length
document.body.style.backgroundImage = 'url(' + images[curImage] + ')'
}
window.setInterval(switchImage, numSeconds * 1000);
Set a function to switch backgrounds and add an interval to do it.
Backgrounds are contained in array bgArr.
bgArr = ['images/bg1.jpg', 'anotherdir/bg2.jpg', 'otherone/bg3.jpg'];
bgCur = 0;
backgroundSwitch = function()
{
if (bgCur == bgArr.length) bgCur = 0;
document.body.style.backgroundImage = 'url('+ bgArr[bgCur++]+ ')';
}
window.setInterval(backgroundSwitch, 30000); // Switch every 30 seconds.
check out setInterval
setInterval(function () {
document.body.style.backgroundImage = new_image;
}, 3000);

Categories

Resources