如何在延迟加载图片的同时防止requestAnimationFrame不稳定?

时间:2019-02-23 09:51:51

标签: javascript multithreading lazy-loading web-worker requestanimationframe

描绘故事:

我有一些视觉内容,分为几组图像(平铺)。

我正在使用一个简单的循环,该循环由requestAnimationFrame调用以在画布上显示这些图块。

实际上,我正在使用PixiJS来完成这些工作;尽管这可能是与该问题不太相关的实现细节。

因为可能会有很多图块,并且因为我希望用户能够“有点即时”滚动浏览这些图块集,所以我只预加载了某些图块集,然后当用户进一步导航时tileet,将加载另一组磁贴,直到没有更多磁贴可供加载。

当视觉内容完全加载后,requestAnimationFrame循环功能良好,没有任何断断续续的情况。

但是在延迟加载期间,有时会有些断断续续。

我尝试使用setIntervalrequestAnimationFrame循环中分离延迟加载(也包括废物管理或垃圾收集)。

但是后来我意识到这是因为JavaScript仅具有一个线程,因此图像的加载阻碍了其余代码。因此setInterval没有帮助;也不进行第二次requestAnimatonFrame的延迟加载调用。

然后,我读到有关Web Workers的信息,这确实为并发执行事情提供了可能性。但是,当我读取Web Worker线程中的所有数据时,它们将被复制而不是通过引用传递。因此它将占用两倍的内存。

我也怀疑Web Worker是否适合同时加载图块,因为Mozilla开发人员网络页面大多将其指定为并行(繁重)数字运算解决方案的帮助。

有一个特定的Web Worker类型。位于网页请求和Web服务器之间的Service Worker。在执行画图循环时,服务人员会在所有加载图像时帮助我吗?

如果不是,是否有人知道一种替代方法,既可以延迟加载又可以对已经加载的一组图像进行动画处理?有什么明显的我忽略了吗?

编辑3:请仔细观察;图像加载时出现口吃;有时比其他时候更多;但它仍然在那里;因为按照注释部分的指示我删除了模拟,所以它不那么明显了;但是口吃还在那里更容易查看是否扩展了代码段,然后在打开开发人员控制台后,转到“网络”选项卡并选中“禁用缓存”,然后单击“运行”按钮。

编辑2::忽略编辑1.因为模拟已由现在加载的真实图像代替。第一次加载图像或关闭浏览器缓存时,结结很明显。

编辑1:如要求的示例。通过循环模拟懒惰负载阻塞,直到执行了一定的随机延迟为止。这些颜色应该代表瓷砖图像,但实际上是使用随机颜色从画布上绘制的。

var tileImageURLs = [
  "https://async.blogjoch.nl/s/W9BcY7RgfNAES38/preview",
  "https://async.blogjoch.nl/s/qoHrK9Nz8i7m8Lb/preview",
  "https://async.blogjoch.nl/s/ZoAxpcMGF8MPrZH/preview",
  "https://async.blogjoch.nl/s/QgTBjbakkSKywpJ/preview",
  "https://async.blogjoch.nl/s/G74zo79txm9KHX9/preview",
  "https://async.blogjoch.nl/s/cgTy6YswifocsqB/preview",
];

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

var y = 0;
var NORMALSPEED = 5;
var currentSpeed = NORMALSPEED;
var TILEWIDTH = 768;
var TILEHEIGHT = 1024;
var TILECOUNT = 6;
var tileImages = [];
var lastTileLoad = -1;
var tileLoadStart = 0;
var PRELOADEDTILESCOUNT = 2;

// For offscreen generation of tiles.
//var tileCanvas = document.createElement('canvas');
//tileCanvas.width = TILEWIDTH;
//tileCanvas.height = TILEHEIGHT;
//var tileCtx = tileCanvas.getContext('2d');
//var tileColors = ['red', 'green', 'blue', 'orange', 'yellow'];

for (var tileIdx = 0; tileIdx < TILECOUNT; tileIdx++) {
  tileImages[tileIdx] = document.createElement('img');
  tileImages[tileIdx].width = TILEWIDTH;
  tileImages[tileIdx].height = TILEHEIGHT;
}


function loadTileImages(tileStart, tileEnd) {
  if (tileImages[tileStart]) {
    tileImages[tileStart].onload = function() {
      // Image loaded; go to next tile if applicable.
      if (tileStart + 1 <= tileEnd) {
        loadTileImages(++tileStart, tileEnd);
      }
    }
    tileImages[tileStart].src = tileImageURLs[tileStart];
  }
}

function loadTiles() {
  var tileLoadCount;
  if (lastTileLoad < Math.round(y / (canvas.height * 1))) {
    /**
     * Load checkpoint which lies past previous load checkpoint found;
     * so load some stuff.
     */
    tileLoadCount = Math.min(tileLoadStart + PRELOADEDTILESCOUNT - 1, TILECOUNT);
    loadTileImages(tileLoadStart, tileLoadCount);

    tileLoadStart += PRELOADEDTILESCOUNT;


    if (tileLoadStart > TILECOUNT - 1) {
      /**
       * Stop the loading; Infinity is always bigger than a non-infinite number.
       */
      console.log('Loading has finished.');
      lastTileLoad = Infinity;
    } else {
      /**
       * Store this 'load checkpoint'.
       */
      //this.needToDrawFrame = true;
      lastTileLoad = Math.round(y / (canvas.height * 1));
    }
  }
}

function tick() {
  var tileImgY;
  if (currentSpeed > 0 && (y >= (TILECOUNT * TILEHEIGHT) - canvas.height)) {
    currentSpeed = -NORMALSPEED;
  } else if (currentSpeed < 0 && (y <= 0)) {
    currentSpeed = NORMALSPEED;
  }
  y += currentSpeed;

  loadTiles();

  var tileStart = Math.max(Math.floor(y / TILEHEIGHT) - 1, 0);
  var tileCount = Math.min(tileStart + Math.ceil(canvas.height / TILEHEIGHT) + 2, TILECOUNT);
  //console.log(y, tileStart, tileCount);
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  for (var tileIndex = tileStart; tileIndex < tileCount; tileIndex++) {
    var tileImg = tileImages[tileIndex];
    tileImgY = (tileIndex * TILEHEIGHT) - y;
    ctx.drawImage(tileImg, 0, tileImgY, TILEWIDTH, TILEHEIGHT);
  }
  requestAnimationFrame(tick);
}

requestAnimationFrame(tick);
#canvas {
  width: 500px;
  height: 500px;
  border: solid 1px black;
}
<canvas id="canvas" width="768" height="1024"></canvas>

1 个答案:

答案 0 :(得分:0)

我对自己的问题有一个答案;这主要是我的愚蠢。

事实证明,它正在加载阶段对股票行情自动收录器功能(由 log.info("Session id=" + vars.get("sessionId")); 调用)(旨在确保新的图块图像到达时刷新屏幕)。

这些调用引起了额外的运动,并感觉好像有些结巴(比我在示例中能够重现的重)。

但是,当我在Web Worker中使用requestAnimationFrame加载图像后,部分口吃-微型口吃就消失了。