54

iPadの写真アプリを模倣した画像ギャラリーをSafariで構築しようとしています。DOMに追加するか、新しいImageオブジェクトを作成して、6 MB以上の画像を読み込むと、新しい画像の読み込みが停止するか、ブラウザがクラッシュすることを除けば、完全に機能します。この問題は十分に広まっているため(他のすべての人が同じ制限にぶつかっています)、Javascriptコードを原因として除外しました。

要素内またはブラウザ内のメディアプレーヤーを介して数MBをはるかに超える量をストリーミングできることを考えると、この制限は不要であるように思われ、何らかの回避策が利用可能であるはずです。おそらく、メモリなどを解放することによって。

UIWebViewのこのリファレンスにも出くわしました。

「JavaScriptの割り当ても10MBに制限されています。JavaScriptの合計メモリ割り当てでこの制限を超えると、Safariで例外が発生します。」

これは私がかなりよく見ているものと一致します。Javascriptでオブジェクトの割り当てを解除することは可能ですか、それともSafari / UIWebViewは現在の合計を維持し、決して手放すことはありませんか?または、この10MBを消費しない別の方法でデータをロードするための回避策はありますか?

4

11 に答える 11

14

更新:アプリケーションによっては、これを行うためのさらに簡単な方法があると思います。複数の画像を用意する代わりに、単純に 1 つの<img>要素またはImageオブジェクト (または、アニメーションや遷移が必要な場合は「この」画像と「次の」画像のように 2 つ) を使用し、単に 、 などを更新する場合は、次のようにする必要が.srcあり.widthます.height。 10MB の制限に近づくことはありません。カルーセル アプリケーションを実行する場合は、最初に小さいプレースホルダーを使用する必要があります。この手法の方が実装しやすいかもしれません。


私は実際にこれに対する回避策を見つけたかもしれないと思います。

基本的に、より深い画像管理を行い、不要な画像を明示的に縮小する必要があります。通常はdocument.removeChild(divMyImageContainer)or$("myimagecontainer").empty()または を使用してこれを行いますが、Mobile Safari ではこれはまったく何もしません。ブラウザがメモリの割り当てを解除することは決してありません。

代わりに、イメージ自体を更新する必要があるため、メモリをほとんど消費しません。画像のsrc属性を変更することでそれを行うことができます。私が知っている最も簡単な方法は、データ URLを使用することです。したがって、これを言う代わりに:

myImage.src="/path/to/image.png"

...代わりに次のように言ってください:

myImage.src="data:image/gif;base64,AN_ENCODED_IMAGE_DATA_STRING"

以下は、動作を実証するためのテストです。私のテストでは、私の大きな 750KB の画像は最終的にブラウザーを強制終了し、すべての JS 実行を停止させました。しかし、リセット後src、画像のインスタンスを 170 回以上読み込むことができました。コードがどのように機能するかについての説明も以下に示します。

var strImagePath = "http://path/to/your/gigantic/image.jpg";
var arrImages = [];
var imgActiveImage = null
var strNullImage = "data:image/gif;base64,R0lGODlhEAAOALMAAOazToeHh0tLS/7LZv/0jvb29t/f3//Ub//ge8WSLf/rhf/3kdbW1mxsbP//mf///yH5BAAAAAAALAAAAAAQAA4AAARe8L1Ekyky67QZ1hLnjM5UUde0ECwLJoExKcppV0aCcGCmTIHEIUEqjgaORCMxIC6e0CcguWw6aFjsVMkkIr7g77ZKPJjPZqIyd7sJAgVGoEGv2xsBxqNgYPj/gAwXEQA7";
var intTimesViewed = 1;
var divCounter = document.createElement('h1');
document.body.appendChild(divCounter);

var shrinkImages = function() {
    var imgStoredImage;
    for (var i = arrImages.length - 1; i >= 0; i--) {
        imgStoredImage = arrImages[i];
        if (imgStoredImage !== imgActiveImage) {
            imgStoredImage.src = strNullImage;
        }
    }
};
var waitAndReload = function() {
    this.onload = null;
    setTimeout(loadNextImage,2500);
};
var loadNextImage = function() {
    var imgImage = new Image();
    imgImage.onload = waitAndReload;
    document.body.appendChild(imgImage);
    imgImage.src = strImagePath + "?" + (Math.random() * 9007199254740992);
    imgActiveImage = imgImage;
    shrinkImages()
    arrImages.push(imgImage);
    divCounter.innerHTML = intTimesViewed++;
};
loadNextImage()

