javascript加载并循环显示每个图像

时间:2018-08-21 03:26:56

标签: javascript

我是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”之类的东西将所有图像合并到一个图像中,因为这在我的后端会占用大量数据。我只想一个接一个地加载并显示图像。

4 个答案:

答案 0 :(得分:1)

在代码执行中断之前,浏览器不会重新渲染页面元素。整个图像循环将在浏览器获得重新绘制图像的机会之前运行。

这不是因为循环“运行得太快”或不是图像没有加载(尽管这肯定会带来问题)。要查看重绘问题的示例,请尝试将动画更改为无限循环并连续播放。页面将永久冻结(“风车”),并且页面元素将永远不会更新,即使在加载所有图像后很长时间也是如此。每当您对页面元素进行更改时,都需要让浏览器有机会重绘页面。

在网络动画中,这通常是通过window.requestAnimationFrame()完成的(请参见MDN docs)。该方法采用回调函数,该函数在浏览器重绘页面后执行。准备更改页面后,浏览器将执行您的回调。

一个与您的代码有关的简单示例就是这样。

var imageNo = 0;

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

window.requestAnimationFrame(step);

使用setTimeout()setInterval()可以达到类似的效果,但是这些方法并未针对动画进行优化。

答案 1 :(得分:0)

之所以只看到最后一张图片,是因为两件事。
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;
  });
}

答案 2 :(得分:-1)

发生这种情况是因为浏览器仍在加载图像...我认为最好的选择是先以编程方式加载它们,一旦加载它们,然后播放视频...这样做可能会有所帮助:< / p>

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);

答案 3 :(得分:-1)

这个主意太可怕了。

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>