2017-03-24 38 views
5

Buduję stronę, która przechodzi przez 3 różne tła, zmieniając się co 750 ms. Aby to zrobić, dodaję klasę do ciała z odpowiednim obrazem tła, zmieniając z JS. Podczas pierwszej pętli migają, ponieważ obraz musi się wczytać, więc nie ma go natychmiast. Czy są więc jakieś metody, które mogę wykorzystać do wstępnego załadowania obrazów?Wstępne ładowanie obrazów tła

CSS:

&.backgrounds{ 
    background-position: center bottom; 
    background-repeat: no-repeat; 
    background-size: 130%; 

    &.freddy{ 
     background-image: url(/img/illustrations/snapchat/snapchat_holding_page_freddy.jpg); 
    } 

    &.irene{ 
     background-image: url(/img/illustrations/snapchat/snapchat_holding_page_irene.jpg); 
    } 

    &.joe{ 
     background-image: url(/img/illustrations/snapchat/snapchat_holding_page_joe.jpg); 
    } 
} 

JS:

setInterval(function() { 
    if ($('.backgrounds').hasClass('freddy')){ 
     $('.backgrounds').removeClass('freddy').addClass('irene'); 

    } else if ($('.backgrounds').hasClass('irene')){ 
     $('.backgrounds').removeClass('irene').addClass('joe'); 

    } else if ($('.backgrounds').hasClass('joe')){ 
     $('.backgrounds').removeClass('joe').addClass('freddy'); 

    } 
}, 750); 

Odpowiedz

4

chciałbym zrobić coś takiego. loadImages zwraca Obietnicę, która rozwiąże się po załadowaniu wszystkich obrazów. Dołączony do niego .then wywołuje cycleImages, który uruchamia interwał. Ponieważ będziesz potrzebował adresów URL w JS i tak do wstępnego ładowania, zamiast przełączania klas, bezpośrednio manipuluję background-image, w ten sposób możesz usunąć adresy URL obrazów z CSS i zapisać kilka zbędnych bajtów. Ułatwia to również rozszerzanie listy obrazów w przyszłości, wystarczy dodać element do tablicy obrazów, zamiast utrzymywać skomplikowane instrukcje if.

function loadImages (images) { 
 
    // each image will be loaded by this function. 
 
    // it returns a Promise that will resolve once 
 
    // the image has finished loading 
 
    let loader = function (src) { 
 
    return new Promise(function (resolve, reject) { 
 
     let img = new Image(); 
 
     img.onload = function() { 
 
     // resolve the promise with our url so it is 
 
     // returned in the result of Promise.all 
 
     resolve(src); 
 
     }; 
 
     img.onerror = function (err) { 
 
     reject(err); 
 
     }; 
 
     img.src = src; 
 
    }); 
 
    }; 
 

 
    // create an image loader for each url 
 
    let loaders = []; 
 
    images.forEach(function (image) { 
 
    loaders.push(loader(image)); 
 
    }); 
 

 
    // Promise.all will return a promise that will resolve once all of of our 
 
    // image loader promises resolve 
 
    return Promise.all(loaders); 
 
} 
 

 

 
// the images we are going to display 
 
let myImages = [ 
 
    'http://www.gifpng.com/400x200', 
 
    'http://www.gifpng.com/400x200/ffffff/000000', 
 
    'http://www.gifpng.com/400x200/000000/ffffff' 
 
]; 
 

 
// $(document).ready(fn) is deprecated, 
 
// use the $(fn) form instead 
 
$(function() { 
 

 
    // after the images are loaded this will be called with an array of the loaded images 
 
    function cycleImages (images) { 
 
    let index = 0; 
 
    setInterval(function() { 
 
     // since we need an array of the image names to preload them anyway, 
 
     // just load them via JS instead of class switching so you can cut them 
 
     // out of the CSS and save some space by not being redundant 
 
     $('#backgrounds').css('backgroundImage', 'url("' + images[index] + '")'); 
 
     // increment, roll over to 0 if at length after increment 
 
     index = (index + 1) % images.length; 
 
    }, 750); 
 
    } 
 

 

 
    // load the images and start cycling through them after they are loaded 
 
    loadImages(myImages).then(cycleImages).catch(function (err) { 
 
    console.error(err); 
 
    }); 
 
});
#backgrounds { 
 
    height: 200px; 
 
    width: 400px; 
 
    border: 1px solid #000; 
 
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 
 
<div id="backgrounds"></div>

Edit: Biorąc Just a student „s komentarze pod uwagę, tutaj jest wersja, która przejdzie tylko obraz po jego załadowaniu, ale zrobić to od razu, jeśli jest załadowany . Pomija również obrazy, których załadowanie się nie powiodło. Ponieważ cycleImages nie jest już wywoływana przez .then, zmieniłem także to tak, że akceptuje element docelowy (jako obiekt jQuery) wraz z tablicą obietnic obrazów. W ten sposób możesz z łatwością użyć tego w wielu miejscach na stronie z różnymi zestawami obrazów, jeśli chcesz.

function loadImages (images) { 
 
    // each image will be loaded by this function. 
 
    // it returns a Promise that will resolve once 
 
    // the image has finished loading 
 
    let loader = function (src) { 
 
    return new Promise(function (resolve, reject) { 
 
     let img = new Image(); 
 
     img.onload = function() { 
 
     // resolve the promise with our url 
 
     resolve(src); 
 
     }; 
 
     img.onerror = function (err) { 
 
     reject(err); 
 
     }; 
 
     img.src = src; 
 
    }); 
 
    }; 
 

 
    // return an array of image-loading promises 
 
    return images.map(function (image) { 
 
    return loader(image); 
 
    }); 
 
} 
 

 

 
// the images we are going to display 
 
