Я новичок в JavaScript, и я застрял на этой простой проблеме в течение нескольких дней. У меня есть серия из тысяч изображений (в совокупности «набор данных»), которые при быстром просмотре одно за другим создают видимость видео. Я хочу просмотреть все мои изображения.

Я создал такую ​​функцию:

function updateImage(dataset, record_id) {
    image_url = '/image?dataset='+dataset+'&record-id='+record_id;
    if ($('#mpeg-image').length > 0) {
        $('#mpeg-image')[0].src = image_url;
    } else {
        $("#image-thumbnail").html('<img id="mpeg-image", src="'+image_url+'"> </img>');
    }
}

Однократный вызов функции, например updateImage('dataset_28_18-08-11',444);, приводит к обновлению содержимого изображения в браузере. Однако я хочу показать изображения в цикле, поэтому я создал такую функцию:

function playVideo() {
    for (i = 0; i < 1800; i++) {
        updateImage(dataset,i);
    }
}

Эта функция использует ту же функцию updateImage(), которая работала выше, но когда я запускаю playVideo();, Chrome не обновляет изображение до самого конца. Тег src изменяется, но фактическое содержимое - нет. Как я могу изменить свой цикл так, чтобы каждое изображение загружалось и показывалось в последовательности? Я не хочу использовать что-то вроде «Sprite», которое объединяет все изображения в одно, так как это слишком интенсивно использует данные на моем бэкэнде. Я просто хочу загрузить и показать мои изображения одно за другим.

1
user554481 21 Авг 2018 в 06:26

4 ответа

Лучший ответ

Браузеры не будут повторно отображать элементы страницы, пока не произойдет перерыв в выполнении кода. Весь цикл изображений будет запущен до того, как браузер получит возможность перекрасить изображения.

Это не потому, что цикл «работает слишком быстро» или потому, что изображения не загружены (хотя это определенно может создавать проблемы). Чтобы увидеть пример проблемы перерисовки, попробуйте изменить анимацию в бесконечный цикл, где она постоянно воспроизводится. Страница будет «зависать» навсегда, а элементы страницы никогда не будут обновляться, даже после загрузки всех ваших изображений. Вам нужно дать браузеру возможность перерисовывать страницу всякий раз, когда вы вносите изменения в элементы страницы.

В веб-анимации это часто делается через window.requestAnimationFrame() (см. Документы MDN ). Метод принимает функцию обратного вызова, которая выполняется после того, как браузер перерисовал страницу. Браузер выполнит ваш обратный вызов, как только он будет готов к изменениям страницы.

Упрощенный пример этого с вашим кодом будет выглядеть примерно так.

var imageNo = 0;

function step() {
  updateImage(dataset, imageNo);
  imageNo++;
  if (imageNo < 1800) {
    window.requestAnimationFrame(step);
  }
}

window.requestAnimationFrame(step);

Вы можете добиться аналогичного эффекта, используя setTimeout() или setInterval(), но эти методы не оптимизированы для анимации.

1
John Ellmore 21 Авг 2018 в 04:54

Эта идея ужасна.

JS выполняется в одном потоке, а вызов данных из src - асинхронный метод. Звоните один за другим и ждите, а затем обязательно заблокируйте поток.

В этом случае вам нужно пообещать подготовить все изображения в первую очередь.

Я делаю работоспособный пример, используя логотип Google.

paths = ["https://www.google.com.hk/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png",
"https://www.google.com.hk/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png",
"https://www.google.com.hk/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png"]
var imgCount = paths.length;


function loadImage(url) {
    return new Promise((resolve, reject) => {
      let img = new Image();
      img.addEventListener('load', e => resolve(img));
      img.src = url;
    });
  }
  
promises = []

for (var i = 0; i < paths.length; i++){
	path = paths[i];
	promises.push(loadImage(path));
}

Promise.all(promises).then(imgs => {
imgs.forEach(e => document.getElementById('image-holder').appendChild(e));});
//What I done here is display then all, but you can make it like, replace one by one, or declare your own display function.
<div id="image-holder"></div>
-1
MT-FreeHongKong 21 Авг 2018 в 04:48

Причина, по которой вы видите только последнее изображение, заключается в двух вещах.
1. Как сказал @andriusain, браузеру требуется определенное количество времени для загрузки каждого изображения.
2. Цикл работает так быстро, что последняя итерация влияет на результат.

С учетом вышесказанного, было бы лучше либо спрайтовать изображение (что звучит так, как будто вы не можете этого сделать), либо вы можете предварительно загрузить изображения, чтобы их кэшировал браузер, а затем установить URL-адреса. Вы можете сделать что-то вроде этого.

// using es6
// I'd recommend adding <img src="mpeg-image" /> to your html page
// just to simplify the code. Also I recommend saving the image
// element in a local variable instead of using jQuery in each 
// iteration loop which will definitely give a slower performance.

const mpegImgElement = $('#mpeg-image')[0];
const FPS = 30 / 1000; // 30 Frames Per Second

function playVideo() {
  preLoadImages()
    .then(urls => {
      playUrls(urls);
    })
    .catch(error => {
      console.log('error loading images', error);
    })
}

function playUrls(urls) {
  const index = 0;
  const intervalId = setInterval(() => {
    // set the url
    mpegImgElement.src = urls[index];

    // all done. stop the interval
    if (++index === urls.length) {
      clearInterval(intervalId);
    }
  }, FPS);
}

function preLoadImages() {
  const promises = [];
  for (i = 0; i < 1800; i++) {
    const imageUrl = '/image?dataset='+dataset+'&record-id='+record_id;
    const promise = loadImage(imageUrl);
    promises.push(promise);
  }
  return Promise.all(promises);
}

function loadImage(url) {
  return new Promise((resolve, reject) => {
    const image = new Image();
    image.addEventListener('load', () => {
      resolve(url);
    });
    image.addEventListener('error', error => {
      reject(error);
    });
    image.src = url;
  });
}
0
CaptainHowdy 21 Авг 2018 в 04:36

Это происходит потому, что браузер все еще загружает изображения ... Я думаю, что вам лучше всего сначала программно загрузить их, а когда они загрузятся, затем воспроизвести видео ... для этого может быть что-то вроде этого поможет:

var loadImages = function(images, callback) {
  var loadTotal = 0;
  for (var i = 0; i < images.length; i++) {
    var image = new Image();
    image.addEventListener('load', function() {
        loadTotal++;
        if (loadTotal === images.length) {
            callback();
        }
    });
    image.src = images[i];
  }
}

var playVideo = function() {
    // do stuff...
}
var images = ['image1.jpg', 'image2.jpg'];

loadImages(images, playVideo);
-1
andriusain 21 Авг 2018 в 03:39
51941163