jQuery - Image replacement transition [issues in Safari] - javascript

Okay I have the following code:-
[SEE JSFIDDLE]
HTML:
<div id="header">
<span class="mobile-menu"></span>
</div>
CSS:
#header {
width: 100%;
background: #000000;
height: 100px;
}
.mobile-menu {
position: absolute;
right: 25px;
top: 20px;
background: url(http://planetbounce.m360.co.uk/wp-content/themes/planetbounce/assets/img/buttons/menu-01.png);
background-repeat: no-repeat;
background-size: 26px !important;
height: 26px;
width: 26px;
display: inline-block;
margin: 7px 0;
-webkit-transition-duration: 0.8s;
-moz-transition-duration: 0.8s;
-o-transition-duration: 0.8s;
transition-duration: 0.8s;
}
.mobile-menu-hover {
background: url(http://planetbounce.m360.co.uk/wp-content/themes/planetbounce/assets/img/mobile-menu-hover.png);
}
jQuery:
var imagesArray = ["http://planetbounce.m360.co.uk/wp-content/themes/planetbounce/assets/img/buttons/menu-01.png",
"http://planetbounce.m360.co.uk/wp-content/themes/planetbounce/assets/img/buttons/menu-02.png",
"http://planetbounce.m360.co.uk/wp-content/themes/planetbounce/assets/img/buttons/menu-03.png",
"http://planetbounce.m360.co.uk/wp-content/themes/planetbounce/assets/img/buttons/menu-04.png",
"http://planetbounce.m360.co.uk/wp-content/themes/planetbounce/assets/img/buttons/menu-05.png",
"http://planetbounce.m360.co.uk/wp-content/themes/planetbounce/assets/img/buttons/menu-06.png",
"http://planetbounce.m360.co.uk/wp-content/themes/planetbounce/assets/img/buttons/menu-07.png"];
function preloadImg(pictureUrls, callback) {
var i, j, loaded = 0;
var imagesArray = [];
for (i = 0, j = pictureUrls.length; i < j; i++) {
imagesArray.push(new Image());
}
for (i = 0, j = pictureUrls.length; i < j; i++) {
(function (img, src) {
img.onload = function () {
if (++loaded == pictureUrls.length && callback) {
callback(imagesArray);
}
};
img.src = src;
}(imagesArray[i], pictureUrls[i]));
}
};
function changeImage(background, imagesArray, index, reverse) {
background.css("background-image", "url('" + imagesArray[index].src + "')").fadeIn(10, function() {
if (reverse) {
index--;
if (index == -1) {
return; // stop the interval
}
} else {
index++;
if (index == imagesArray.length) {
return; // stop the interval
}
}
//Fade in the top element
background.fadeOut(10, function () {
//Set the background of the top element to the new background
background.css("background-image", "url('" + imagesArray[index] + "')");
changeImage(background, imagesArray, index, reverse);
});
});
}
jQuery(function () {
/* Preload Image */
preloadImg(imagesArray, function (imagesArray) {
jQuery(".mobile-menu").css("background-image", "url('" + imagesArray[0].src + "')")
jQuery('.mobile-menu').on('click', {imgs: imagesArray}, function (event) {
var background = jQuery(".mobile-menu");
var bi = background.css('background-image');
var index = 0;
var reverse = false;
if (imagesArray[0].src != bi.replace('url("', '').replace('")', '')) {
index = imagesArray.length - 1;
reverse = true;
}
changeImage(background, event.data.imgs, index, reverse);
});
});
});
The Issue:
This works fine in Firefox and Chrome, it transitions between the 7 different images on click, then does the reverse on the second click (toggling).
The problem is when I try this in Safari, it basically goes through the image replacement process then reverts back to the first image for some reason and I can't figure out why?
Any ideas?

It seems to be because Safari returns the background-image without double quotes, but your replace function checks for url(", so it doesn't replace and reverse never gets true.
Instead of replacing you can check using either indexOf or matching using a RegExp, it would be safer and more straightforward. Either:
if ( bi.indexOf(imagesArray[0].src) == -1) {
or
if (imagesArray[0].src != bi.match(/http.+png/)[0]) {
With indexOf: http://jsfiddle.net/u9ske14r/

Related

How to wait until lines are printed iteratively?

I am building a terminal like personal website and I want to display a welcome banner + message after one another. I have this cool effect where individual lines appear from top to bottom and characters from left to right (See here for yourself).
My problem is that the welcome banner and the welcome message are mixed up with one another. Thus, I want to wait until the banner is printed before printing the message. However something about my code must be wrong... (I tried using await/async) ...
Here you have a small reproducible example of the issue. All lines with the character "a" should be printed before lines with character "b". Can you please point out what the root of the problem is?
banner = ["aaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaa"]
welcomeMsg = ["bbbbbbbbbbbb", "bbbbbbbbbbbb", "bbbbbbbbbbbb", "bbbbbbbbbbbb"]
var before = document.getElementById("before");
setTimeout(async function() {
await loopLines(banner, "", 80);
loopLines(welcomeMsg, "", 80);
}, 100);
async function addLine(text, style, time) {
var t = "";
for (let i = 0; i < text.length; i++) {
if (text.charAt(i) == " " && text.charAt(i + 1) == " ") {
t += " ";
i++;
} else {
t += text.charAt(i);
}
}
setTimeout(function() {
var next = document.createElement("p");
next.innerHTML = t;
next.className = style;
before.parentNode.insertBefore(next, before);
window.scrollTo(0, document.body.offsetHeight);
}, time);
return;
}
async function loopLines(name, style, time) {
for (var i = 0; i < name.length; i++) {
await addLine(name[i], style, i * time);
}
return;
}
p {
display: block;
line-height: 1.3em;
margin: 0;
overflow: hidden;
/* white-space: nowrap; */
margin: 0;
letter-spacing: 0.05em;
animation: typing 0.5s steps(30, end);
/* font-size: calc(2vw + 7px); */
font-size: min(20px, calc(1.5vw + 7px));
}
#keyframes typing {
from {
width: 0;
}
to {
width: 100%;
}
}
<a id="before"></a>
In addLine you don't use await. Instead you call setTimeout, but that doesn't return a pending promise. So the promise returned by addLine resolves immediately without waiting for the timeout to complete.
To change that, make use of a promisified version of setTimeout. I have added its definition at the top of the snippet. Then see where addLine uses it instead of setTimeout:
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
banner = ["aaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaa"]
welcomeMsg = ["bbbbbbbbbbbb", "bbbbbbbbbbbb", "bbbbbbbbbbbb", "bbbbbbbbbbbb"]
var before = document.getElementById("before");
setTimeout(async function() {
await loopLines(banner, "", 80);
loopLines(welcomeMsg, "", 80);
}, 100);
async function addLine(text, style, time) {
var t = "";
for (let i = 0; i < text.length; i++) {
if (text.charAt(i) == " " && text.charAt(i + 1) == " ") {
t += " ";
i++;
} else {
t += text.charAt(i);
}
}
await delay(time); // <---------------
var next = document.createElement("p");
next.innerHTML = t;
next.className = style;
before.parentNode.insertBefore(next, before);
window.scrollTo(0, document.body.offsetHeight);
}
async function loopLines(name, style, time) {
for (var i = 0; i < name.length; i++) {
await addLine(name[i], style, i * time);
}
}
p {
display: block;
line-height: 1.3em;
margin: 0;
overflow: hidden;
/* white-space: nowrap; */
margin: 0;
letter-spacing: 0.05em;
animation: typing 0.5s steps(30, end);
/* font-size: calc(2vw + 7px); */
font-size: min(20px, calc(1.5vw + 7px));
}
#keyframes typing {
from {
width: 0;
}
to {
width: 100%;
}
}
<a id="before"></a>

How to repeat randomised img endlessly on javascript?

I have a function that render random image without repeating, but it stops working when the array of images has come to an end, my goal is to restart function with another random order of images, so function can work infinite. I've read another questions, but didn't find something appropriate to me case.
Here is html part:
<div class="card">
<div class="front" onClick="pickimg();return false;"><img
src="1.jpg" alt=""></div>
<div class="back"><img src="2.jpg" name="randimg"></div>
</div>
Css (just in case):
.card {
width: 200px;
height: 300px;
position: relative;
perspective: 1000px;
cursor: pointer;
margin: 0 50px;
}
.front, .back {
width: 100%;
height: 100%;
position: absolute;
display: flex;
justify-content: center;
align-items: center;
transition: 1s;
backface-visibility: hidden;
border-radius: 10px;
}
.front {
transform: rotateY(360deg);
}
.back {
transform: rotateY(180deg);
}
And JS:
var cards = document.querySelectorAll('.card')
Array.from(cards).forEach(function(card) {
card.addEventListener('click', function() {
Array.from(card.querySelectorAll('.back, .front')).forEach(function(el) {
['back', 'front'].forEach(function(s) {
el.classList.toggle(s)
});
});
});
});
var usedImages = {};
var usedImagesCount = 0;
function pickimg(){
var imagenumber = 3;
var randomnumber = Math.random();
var rand1 = Math.round( (imagenumber-1) * randomnumber) + 1;
images = new Array();
images[0] = "";
images[1] = "3.jpg";
images[2] = "4.jpg";
images[3] = "2.jpg";
var image = images[rand1];
if (!usedImages[rand1]){
document.randimg.src = images[rand1];
usedImages[rand1] = true;
usedImagesCount++;
if (usedImagesCount === images.length){
usedImagesCount = 0;
usedImages = {};
}
} else {
pickimg();
}
}
Thank you for your help.
You could try something like this:
let img = [1,2,3,4,5];
function switchImage () {
for(; ; ){
let x = Math.random() * 10;
if(typeof img[Math.round(x)] !== 'undefined') {
img.splice(x, 1);
break;
}
}
console.log(img);
if (img.length > 0){
setTimeout(() => switchImage (),1000);
}
}
switchImage();
This is a simplified example where every second the function calls itself again and a new image is picked from the image array. The old image is cut out of the array and the function will stop calling itself when every picture is shown.
Try this -
int lastIndex = Math.round(Math.random()*(imagenumber - 1)) + 1;
function pickImg(){
let imagenumber = 3;
int currIndex = Math.round(Math.random()*(imagenumber - 1)) + 1;
images = new Array();
images[0] = "2.jpg";
images[1] = "3.jpg";
images[2] = "4.jpg";
if (lastIndex !== currIndex) {
document.randimg.src = images[currIndex];
lastIndex = currIndex;
}
else {
pickImg();
}
}
If you didn't get any image displayed that means you have to deal with when images[index] returns undefined.
Inshort you need to have index in images always equal to some value.
How about simple like this does this work for you? at least it will not give you same number a row
var imgArr = [1, 2, 3, 4, 5, 6] // imagine this is your img
var lastImgIndex;
function loadImg() {
var RandomIndex = Math.floor((Math.random() * imgArr.length) + 1);
if (lastImgIndex != RandomIndex) {
$('span').text(RandomIndex + ' index of img show');
lastImgIndex = RandomIndex;
} else {
loadImg();
}
}
loadImg();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span></span>

how to loop through an object with an interval time to change a css property

i am trying to loop through a javascript object to change background-image to other images every 3 seconds.
i know i will be using setInterval, a for-loop to go through the object and jQuery to access the css and change the background. but i am not sure about how to use all that together
HTML
<div class="example"></div>
CSS
.example {
height: 600px;
background-image: url(path/pic01.jpeg); }
JS
var header = {
pic01 : 'url(path/pic02.jpeg)',
pic02 : 'url(path/pic03.jpeg)',
pic03 : 'url(path/pic04.jpeg)',
pic04 : 'url(path/pic05.jpeg)'}
i tried:
var counter = 0;
var i = setInterval(function(){
counter++;
if(counter === 5) {
clearInterval(i);
}
}, 3000);
and something like this. but i can't seem to get it done
for (var key in header) {
if (header.hasOwnProperty(key)) {
console.log(header[key]);
})
}
Why not use CSS?
html, body {
position: relative;
padding: 0;
margin: 0;
}
html, body, .example {
width: 100%;
height: 100%;
}
.example {
background-color: #333;
background-size: contain;
background-position: center;
background-repeat: no-repeat;
animation-name: gallery;
animation-duration: 12s;
animation-timing-function: steps(1, start);
animation-iteration-count: infinite;
}
#keyframes gallery {
0%, 100% {
background-image: url(https://placebear.com/145/201)
}
25% {
background-image: url(https://placebear.com/395/205)
}
50% {
background-image: url(https://placebear.com/150/200)
}
75% {
background-image: url(https://placebear.com/145/300)
}
}
<div class="example"></div>
First, use an array for the header images, that makes it a little easier. Below are two functions that will either loop infinite, starting from 0 index once the array's length is reached, the other one will loop the array once until all elements have been shown once. (You'll need to adjust it to your needs instead of writing the text to the div...)
var header = [
'url(path/pic02.jpeg)',
'url(path/pic03.jpeg)',
'url(path/pic04.jpeg)',
'url(path/pic05.jpeg)'
];
var i = 0;
function loopInfinite(arr) {
document.getElementById('foo').innerHTML = arr[i];
i++;
if (i === arr.length) i = 0;
setTimeout(() => {
loopInfinite(arr);
}, 500);
}
function loopOnce(arr) {
document.getElementById('foo').innerHTML = arr[i];
i++;
if (i < arr.length) {
setTimeout(() => {
loopOnce(arr);
}, 500);
}
}
loopInfinite(header);
<div id="foo"></div>
In this codeblock:
var counter = 0;
var i = setInterval(function(){
counter++;
if(counter === 5) {
clearInterval(i);
}
}, 3000);
You have not included your background-updating code. Something like this might be what you are looking for:
var imageNumber = 0;
var counter = 0;
var i = setInterval(function(){
counter++;
if(counter === 5) {
// your background-updating code
$('.example').css('background-image',headers['pic0' + imageNumber]);
imageNumber++;
if (imageNumber > headers.length) {
imageNumber = 0;
}
clearInterval(i);
}
}, 3000);

jquery fadeIn/Out, custom slideshow glitches, fade memory? fade queue?

I am building a background img slideshow and running into glitches I can't comprehend.
I have several objects that contains a list of images. I have two functions that will take these images, create one div per each, and add the imgs as background of these divs, all within a container.
Then, as described in this website, I fadeout the first div,and fadeIn the second, then move the first child to last child position, and loop, creating a slideshow effect.
When I want this over i .empty() the container. Then the process can start again with the same or another object.
The first time I do this, it works, but second, third... times, it starts to glitch. Not only two, but all divs start to fade in and out, for I don't know what reason
This happens even if I am using the same object in the first, second, third... attempts.
It would seem as if although the divs are erased from DOM, apparently there is some memory of them? Could it be related to the fact that created divs share the name with previously created divs? maybe fadein out keep some kind of internal queue I am unaware of?
Here is an JsFiddle:
https://jsfiddle.net/93h51k9m/11/
and the code:
$(document).ready(function(){
var imgObject = {
imgs: ['http://lorempixel.com/400/200/sports/1/','http://lorempixel.com/400/200/sports/2/','http://lorempixel.com/400/200/sports/3/']
};
var imgObject2 = {
imgs: ['http://lorempixel.com/400/200/sports/4/','http://lorempixel.com/400/200/sports/5/','http://lorempixel.com/400/200/sports/6/']
};
var noImgObject = {
};
function prepare(index) {
if ($("#cover").css("display") != "none") {
console.log("cover is visible: hide it first");
console.log("fadeOut cover in 3000ms");
$("#cover").fadeOut(3000, function() {
console.log("then empty cover")
$("#cover").empty();
console.log("now for the images")
roll(index);
});
} else {
console.log("cover is already hidden: now for the images");
roll(index);
};
};
function roll(index) {
if (typeof index.imgs != "undefined") {
console.log("called object has images")
console.log("get them and their numbers")
var imgs = index.imgs;
var imgsLength = imgs.length;
console.log("create as many divs as imgs, and place each img as bg in each div")
for (i = 0; i < imgsLength; i++) {
$("#cover").append("<div class='imgdiv" + i + "'></div>");
$(".imgdiv" + i).css("background-image", "url('"+imgs[i]+"')");
};
console.log("now hide all but first div, fadeIn cover and start the carousel");
//as seen at http://snook.ca/archives/javascript/simplest-jquery-slideshow
$('#cover').fadeIn(3000);
$('#cover div:gt(0)').hide();
setInterval(function() {
console.log("fade and swap")
$('#cover :first-child').fadeOut(3000)
.next('div').fadeIn(3000)
.end().appendTo('#cover')
}, 6000);
} else {
console.log("index has no images, nothing to do");
};
};
$("#imgobj").click(function(){
console.log("imgObject called");
prepare(imgObject);
});
$("#imgobj2").click(function(){
console.log("imgObject2 called");
prepare(imgObject2);
});
$("#noimgobj").click(function(){
console.log("noImgObject called");
prepare(noImgObject);
});
});
Thank you
Every time click event is invoked, another interval is being started and that is the reason, actions are appended in the queue
Use global variable which will hold the setInterval instance and clear it every time you start new Interval.
var interval;
$(document).ready(function() {
var imgObject = {
imgs: ['http://lorempixel.com/400/200/sports/1/', 'http://lorempixel.com/400/200/sports/2/', 'http://lorempixel.com/400/200/sports/3/']
};
var imgObject2 = {
imgs: ['http://lorempixel.com/400/200/sports/4/', 'http://lorempixel.com/400/200/sports/5/', 'http://lorempixel.com/400/200/sports/6/']
};
var noImgObject = {};
function prepare(index) {
clearInterval(interval);
if ($("#cover").css("display") != "none") {
console.log("cover is visible: hide it first");
console.log("fadeOut cover in 3000ms");
$("#cover").fadeOut(3000, function() {
console.log("then empty cover")
$("#cover").empty();
console.log("now for the images")
roll(index);
});
} else {
console.log("cover is already hidden: now for the images");
roll(index);
};
};
function roll(index) {
if (typeof index.imgs != "undefined") {
console.log("called object has images")
console.log("get them and their numbers")
var imgs = index.imgs;
var imgsLength = imgs.length;
console.log("create as many divs as imgs, and place each img as bg in each div")
for (var i = 0; i < imgsLength; i++) {
$("#cover").append("<div class='imgdiv" + i + "'></div>");
$(".imgdiv" + i).css("background-image", "url('" + imgs[i] + "')");
};
console.log("now hide all but first div, fadeIn cover and start the carousel");
//as seen at http://snook.ca/archives/javascript/simplest-jquery-slideshow
$('#cover').fadeIn(3000);
$('#cover div:gt(0)').hide();
interval = setInterval(function() {
console.log("fade and swap")
$('#cover :first-child').fadeOut(3000)
.next('div').fadeIn(3000)
.end().appendTo('#cover')
}, 6000);
} else {
console.log("index has no images, nothing to do");
};
};
$("#imgobj").click(function() {
console.log("imgObject called");
prepare(imgObject);
});
$("#imgobj2").click(function() {
console.log("imgObject2 called");
prepare(imgObject2);
});
$("#noimgobj").click(function() {
console.log("noImgObject called");
prepare(noImgObject);
});
});
html {
color: black;
height: 100%;
padding: 0;
margin: 0;
overflow: hidden;
}
body {
height: 100%;
padding: 0;
margin: 0;
background: #f7fafa;
}
* {
box-sizing: border-box;
}
button {
cursor: pointer;
}
#buttons {
z-index: 1000;
}
#cover {
display: none;
position: fixed;
top: 5vh;
left: 0;
width: 100vw;
height: 95vh;
opacity: 0.5;
z-index: 0;
}
#cover div {
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-repeat: no-repeat;
background-size: cover;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div id="buttons">
<button id="imgobj">imgObject</button>
<button id="imgobj2">imgObject2</button>
<button id="noimgobj">noImgObject</button>
</div>
<div id="cover"></div>

jQuery delay fadeIn

I have a javascript/jquery slideshow but I want to remove the somewhat long blank nothingness in between the fadein and fadeout images. I tried using a small delay because by default it still had a blank pause but that didn't work, any idea?
jsfiddle: https://jsfiddle.net/jzhang172/s624zn7d/1/
var imagesArray = ["http://assets.pokemon.com/assets/cms2/img/pokedex/full//007.png",
"http://assets.pokemon.com/assets/cms2/img/pokedex/full/001.png",
"https://assets.pokemon.com/static2/_ui/img/account/sign-up.png",
"http://static.giantbomb.com/uploads/scale_small/0/6087/2438704-1202149925_t.png",
"http://static.giantbomb.com/uploads/scale_small/0/6087/2438704-1202149925_t.png"];
function preloadImg(pictureUrls, callback) {
var i, j, loaded = 0;
var imagesArray = [];
for (i = 0, j = pictureUrls.length; i < j; i++) {
imagesArray.push(new Image());
}
for (i = 0, j = pictureUrls.length; i < j; i++) {
(function (img, src) {
img.onload = function () {
if (++loaded == pictureUrls.length && callback) {
callback(imagesArray);
}
};
img.src = src;
}(imagesArray[i], pictureUrls[i]));
}
};
function roll(imagesArray, currentPos){
var slide = $('.parallax-mirror').find('img').attr('src', imagesArray[currentPos].src);
slide.fadeIn(2000, function() {
slide.fadeOut(1500, function() {
currentPos++;
if(currentPos >= imagesArray.length){
currentPos = 0;
}
roll(imagesArray, currentPos);
});
});
}
$(function () {
preloadImg(imagesArray, function (imagesArray) {
roll(imagesArray, 0, 3);
});
});
.featured-wrapper {
height: 500px;
width: 100%;
overflow: hidden;
}
.things {
font-size: 50px;
height: 500px;
width: 100%;
background: blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/parallax.js/1.4.2/parallax.min.js"></script>
<div class="featured-wrapper" data-parallax="scroll" data-image-src="http://assets.pokemon.com/assets/cms2/img/pokedex/full//007.png">
</div>
<div class="things">I'm the stuff underneath</div>
You should use an opacity animation with an easing that fits your needs. You can use jquery ui easings

Categories