このコードは私のソリューションをテストするために書かれたものなので、自分のコードに適用する方法を理解する必要があります。コードは 3 つの部分で構成されており、以下で説明しますが、本当に重要な部分はimgStoredImage.src = strNullImage;

loadNextImage()新しい画像をロードして を呼び出すだけshrinkImages()です。また、onload別の画像をロードするプロセスを開始するために使用されるイベントを割り当てます (バグ: このイベントは後でクリアする必要がありますが、そうではありません)。

waitAndReload()ここにあるのは、画像が画面に表示される時間を許可するためだけです。Mobile Safari は非常に遅く、大きな画像を表示するため、画像が読み込まれてから画面が描画されるまでに時間がかかります。

shrinkImages()以前にロードされたすべてのイメージ (アクティブなイメージを除く) を.src調べて、dataurl アドレスに変更します。

ここでは、dataurl にファイル フォルダーの画像を使用しています (これは、私が見つけた最初の dataurl 画像でした)。スクリプトの動作を確認できるように、単純に使用しています。おそらく代わりに透明な gif を使用したいので、代わりに次のデータ URL 文字列を使用してください。data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==

于 2010-07-16T17:31:42.723 に答える
12

6.5MB (iPad) / 10MB (iPhone) のダウンロード制限は、src プロパティを介して画像を設定するために使用される画像要素の数に基づいて計算されます。モバイル サファリは、キャッシュから読み込まれた画像とネットワーク経由で読み込まれた画像を区別していないようです。また、画像が dom に注入されるかどうかも問題ではありません。

解決策の 2 番目の部分は、モバイル サファリが "background-image" css プロパティを介して無制限の数の画像をロードできるように見えることです。

この概念実証では、正常にダウンロードされた後に background-image プロパティを設定する precacher のプールを使用します。私はそれが最適ではなく、使用されたイメージダウンローダーをプールに返さないことを知っていますが、あなたはその考えを理解していると確信しています:)

このアイデアは、Rob Laplaca の元のキャンバスの回避策http://roblaplaca.com/blog/2010/05/05/ipad-safari-image-limit-workaround/から採用されています。

<!DOCTYPE html>
<head> 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
<title>iPad maximum number of images test</title> 
<script type="text/javascript">
    var precache = [
        new Image(),
        new Image(),
        new Image(),
        new Image()
    ];

    function setImage(precache, item, waiting) {
        precache.onload = function () {
            item.img.style.backgroundImage = 'url(' + item.url + ')';
            if (waiting.length > 0) {
                setImage(precache, waiting.shift(), waiting);
            }
        };
        precache.src = item.url;
    }

    window.onload = function () {
        var total = 50,
            url = 'http://www.roblaplaca.com/examples/ipadImageLoading/1500.jpg',
            queue = [],
            versionUrl,
            imageSize = 0.5,
            mb,
            img;

        for (var i = 0; i < total; i++) {
            mb = document.createElement('div');
            mb.innerHTML = ((i + 1) * imageSize) + 'mb';
            mb.style.fontSize = '2em';
            mb.style.fontWeight = 'bold';

            img = new Image();
            img.width = 1000;
            img.height = 730;
            img.style.width = '1000px';
            img.style.height = '730px';
            img.style.display = 'block';

            document.body.appendChild(mb);
            document.body.appendChild(img);


            queue.push({
                img: img,
                url: url + '?ver=' + (i + +new Date())
            });
        }

        //
        for (var p = 0; p < precache.length; p++) {
            if (queue.length > 0) {
                setImage(precache[p], queue.shift(), queue);
            }
        }
    };
</script>
</head> 
<body> 
<p>Loading (roughly half MB) images with the <strong>img tag</strong></p> 
</body> 
</html> 
于 2011-01-18T08:27:23.380 に答える
6

これまでのところ<div>、タグの代わりに<img>タグを使用し、画像を div の背景画像として設定することができました。

全体として、それはクレイジーです。ユーザーがより多くの画像コンテンツを求める肯定的な要求をしている場合、Safari がそのコンテンツの読み込みを許可しない理由はありません。

