问题
I am using jQuery to apply background-images to randomly selected DIV elements.
The problem is that I want to make sure that no two DIV elements can have the same background-image at any given time. So when the code chooses a DIV to apply the background-image, it should also check if any of the DIVs are already using that image in that moment.
I have put together a piece of code with a lot of help from others while searching for a solution, but it does not check if any of the DIVs are already using that image in that moment. As a result it often happens that two or more DIVs have the same background-image at the same time...
Here is my code:
var urls = [
'url(https://placekitten.com/g/350/150)',
'url(https://placekitten.com/g/300/550)',
'url(https://placekitten.com/g/400/200)',
'url(https://placekitten.com/g/350/750)',
'url(https://placekitten.com/g/250/600)',
'url(https://placekitten.com/g/400/150)',
'url(https://placekitten.com/g/300/600)',
'url(https://placekitten.com/g/450/350)'
];
// Select the next image
var active = Math.floor(Math.random() * (urls.length - 4)) + 1
setInterval(function() {
// Select randomnly the next div to change
var rand = Math.floor(Math.random() * 4);
// Store this list, so that we only make one call to the page
let images = $('.image');
let datachanged = images.attr("data-changed");
while(rand == datachanged)
rand = Math.floor(Math.random() * 4);
let current = $('.image:nth-child('+(rand+1)+')');
current.toggleClass("show hide");
// The fade effect takes 400ms
setTimeout(function(){
current.css('background-image', urls[active]);
images[0].setAttribute("data-changed", rand);
current.toggleClass("hide show");
},400);
// Change active value so that the background will not be same next time
active++;
active = (active == urls.length) ? 0 : active ;
}, 2000);
body { margin: 0; font-size: 0;}
.image {
background-size: cover;
display: inline-block;
width: 23%;
padding-top: 23%;
margin: 1%;
transition: opacity 0.4s ease;
}
.image:nth-of-type(1) {
background-image: url(https://placekitten.com/g/350/150);
}
.image:nth-of-type(2) {
background-image: url(https://placekitten.com/g/300/550);
}
.image:nth-of-type(3) {
background-image: url(https://placekitten.com/g/400/200);
}
.image:nth-of-type(4) {
background-image: url(https://placekitten.com/g/350/750);
}
.show {
opacity: 1;
}
.hide {
opacity: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<div id="images">
<div class="image"></div>
<div class="image"></div>
<div class="image"></div>
<div class="image"></div>
</div>
And here is the same code on jsFiddle.
回答1:
You can use data to store the index of the image used. Then in each interval you can exclude the once used from the main urls array. Then choose a random index to store in element data.
Also, I changed the class toggle to jQuery .fadeIn()
and .fadeOut()
as the provide more stable approach with callbacks.
Make sure that the array of the images urls is more than the divs that you have.
let urls = [
'https://source.unsplash.com/5E5N49RWtbA/300x300',
'https://source.unsplash.com/G85VuTpw6jg/300x300',
'https://source.unsplash.com/fIq0tET6llw/300x300',
'https://source.unsplash.com/P_0R02ArdLE/300x300',
'https://source.unsplash.com/PC_lbSSxCZE/300x300',
'https://source.unsplash.com/nptLmg6jqDo/300x300',
'https://source.unsplash.com/1_CMoFsPfso/300x300',
'https://source.unsplash.com/mk7D-4UCfmg/300x300'
];
// Get reference to divs and assign images on inital load
let images = $('.image');
let unusedDivs;
images.each(function(i, e) {
jQuery(this).css('background-image', `url(${urls[i]})`);
jQuery(this).data("image-index", i).data("div-used", false);
});
let lastImageIndex = null;
setInterval(function() {
// Get indexes that are already in ue
const usedIdx = images.map(function(i, e) {
return jQuery(e).data("image-index");
}).get();
// Filter indexes of urls that are not displayed yet
const unusedIdx = [];
urls.forEach(function(url, i) {
if (usedIdx.indexOf(i) == -1)
unusedIdx.push(i);
});
// get images div that not in use yet
unusedDivs = jQuery('.image').filter(function(i, e) {
return $(e).data("div-used") == false;
});
// Choose a random index from the unused indexes array
const randUnsedIdx = Math.floor(Math.random() * unusedIdx.length);
// Chose a random image div to apply image to
const randDivIdx = Math.floor(Math.random() * unusedDivs.length);
const current = unusedDivs.eq(randDivIdx);
lastImageIndex = randDivIdx;
// Get index number
const imgUrlIdx = unusedIdx[randUnsedIdx];
// fade out old image then on callback set the url of the new image
// then fade it in and assign the index to the dive so in next
// iteration we can filter it out
current.fadeOut(400, function() {
current.css('background-image', `url(${urls[imgUrlIdx]})`);
current.fadeIn();
current.data("image-index", imgUrlIdx).data("div-used", true);
// if all unsed divs have been used then reset
if (unusedDivs.length == 1)
jQuery('.image').data("div-used", false);
});
}, 2000);
body {
margin: 0;
font-size: 0;
}
.image {
background-size: cover;
display: inline-block;
width: 23%;
padding-top: 23%;
margin: 1%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="images">
<div class="image"></div>
<div class="image"></div>
<div class="image"></div>
<div class="image"></div>
<div class="image"></div>
</div>
回答2:
Instead of saving the 'last active img index' as a data attribute, I just created a selectedImages
which holds the state of images displayed in divs: initial is [1,2,3,4]
And when looking for next unique random, I just do in a do while look, until I get the value that is not in selectedImages
:
do{
active = Math.floor(Math.random() * (urls.length - 2)) + 1
} while(selectedImages.indexOf(active) != -1)
Here is the full snippet
var urls = [
'url(https://placekitten.com/g/350/150)',
'url(https://placekitten.com/g/300/550)',
'url(https://placekitten.com/g/400/200)',
'url(https://placekitten.com/g/350/750)',
'url(https://placekitten.com/g/250/600)',
'url(https://placekitten.com/g/400/150)',
'url(https://placekitten.com/g/300/600)',
'url(https://placekitten.com/g/450/350)'
];
// Select the next image
var selectedImages = [1,2,3,4] // initial state of divs img urls
var lastSelectedDiv = -1; // initial state of last selected div
setInterval(function() {
var active;
do{
active = Math.floor(Math.random() * (urls.length - 2)) + 1
} while(selectedImages.indexOf(active) != -1) // get a unique image (not in current ones that are displayed)
// Select randomnly the next div to change which is diffrent than the lastSelectedDiv
var rand;
do{
rand = Math.floor(Math.random() * 4); // get a random value until its not the same as lastSelectedDiv
} while(rand == lastSelectedDiv)
selectedImages[rand] = active;
lastSelectedDiv = rand; // override lastSelectedDiv to new random value so that next time it must be different
let current = $('.image:nth-child('+(rand+1)+')');
current.toggleClass("show hide");
// The fade effect takes 400ms
setTimeout(function(){
current.css('background-image', urls[active]);
current.toggleClass("hide show");
},400);
}, 2000);
body { margin: 0; font-size: 0;}
.image {
background-size: cover;
display: inline-block;
width: 23%;
padding-top: 23%;
margin: 1%;
transition: opacity 0.4s ease;
}
.image:nth-of-type(1) {
background-image: url(https://placekitten.com/g/350/150);
}
.image:nth-of-type(2) {
background-image: url(https://placekitten.com/g/300/550);
}
.image:nth-of-type(3) {
background-image: url(https://placekitten.com/g/400/200);
}
.image:nth-of-type(4) {
background-image: url(https://placekitten.com/g/350/750);
}
.show {
opacity: 1;
}
.hide {
opacity: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<div id="images">
<div class="image"></div>
<div class="image"></div>
<div class="image"></div>
<div class="image"></div>
</div>
Edit here is a fiddle for 8 divs: https://jsfiddle.net/pascrzx9/1/ - I marked each div img with a different number, so we dont relay on colors only (sometimes I thought Im colorblind or its a bugged code :P)
来源:https://stackoverflow.com/questions/61796547/apply-a-css-value-with-jquery-but-check-if-it-is-specified-already