let myImages = [ 
 
    'http://www.gifpng.com/400x200', 
 
    'http://www.invalid-domain-name.foo/this-url-certainly-does-not-exist.jpg', 
 
    'http://www.gifpng.com/400x200/ffffff/000000', 
 
    'http://www.gifpng.com/400x200/000000/ffffff' 
 
]; 
 

 
// $(document).ready(fn) is deprecated, 
 
// use the $(fn) form instead 
 
$(function() { 
 

 
    // this receives an array of the promises for each image 
 
    function cycleImages ($target, images) { 
 
    let index = 0, 
 
     interval = 750; // how many ms to wait before attempting to switch images 
 

 
    function nextImage() { 
 
     // p is the promise for the current image 
 
     let p = images[index], 
 
     next = function (wait) { 
 
      // increment our counter and wait to display the next one 
 
      index = (index + 1) % images.length; 
 
      setTimeout(nextImage, wait); 
 
     }; 
 

 
     // wait for this image to load or fail to load 
 
     p.then(function (src) { 
 
     // it loaded, display it 
 
     $target.css('backgroundImage', 'url("' + src + '")'); 
 
     next(interval); 
 
     }).catch(function (err) { 
 
     // this one failed to load, skip it 
 
     next(0); 
 
     }); 
 

 
    } 
 

 
    // start cycling 
 
    nextImage(); 
 
    } 
 

 

 
    // load the images and start cycling through them as they are loaded 
 
    cycleImages($('#backgrounds'), loadImages(myImages)); 
 
});
#backgrounds { 
 
    height: 200px; 
 
    width: 400px; 
 
    border: 1px solid #000; 
 
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 
 
<div id="backgrounds"></div>

Zamiast sztywnego kodowania odstęp między obrazami zmian można również przekazać, że jako parametr. W tym momencie jednak chciałbym go przekonać do użycia obiektu konfiguracyjnego, aby przekazać wszystko oprócz tablicy obietnicy obrazów: cycleImages(myImages, {target: $('#backgrounds'), interval: 1000});

+0

Jedna uwaga poboczna, możesz pomyśleć o zmianie odstępu na co kilka sekund zamiast co każde 750ms. Większość ludzi wpadnie w irytację na coś, co miga tak szybko. Jako osoba z zaburzeniami koncentracji uwagi mogę powiedzieć, że coś migającego tak szybko na stronie sprawia, że ​​naprawdę trudno jest skoncentrować się na czytaniu czegokolwiek na stronie; to po prostu zbyt rozprasza. Nawet osoby bez ADD mogą być osłabione przez rozproszenie uwagi. –

+0

Świetna odpowiedź! Jedna sugestia: obecnie wszystkie obrazy są ładowane przed wyświetleniem pierwszego. Może to być miłe alternatywne rozwiązanie, aby załadować obraz tylko wtedy, gdy jest on potrzebny i po prostu opóźnić przełączenie, dopóki Obietnica nie zostanie rozstrzygnięta. Oznaczałoby to, że w przypadku pierwszej pętli nie można jeszcze użyć interwału, ale trzeba się upewnić, że oba (a) 750 ms (lub cokolwiek innego ustawiono) minęły i (b) załadowano następny obraz. To w rzeczywistości może być dobrze zrobione również z Obietnicami. –

+0

Aha, a kolejną wadą obecnego rozwiązania jest to, że żaden obraz tła nie zostanie wyświetlony, nawet jeśli nie można uzyskać dostępu do niego. –

0

ciekawy pomysł.

potencjalnie można załadować je w tle, tak jak:

<div class="hidden-images"> 
    <img src="img1.jpg" /> 
    <img src="img2.jpg" /> 
    <img src="img3.jpg" /> 
</div> 

potem w CSS

.hidden-images { 
    position: relative; 
    z-index: 1; 
} 

wtedy na cokolwiek w rodzaju głównego kontenera div się, co następuje uderzenie na nim?

.main-container { 
    position: relative; 
    z-index: 2; // Or higher, whatever is needed 
} 
1

Możesz załadować je w javascript. Na przykład:

(new Image()).src = url1; 
(new Image()).src = url2; 
(new Image()).src = url3; 

Zmień adresy URL "url1", "url2", "url3". Przeglądarka załaduje obraz, ale nie będzie widoczny w żadnym miejscu.

0

Możesz zrobić to również z inną strukturą html i css w ten sposób.

html

<div class="backgrounds"> 
    <div class="background freddy"></div> 
</div> 

css

.backgrounds{ 
    background-color: transparent; 
    background-position: center bottom; 
    background-repeat: no-repeat; 
    background-size: 130%; 
    height: 250px; 
    width; 100%; 
} 

.backgrounds .freddy{ 
     height: 100%; 
     width: 100%; 
     background-image: url('http://adrianweb.net/includes/images/hero.jpg'); 
    } 

.backgrounds .irene{ 
     height: 100%; 
     width: 100%; 
     background-image: url('http://adrianweb.net/includes/images/projects-blur.png'); 
    } 

.backgrounds .joe{ 
     height: 100%; 
     width: 100%; 
     background-image: url('http://adrianweb.net/includes/images/contacts-blur.png'); 
    } 

JS

$(document).ready(function(){ 
setInterval(function() { 
    if ($('.background').hasClass('freddy')){ 
     $('.background').removeClass('freddy').addClass('irene'); 

    } else if ($('.background').hasClass('irene')){ 
     $('.background').removeClass('irene').addClass('joe'); 

    } else if ($('.background').hasClass('joe')){ 
     $('.background').removeClass('joe').addClass('freddy'); 

    } 
}, 750); 
}); 

Może można dodać przekształceń/animację, aby wyglądać fade in i out.

Próbka codepen poniżej:

http://codepen.io/adrianrios/pen/yMEpEv