Для одного функционала требуется сделать ротатор изображений, но не по обычному таймауту, а по таймауту после загрузки и показа изображения, т.е. этакий показ превью видео из кучи мини скриншотов видео, с таймаутом где то в 100 мс.
Проблема заключается в том что мне не удаётся заставить браузер сначала загрузить изображение, а потом показать его, и только после этого начинат отсчёт таймаута. Проблема усугубляется что используется Lazy Load XT 1.1.0.
HTML изображения выглядит так:
<img src="URL" data-src="URL"
data-images="изображение1|изображение2|изображение3|изображение4|изображение5|и т.д."
data-timeout="опционально, по умолчанию 0.1">
Таких изображений множество, и у всех свои атрибуты, работать они должны соответственно так же независимо друг от друга.
Почему img.onload = function не дожидается исполнения кода внутри, и позволяет циклу исполняться дальше?
$("[data-images]").on('mouseover mouseout', function(data) {
if (data.type == "mouseover") {
$(this).attr("data-images_original", $(this).attr("src"));
images_data = $(this).attr("data-images");
images = images_data.split('|');
images_counter = images.length;
timeout = 1000;
for (for_counter = 1; for_counter < images_counter; for_counter++) {
image_url = images[while_counter];
var img = new Image();
img.onload = function() {
if (img.width == 0) {} else {
// $(this).attr("src", image_url);
}
};
img.onerror = function() {};
img.src = image_url;
$(this).attr("src", image_url);
}
} else if (data.type == "mouseout") {
$(this).attr("src", $(this).attr("data-images_original"));
}
});
Почему img.onload = function не дожидается исполнения кода внутри, и позволяет циклу исполняться дальше?
Потому что было бы плохо, если бы скрипт завис из-за долгой загрузки или ошибки. Поэтому onload выполняется асинхронно: цикл не будет ждать загрузки изображения, а выполнится практически моментально. А onload будет спокойно ждать, сработает после заргузки изображения... когда цикл давно уже будет завершен.
100ms слишком быстро, невозможно там что-то разглядеть) А вам нужна функция, которая будет вызывать сама себя после onload:
let test = document.querySelectorAll('.test');
Array.from(test).forEach(function(img){
let frameTimeout = null;
img.addEventListener('mouseenter', function(){
let imgs = this.dataset.images.split("|");
let i = 0;
recursiveLoad();
function recursiveLoad(){
img.src = "https://gyazo.com/" + imgs[i] + '.png';
img.onload = function(){
i = (i == imgs.length - 1) ? 0 : i + 1;
frameTimeout = setTimeout(recursiveLoad, 1000);
}
// детали с onerror
}
});
img.addEventListener('mouseleave', function(){
clearTimeout(frameTimeout);
img.src = ""; // default.png
});
});
img { width: 150px; height: 150px; background-color: grey; }
<img class="test" data-images="f850be7e9fc91aa2c8cb844603ea1e10|c708f879e56f725318fafca74b9cbe7f|fe48322c44d1c214ee8d9ba49876a0da">
<img class="test" data-images="f850be7e9fc91aa2c8cb844603ea1e10|c708f879e56f725318fafca74b9cbe7f|fe48322c44d1c214ee8d9ba49876a0da">
Создавать кучу Image
буферов в циклах - совсем не требуется, если не хотим скачивать вообще все сразу.
Так как курсор один, он может быть только над одной картинкой единовременно. Значит, нужен всего один буферный элемент для всех, и только один таймер должен быть активен.
Пример:
($ => {
$(document).ready(previews);
function previews() {
const buf = new Image();
buf.cancel = function () { this.onload = void(0); };
buf.load = function (src, cb) {
this.cancel();
this.onload = () => cb(src);
this.src = src;
};
const nextSrc = el => {
const images = $(el).data('images').split('|'),
curIdx = +$(el).data('idx'),
resetIdx = !isFinite(curIdx) || curIdx + 1 >= images.length,
nxtIdx = resetIdx ? 0 : curIdx + 1;
$(el).data('idx', nxtIdx);
return images[nxtIdx];
};
$('[data-images]').on('mouseover', function () {
buf.load(`https://picsum.photos/id/${nextSrc(this)}/1800`, src => {
this.src = src;
this.previewTimeout = setTimeout(() => {
$(this).trigger('mouseover');
}, (+$(this).data('timeout') || 0.1) * 1e3);
});
}).on('mouseout', function () {
clearTimeout(this.previewTimeout);
buf.cancel();
}).each(function () {
this.src = `https://picsum.photos/id/${nextSrc(this)}/180`;
});
}
})(jQuery);
img { width: 180px; height: 180px; background: #eee; }
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>
<img src="" data-src="URL" data-images="1|2|3|4|5" data-timeout="0.5">
<img src="" data-src="URL" data-images="6|7|8|9|10" data-timeout="0.5">
<img src="" data-src="URL" data-images="11|12|13|14|15" data-timeout1="0.5">
Картинки прописал пожирнее, чтобы грузились не моментально.
Первый цикл переключения с прогрузками, в последующих уже используется кэш браузера (особенно заметно это будет на третьем элементе, с дефолтным интервалом 0,1с).
В реальном коде, помимо обработки ошибок, желательно: подгружать следующую превьюшку заранее (начиная сразу после переключения), и хранить массив ссылок из data-атрибута в объекте элемента (чтобы на каждом переключении не создавать этот массив заново).
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Помогите пожалуйста с идеей, есть верстка, в очень упрощенном виде так:
Никак не могу понять почему в строке присвоения thisid = 120 браузер ругается