2012-06-12 43 views
4

我正在製作一個旨在用於iPad的紋理拾取器。所以基本上只是一堆圖像元素。爲了避免圖像重新加載和滯後,我緩存並重用JS中的Image對象。排序這個在iPad上使用JS進行圖像緩存

/** 
* Asynchronous version of memoize for use with callback functions. Asserts 
* that last argument is the callback. 
* 
* @param {Function} func 
* @return {Function} 
*/ 
util.memoize.async = function(func) { 
    var cache = {}; 
    return function() { 
     var hash = JSON.stringify(arguments); 
     var args = Array.prototype.splice.call(arguments, 0); 
     var callback = args.pop(); 
     if (hash in cache) { 
      return callback.apply(this, cache[hash]); 
     } 
     args.push(function() { 
      cache[hash] = Array.prototype.splice.call(arguments, 0); 
      callback.apply(this, cache[hash]); 
     }); 
     return func.apply(this, args); 
    }; 
}; 

/** 
* Creates new Image element and calls back with loaded image. 
* @param {string} url 
*/ 
io.GetImage = function(url, callback) { 
    var img = new Image(); 
    img.onload = function() { 
     callback(img); 
    }; 
    img.src = url; 
}; 

picker.image_ = util.memoize.async(io.GetImage); 

然後每當我需要的形象,我打電話picker.image_並獲得緩存之一。它在桌面,Chrome,Firefox,Safari上完美運行,但在iPad上,我得到了空的(未加載的)圖像。這是爲什麼?我非常喜歡這種方法,它表現非常好。

它看起來就像移動Safari從DOM中刪除圖像數據時一樣。那可能嗎?

UPDATE:爲了說明,所加載的數據是動態的,因此它不是AppCache最適合的用例。

UPDATE *:還沒有完全令人滿意的答案,這是我的解決方案。請注意,複製方法非常慢。

/** 
* Creates new Image element and calls back with loaded image. 
* @param {string} url 
*/ 
var GetImage = function(url, callback) { 
    var img = new Image(); 
    img.onload = function() { 
     callback(img); 
    }; 
    img.src = url; 
}; 

/** 
* @param {number} num maximum number of stored images 
*/ 
var ImagePool = function(num) { 
    this.limit_ = num; 
    this.canvases_ = {}; 
    this.order_ = []; 
}; 

/** 
* Retrieve image from cache. 
* 
* @param {string} url  URL of request image 
* @param {function(HTMLCanvasElement)} callback 
*/ 
ImagePool.prototype.get = function(url, callback) { 
    if (this.canvases_[url] !== undefined) { 
     callback(this.copy_(url)); 
    } else { 
     if (this.limit_ && this.order_.length == this.limit_) { 
      delete this.canvases_[url]; 
      this.order_.pop(); 
     } 
     GetImage(realUrl, function(img) { 
      var c = document.createElement('canvas'); 
      c.width = img.width; 
      c.height = img.height; 
      var ctx = c.getContext('2d'); 
      ctx.drawImage(img, 0, 0); 

      this.canvases_[url] = c; 
      this.order_.unshift(url); 
      callback(this.copy_(url)); 
     }.bind(this)); 
    } 
}; 

/** 
* @param {string} url 
* @return {HTMLCanvasElement} 
* @private 
*/ 
ImagePool.prototype.copy_ = function(url) { 
    var c = document.createElement('canvas'), 
     cached = this.canvases_[url]; 
    c.width = cached.width; 
    c.height = cached.height; 
    var ctx = c.getContext('2d'); 
    ctx.drawImage(cached, 0, 0); 
    return c; 
}; 
+0

那麼你爲什麼不把它放在DOM中並使其不可見? –

+0

我必須竭盡全力在我的UI系統中做到這一點,絕對不是一個優雅的解決方案(我正在尋找)。 – skrat

回答

5

我認爲您的問題可以通過使用HTML 5脫機應用程序緩存來最好地解決。您列出了您想要緩存的資源,並且在用戶訪問請求這些資源的頁面後,它們將被緩存供以後使用。您仍然需要讓用戶界面等待,直到您的圖像加載完畢,但是一旦加載完成,您就不必擔心被丟棄,因爲它們不在DOM中(This SO question表示圖像處於DOM,但不顯示在屏幕上,也被刪除)。

顯然,Safari Mobile有一個5MB的緩存限制,可以通過要求用戶同意擴展它(Source)來提高。在this blog post有意見認爲,這種擴張提示,請儘快的iOS 4

幫助鏈接:

+0

也許我最終得到應用程序緩存,這個數據是動態的,我將不得不更新到清單。幾乎忘了這一個http://www.alistapart.com/articles/application-cache-is-a-douchebag/ – skrat

+0

@skrat - 你絕對需要編輯你的問題,以添加圖像是動態數據的要求。這改變了很多。 – RustyTheBoyRobot

+0

@skrat - 我只是好奇;爲什麼你的圖像會變得如此動態? – RustyTheBoyRobot

2

您是否嘗試過在服務器上設置適當的標題並讓瀏覽器管理緩存而不是使用此方案?

+0

我確實碰到了Mobile Safari的限制,到目前爲止我的解決方法是維護圖像池,將每個下載的圖像渲染到畫布上,並在需要時複製它。 – skrat

3

如果附加Image對象到DOM直接你可能會失去它刪除,你可以嘗試刪除或附加

function clone(obj) { 
    if (null == obj || "object" != typeof obj) return obj; 
    var copy = obj.constructor(); 
    for (var attr in obj) { 
    if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr]; 
    } 
    return copy; 
} 
+0

這是嘗試,但這個問題歸結爲Mob Safari的6 MB限制。無論是否在DOM中。 – skrat

1

我會用所編碼的嵌入圖片src的base64之前將其克隆。只需檢查here

因此,如果需要,您可以將數據作爲字符串存儲爲JSON,如果要從其他URL調用圖像,則還可以將其保存到localStorage中。

是的,它會導致你一個巨大的html內容和存儲空間,但在你的情況下,它絕對值得。