如何缓存图像栅格化

时间:2015-04-19 14:29:53

标签: javascript canvas html5-canvas

我正在做一个像索尼Be Moved这样的停止 - 滚动控制 - 回放网站。

考虑到停止拍摄技术,我面临的问题是在浏览器在屏幕上绘制图像之前对图像进行栅格化所需的时间。移动设备需要很多。可能调整大小的图像占用了大部分cpu,但我不确定。这就是我展示框架的方式:

<div 
  style="
    position: fixed;
    top:0; right:0; bottom:0; left:0;
    background-image: url(...);
    background-position: center;
    background-size: cover;
  "
></div>

问题:

有没有办法缓存图像的栅格化版本?也许画布支持这个?这样,当我决定在屏幕上显示它时,它就会准备就绪。

现在,这是我知道如何缓存图像的唯一方法。

var image = new Image();
image.src = '...';

2 个答案:

答案 0 :(得分:3)

参考评论 - 有一种方法可以预先缓存视频帧。每个帧将使用一个完整的内存块用于位图(在任何情况下也是预装图像序列的情况)。

缓存进程

  • 制作“离线”视频元素
  • preload设置为auto
  • 的视频来源设置
  • 您需要知道帧速率(典型:美国/日本为30 fps,欧洲为25 fps),根据此计算时间差值,即。 1 / FPS
  • 对每timeupdate次更新使用currentTime事件,因为设置当前时间异步

选择视频中的入点,缓存(由于事件周期,这可能需要一段时间),使用每个帧的canvas元素存储到帧缓冲区。然后根据需要播放缓冲区(这也使您能够向后播放视频,如下所示,这是浏览器尚不支持的功能)。

实施例

此示例将视频从网络,缓存90(3秒@ 30 fps)帧加载到内存,然后在窗口中播放序列乒乓(显示的图像显然来自缓存):

var canvas = document.querySelector("canvas"),
    ctx = canvas.getContext("2d"),
    video = document.createElement("video"),
    frames = [],
    w = canvas.width, h = canvas.height;

video.addEventListener("canplay", cache);
video.preload = "auto";
video.src = "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4";

function cache() {
  this.removeEventListener("canplay", cache);         // remove to avoid recalls

  var fps = 30,                                       // assuming 30 FPS
      delta = 1 / fps,                                // time delta
      count = 0,                                      // current cached frame
      max = fps * 3,                                  // 3 seconds
      div = document.querySelector("div");            // just for info

  this.addEventListener("timeupdate", cacheFrame);    // time update is aync
  this.currentTime = 19;                              // start with initial time
  
  function cacheFrame() {
    div.innerHTML = "Caching frame: " + count;
    
    if (count++ < max) {
      
      // create canvas for frame-buffer;
      var canvas = document.createElement("canvas"),
          ctx = canvas.getContext("2d");

      canvas.width = this.videoWidth;                 // canvas size = video frame
      canvas.height = this.videoHeight;
      
      ctx.drawImage(video, 0, 0);                     // draw current frame
      frames.push(canvas);                            // store frame
      
      this.currentTime += delta;                      // update time, wait..
    }
    else {
      this.removeEventListener("timeupdate", cacheFrame); // remove!!
      play();                                         // play back cached sequence
    }
  }
}

// to demo the cached frames
function play() {
  var current = 0, max = frames.length, dlt = 1,
      div = document.querySelector("div"),
      toggle = false,
      mem = max * video.videoWidth * video.videoHeight * 4; // always RGBA
 
  mem = (mem / 1024) / 1024;                          //mb

  ctx.fillStyle = "red";

  (function loop() {
    toggle = !toggle;                                 // toggle FPS to 30 FPS
    requestAnimationFrame(loop);
    
    if (toggle) {
      div.innerHTML = "Playing frame: " + current + 
                      " (raw mem: " + mem.toFixed(1) + " mb)";

      ctx.drawImage(frames[current], 0, 0, w, h);     // using frame-buffer
      ctx.fillRect(0, 0, current/max * w, 3);
      
      current += dlt;
      if (!current || current === max-1) dlt = -dlt;  // pong-pong
    }
  })();
}
html, body {width:100%;height:100%}
body {margin:0; overflow:hidden;background:#aaa}
div {font:bold 20px monospace;padding:12px;color:#000}
canvas {z-index:-1;position:fixed;left:0;top:0;width:100%;height:100%;min-height:400px}
<div>Pre-loading video... wait for it, wait for it...</div>
<canvas width=600 height=360></canvas>

答案 1 :(得分:1)

画布绘图源是image&amp;视频对象(以及其他一些现在不相关的来源)。因此,如果在初始下载和渲染期间发生了不必要的延迟,那么画布将花费更长的时间,因为传入的图像必须首先渲染到图像对象上,然后再次渲染到画布上 - 两步而不是一步。

你的答案不在canvas元素中,所以你回到了通常的解决方案:通过降低图像质量(质量较低的jpg)来减少下载的图像位数量。

您也可以(正如您所指出的那样)预先加载&amp;将所有图像缓存在new Image中,以便在需要时立即使用。通常的成本适用:缓存图像的内存使用量增加,以及在下载所有必需图像时应用程序启动时的延迟。