于 2010-07-20T02:45:25.570 に答える
6

Steve Simitzis と Andrew の提案から始めて幸運に恵まれました。

私のプロジェクト:

6 つのメイン セクションと約 45 のサブセクションを持つ PhoneGap ベースのアプリで、それぞれ 640 x 440 (全体で 215 以上の画像) の 2 ~ 7 枚の画像の jquery サイクル ギャラリーがあります。最初は ajax を使用してページ フラグメントをロードしていましたが、その後、必要になるまですべてのセクションを非表示にして、1 ページのサイトに切り替えました。

最初に、約 20 のギャラリーを調べた後、メモリ警告 1、2、そしてクラッシュが発生しました。

背景として適用された画像ですべての画像を div にすると、クラッシュする前にアプリでより多くのギャラリー (約 35) を通過できましたが、以前にアクセスしたギャラリーに移動した後、最終的に失敗しました。

私にとってうまくいっていると思われる解決策は、背景画像の URL を div の title 属性に保存し、すべての背景画像を空白の gif に設定することです。215 以上の画像があるので、簡単に参照できるように、URL を html のどこかに残しておきたいと思いました。

サブナビゲーションボタンが押されると、表示されているギャラリーのみのために、divのタイトルタグに含まれる正しいソースにcss背景画像を書き換えます。これにより、正しいソース画像を保存するために派手な JavaScript を実行する必要がなくなりました。

var newUrl = $(this).attr('title');
$(this).css('background-image', 'url('+newUrl+')'); 

新しいサブナビゲーション ボタンが押されると、最後のギャラリー div の背景画像を空白の gif に書き換えます。そのため、インターフェイス gfx を除いて、常に「アクティブ」なイメージは 2 ~ 7 個しかありません。画像を含む他のものを追加する場合は、この「オンデマンド」手法を使用して、タイトルを背景画像と交換します。

これで、クラッシュすることなくアプリを無期限に使用できるようになりました。これが他の人に役立つかどうかはわかりません。これは最もエレガントなソリューションではないかもしれませんが、修正を提供してくれました。

于 2010-08-17T15:47:36.947 に答える
3

私はこれに対する解決策を見つけることができませんでした。これが私が試したいくつかの方法ですが、それらはすべて失敗しました:

  • を使用してDIVの背景を変更するだけですdiv.style.backgroundImage = "url("+base64+")"

  • .srcを使用して画像のを変更しましたimg.src = base64

  • 古い画像を削除し、を使用して新しい画像を追加しましたremoveChild( document.getElementById("img") ); document.body.appendChild( newImg )

  • 上記と同じですが、新しい画像の高さがランダムになります

  • 画像をHTML5キャンバスオブジェクトとして削除および追加します。また、新しいものを作成する必要があるため、機能しません。Image();*を参照してください。

  • 起動時に、新しいImage()オブジェクトを作成しました。これをコンテナと呼びましょう。画像をとして表示しました<canvas>。画像が変更されるたびに、コンテナを変更し、を.src使用してキャンバスを再描画しましたctx.drawImage( container, 0,0 )

  • 前と同じですが、実際にキャンバスを再描画することはありません。Image()オブジェクトの使用量を変更するだけsrcでメモリを使い果たします。

私が気付いた奇妙なこと:画像が表示されていなくてもバグが発生します!たとえば、これを行う場合:

var newImg = new Image( 1024, 750 );
newImg.src = newString; // A long base64 string

5秒ごとに、そして他に何も、画像の読み込みや表示はもちろん、オブジェクトにラップされていないので、しばらくするとメモリがクラッシュします!

于 2010-10-10T17:01:18.163 に答える
3

