在Javascript中加载图像时,iPad/iPhone浏览器崩溃

Ste*_*zis 53 javascript iphone webkit ipad mobile-webkit

我正在尝试在Safari中构建一个模仿iPad照片应用程序的图库.它工作得很好,除了我通过将它们添加到DOM或创建新的Image对象加载超过6MB左右的图像,新图像停止加载或浏览器崩溃.这个问题已经足够普遍(其他所有人都遇到了相同的限制),我已经排除了我的Javascript代码作为罪魁祸首.

鉴于您可以在元素中或通过浏览器内媒体播放器流式传输多于几MB,这个限制似乎是不必要的,并且应该有某种可用的解决方法.也许是通过释放记忆或其他东西.

我也遇到过UIWebView的这个参考.

"JavaScript分配也限制在10 MB.如果超过JavaScript的总内存分配限制,Safari会引发异常."

这与我看到的相当匹配.是否可以在Javascript中解除分配对象,或者Safari/UIWebView是否保持运行总计并且永远不会放手?或者,是否有任何解决方法以另一种方式加载数据而不会消耗这10MB?

And*_*rew 14

更新:我认为有一种更简单的方法可以做到这一点,具体取决于您的应用程序.而不必多张图片,如果你只是有一个<img>元素或Image物体(或者两个,像"这个"形象和"未来"的形象,如果你需要的动画或转换),并简单地更新.src,.width,.height等等,你应该永远不会接近10MB的限制.如果您想要进行轮播申请,则必须先使用较小的占位符.您可能会发现此技术可能更容易实现.


我想我可能真的找到了解决方法.

基本上,您需要进行更深入的图像管理,并明确缩小您不需要的任何图像.你通常是通过使用document.removeChild(divMyImageContainer)或者$("myimagecontainer").empty()你有什么来做到这一点,但在Mobile Safari上这绝对没有; 浏览器根本不会释放内存.

相反,您需要更新图像本身,因此它占用的内存非常少; 你可以通过改变图像的src属性来做到这一点.我知道这样做的最快方式是使用数据URL.所以不要这样说:

myImage.src="/path/to/image.png"
Run Code Online (Sandbox Code Playgroud)

......反而说:

myImage.src="data:image/gif;base64,AN_ENCODED_IMAGE_DATA_STRING"
Run Code Online (Sandbox Code Playgroud)

下面是一个测试,以证明它的工作.在我的测试中,我的大型750KB图像最终会杀死浏览器并停止所有JS exectution.但是在重置之后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()
Run Code Online (Sandbox Code Playgroud)

编写此代码是为了测试我的解决方案,因此您必须弄清楚如何将其应用于您自己的代码.代码分为三部分,我将在下面解释,但唯一非常重要的部分是imgStoredImage.src = strNullImage;

loadNextImage()只需加载新图像和调用shrinkImages().它还分配了一个onload事件,用于开始加载另一个图像的过程(错误:我应该稍后清除此事件,但我不是).

waitAndReload()仅在此处允许图像时间显示在屏幕上.移动Safari非常慢并且显示大图像,因此在加载图像以绘制屏幕后需要时间.

shrinkImages()浏览所有先前加载的图像(活动图像除外)并更改.src为dataurl地址.

我在这里使用dataurl的文件夹图像(这是我能找到的第一个dataurl图像).我正在使用它,所以你可以看到脚本工作.你可能想要使用透明的gif代替,所以请使用这个数据url字符串:data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==


Ale*_*lex 12

6.5MB(iPad)/ 10MB(iPhone)下载限制是根据用于通过其src属性设置图像的图像元素的数量计算的.移动safari似乎不区分从缓存或通过网络加载的图像.将图像注入dom也无关紧要.

该解决方案的第二部分是移动safari似乎能够通过"background-image"css属性加载无限数量的图像.

这个概念证明使用了一个预先安置的池,它在成功下载后设置了背景图像属性.我知道它不是最佳的,并没有将使用过的Image下载器返回到池中,但我相信你明白了:)

这个想法改编自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> 
Run Code Online (Sandbox Code Playgroud)


Tra*_*tic 6

我很幸运,从Steve Simitzis和Andrew的建议开始.

我的项目:

基于PhoneGap的应用程序,包含6个主要部分,以及约45个子部分,其中包含2到7个图像的jquery循环库,每个640 x 440(共215个图像).起初我使用ajax来加载页面片段,但我已经切换到一个单页的网站,所有部分都隐藏起来直到需要.

最初,在经历了大约20个画廊之后,我得到了记忆警告1,然后是2,然后是崩溃.

在将所有图像作为背景应用于div之后,我可以在崩溃之前通过应用程序中的更多画廊(大约35个),但是在去往之前访问过的画廊之后,它最终会失败.

似乎对我有用的解决方案是将背景图像URL存储在div的title属性中,并将所有背景图像设置为空白gif.有215多张图片,为了方便和快速参考,我想在html中保留url.

当按下一个子导航按钮时,我将css背景图像重写为包含在div标题标签中的正确来源,仅用于显示的图库.这使我免于必须做任何花哨的JavaScript来存储正确的源图像.

var newUrl = $(this).attr('title');
$(this).css('background-image', 'url('+newUrl+')'); 
Run Code Online (Sandbox Code Playgroud)

当按下新的子导航按钮时,我将最后一个画廊div的背景图像重写为空白GIF.所以,除了界面gfx,我总是只有2-7张图像'活跃'.除了我添加的包含图像的任何内容之外,我只使用这种"ondemand"技术将标题与background-image交换.

现在看来我可以无限期地使用该应用程序而不会崩溃.不知道这是否会对其他人有所帮助,它可能不是最优雅的解决方案,但它为我提供了一个解决方案.


Ste*_*zis 5

到目前为止,我很幸运使用<div>标签而不是<img>标签,并将图像设置为div的背景图像.

总而言之,这很疯狂.如果用户对更多图像内容做出了肯定的请求,那么Safari就没有理由不允许您加载它.