Rails アプリで、何百枚もの中サイズの写真 (無限スクロール) を怠惰に読み込み、必然的に iPhone の 10Mb 制限に達しました。グラフィックをキャンバスにロードしようとしましたが (新しい画像、src=、次に Image.onload)、それでも同じ制限に達しました。また、img srcを置き換えて削除しようとしましたが(表示可能な領域から出たとき)、まだ葉巻はありません。最後に、div を含むすべての img タグを背景として写真に切り替えるとうまくいきました。

      $.ajax({
        url:"/listings/"+id+"/big",
        async:true,
        cache:true,
        success:function(data, textStatus, XMLHttpRequest) {
          // detect iOS
          if (navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPod/i) || navigator.userAgent.match(/iPad/i)) {
            // load html into data
            data = $(data);
            // replace img w/ div w/ css bg
            data.find(".images img").each(function() { 
              var src = $(this).attr("src").replace(/\s/g,"%20");
              var div = $("<div>"); 
              div.css({width:"432px",height:"288px",background:"transparent url("+src+") no-repeat"}); 
              $(this).parent().append(div); 
              $(this).remove(); 
            }); 
            // remove graphic w/ dynamic dimensions
            data.find(".logo").remove();
          }
          // append element to the page
          page.append(data);
        }
      });

壁にぶつかることなく、1 ページに 40Mb をはるかに超える写真をロードできるようになりました。ただし、CSS の背景グラフィックの一部が表示されないという奇妙な問題が発生しました。クイックjsスレッドがそれを修正しました。div の css bg プロパティを 3 秒ごとに設定します。

  setInterval(function() {
    $(".big_box .images div.img").each(function() {
      $(this).css({background:$(this).css("background")});
    });
  }, 3000);

http://fotodeck.comで実際の動作を確認できます。iPhone/iPadでご覧ください。

于 2011-02-08T02:09:19.243 に答える
2

数秒ごとなど、頻繁に画像を更新しようとしたときに、iPadのJavascriptでメモリ不足が発生しました。頻繁に更新するのはバグでしたが、Safariがクラッシュしてホーム画面に表示されました。更新のタイミングを制御できるようになると、Webアプリは正常に機能しました。Javascriptエンジンは、古い画像をすべて破棄するのに十分な速さでガベージコレクションに追いつくことができなかったようです。

于 2010-08-06T11:54:10.663 に答える
2

メモリには問題があり、この問題を解決する方法は非常に簡単です。1) すべてのサムネイルをキャンバスに配置します。多くの新しい Image オブジェクトを作成してキャンバスに描画しますが、サムネイルが非常に小さい場合は問題ありません。実物大の画像を表示するコンテナには、Image オブジェクトを 1 つだけ作成し、このオブジェクトを再利用してキャンバスにも描画するようにしてください。したがって、ユーザーがサムネイルをクリックするたびに、メインの Image オブジェクトが更新されます。ページに IMG タグを挿入しないでください。代わりに、サムネイルとメイン表​​示コンテナーの正しい幅と高さで CANVAS タグを挿入します。IMG タグを挿入しすぎると、iPad がファウルします。だから、それらを避けてください!!! キャンバスのみを挿入します。その後、ページから canvas オブジェクトを見つけて、コンテキストを取得できます。したがって、ユーザーがサムネイルをクリックするたびに、メイン イメージ (実際のサイズのイメージ) の src を取得し、それをメイン キャンバスに描画して、メインの Image オブジェクトを再利用し、イベントを発生させます。最初は毎回イベントクリア。

mainDisplayImage.onload = null;
mainDisplayImage.onerror = null;

...

mainDisplayImage.onload = function() { ... Draw it to main canvas }
mainDisplayImage.onerror = function() { ... Draw the error.gif to main canvas }
mainDisplayImage.src = imgsrc_string_url;

200 個のサムネイルを作成しましたが、それぞれが 15kb 程度です。実際の画像はそれぞれ 1 MB のようです。

于 2010-07-19T03:05:34.307 に答える
0

私はChromeでも同様の問題を抱えており、同じページ(実際にはポップアップ)に画像をロードして古い画像を新しい画像に置き換える拡張機能を開発しています。(DOM から削除された) 古いイメージによって使用されたメモリは決して解放されず、短時間ですべての PC メモリを消費します。CSS でさまざまなトリックを試しましたが、成功しませんでした。iPad のように、PC よりもメモリが少ないハードウェアを使用すると、当然、この問題は早期に発生します。

于 2010-08-26T13:28:34.417 に答える
-1

jQuery はメモリ リークを処理しようとするため、jQuery にバグを報告しました。これはバグだと思います。Mobile Safari でこの問題を処理するための簡潔で巧妙な方法を、チームがすぐに思いつくことを願っています。

http://dev.jquery.com/ticket/6944#preview

于 2010-08-25T14:07:41.643 に答える