Я пытаюсь создать галерею изображений в Safari, который подражает фото приложению iPad. Это работает отлично, за исключением того, что, после того как я загружаю приблизительно больше чем 6 МБ ценность изображений или путем добавления их к DOM или создания новых Объектов изображения, новые изображения или прекращаю загружаться или катастрофические отказы браузера. Эта проблема достаточно широко распространена (со всеми остальными подбрасывающими ударом против того же предела), что я исключил свой код JavaScript как преступник.
Учитывая, что можно передать потоком намного больше, чем некоторые МБ в элементе или через медиаплеер в браузере, этот предел кажется ненужным, и должно быть некоторое доступное обходное решение. Возможно, путем освобождения памяти или чего-то еще.
Я также столкнулся с этой ссылкой для UIWebView.
"Выделения JavaScript также ограничены 10 МБ. Safari повышает исключение при превышении этого предела на выделение общей памяти для JavaScript".
Который соответствует тому, что я вижу довольно хорошо. Действительно ли возможно освободить объекты в JavaScript, или Safari/UIWebView сохраняет рабочее общее количество и никогда не отпускает? Поочередно, есть ли какое-либо обходное решение для загрузки в данных иначе, которые не съедают выше на это 10 МБ?
Есть проблемы с памятью, и способ решить эту проблему очень прост. 1) Поместите все ваши миниатюры в canvas. Вы будете создавать много новых объектов Image и рисовать их в canvas, но если ваши миниатюры очень маленькие, то все будет в порядке. Для контейнера, где будет отображаться изображение реального размера, создайте только один объект Image и повторно используйте этот объект, а также обязательно нарисуйте его на холсте. Таким образом, каждый раз, когда пользователь нажимает на миниатюру, вы будете обновлять свой основной объект Image. Не вставляйте в страницу теги IMG. Вместо этого вставляйте теги CANVAS с правильной шириной и высотой миниатюр и основного контейнера отображения. iPad будет ругаться, если вы вставите слишком много тегов IMG. Поэтому избегайте их!!! Вставляйте только холст. Затем вы можете найти объект 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 миниатюр и каждая из них имеет размер около 15 Кб. Настоящие изображения имеют размер около 1 Мб каждое.
Обновление: я думаю, что есть еще более простой способ сделать это, в зависимости от вашего приложения. Вместо нескольких изображений, если у вас просто один элемент
или объект Image
(или, может быть, два, например, «это» изображение и «следующее» изображение, если вам нужно анимации или переходы) и просто обновите .src
, .width
, .height
и так далее, вы никогда не должны приближаться к пределу в 10 МБ. Если вы хотите создать приложение-карусель, вам сначала придется использовать заполнители меньшего размера. Возможно, вы обнаружите, что эту технику проще реализовать.
Думаю, я действительно нашел способ обойти это.
По сути, вам нужно будет глубже управлять изображениями и явно сжать любое изображение, которое вам не нужно. Обычно это делается с помощью документа .removeChild (divMyImageContainer)
или $ ("myimagecontainer"). empty ()
или что у вас есть, но в Mobile Safari это абсолютно ничего не делает; браузер просто никогда не освобождает память.
Вместо этого вам нужно обновить само изображение, чтобы оно занимало очень мало памяти; и вы можете сделать это, изменив атрибут изображения src
. Самый быстрый способ сделать это - использовать URL-адрес данных . Поэтому вместо того, чтобы сказать:
myImage.src="/path/to/image.png"
... скажите вместо этого:
myImage.src="data:image/gif;base64,AN_ENCODED_IMAGE_DATA_STRING"
Ниже приведен тест, демонстрирующий его работу. В моих тестах мое большое изображение размером 750 КБ в конечном итоге убивало браузер и останавливало все выполнение 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()
Этот код был написан для проверки моего решения , поэтому вам придется придумать, как применить его к своему собственному коду. Код состоит из трех частей, которые я объясню ниже, но единственная действительно важная часть - это imgStoredImage.src = strNullImage;
loadNextImage ()
просто загружает новое изображение и вызывает shrinkImages ()
. Он также назначает событие onload
, которое используется для начала процесса загрузки другого изображения (ошибка: I должен очистить это событие позже, но я не буду).
waitAndReload ()
здесь только для того, чтобы дать изображению время появиться на экране. Mobile Safari работает довольно медленно и отображает большие изображения, поэтому требуется время после загрузки изображения, чтобы раскрасить экран.
shrinkImages ()
просматривает все ранее загруженные изображения (кроме активного) и меняет .src
на адрес dataurl.
Я использую здесь образ файловой папки для dataurl (это было первое изображение dataurl, которое я смог найти). Я использую его просто для того, чтобы вы могли видеть, как скрипт работает. Вместо этого вы, вероятно, захотите использовать прозрачный gif, поэтому используйте вместо этого эту строку URL-адреса данных: data: image / gif; base64, R0lGODlhAQABAIAAAP /// wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw ==
Пока мне повезло, я использовал теги В общем, это безумие. Если пользователь делает утвердительный запрос на добавление графического содержимого, тогда нет причин, по которым Safari не должен позволять вам его загружать.
и установил изображение в качестве фонового изображения div.
Я столкнулся с нехваткой памяти с помощью Javascript на iPad, когда мы очень часто пытались обновить изображение, например, каждые пару секунд. Это было ошибкой при частом обновлении, но Safari вылетало на главный экран. Как только я получил контроль над временем обновления, веб-приложение заработало нормально. Казалось, что движок Javascript не может справиться со сборкой мусора достаточно быстро, чтобы отбросить все старые изображения.
Мне повезло, начиная с предложения Стива Симициса и Эндрю.
Мой проект:
Приложение на основе PhoneGap с 6 основными разделами и примерно 45 подразделами, которые имеют галерею цикла jquery от 2 до 7 изображений, каждое 640 x 440 (всего 215+ изображений). Сначала я использовал ajax для загрузки фрагментов страницы, но с тех пор перешел на одностраничный сайт, где все разделы скрыты до тех пор, пока они не понадобятся.
Изначально, после просмотра примерно 20 галерей, я получал предупреждение памяти 1, затем 2, затем сбой.
После преобразования всех изображений в блоки div с изображением, примененным в качестве фона, я мог пройти через большее количество галерей (около 35) в приложении до сбоя, но после перехода к ранее посещенным галереям оно в конечном итоге завершилось ошибкой.
Решение, которое, кажется, работает для меня, состоит в том, чтобы сохранить URL-адрес фонового изображения в атрибуте заголовка div и установить для всех фоновых изображений пустой gif. С 215+ изображениями я хотел сохранить URL-адрес где-нибудь в html для простоты и быстрого доступа.
Когда нажата кнопка поднавигации, я перезаписываю фоновое изображение css на правильный источник, который содержится в теге заголовка div, ТОЛЬКО для отображаемой галереи. Это избавило меня от необходимости использовать какой-либо причудливый javascript для хранения правильного исходного изображения.
var newUrl = $(this).attr('title');
$(this).css('background-image', 'url('+newUrl+')');
Когда нажимается новая кнопка поднавигации, я переписываю фоновое изображение последних div галереи, чтобы оно было пустым gif. Итак, помимо интерфейса gfx, у меня всегда есть только 2-7 активных изображений. Ко всему, что я добавляю, что содержит изображения, я просто использую эту технику «ondemand», чтобы поменять заголовок на фоновое изображение.
Теперь кажется, что я могу использовать приложение бесконечно без сбоев. Не знаю, поможет ли это кому-то еще, и, возможно, это не самое элегантное решение, но оно предоставило исправление для меня.
Я сообщил об ошибке в jQuery, поскольку jQuery пытается справиться с утечкой памяти... так что я считаю это ошибкой. Надеемся, что вскоре команда сможет придумать краткий и умный способ решения этой проблемы в Mobile Safari.
Я столкнулся с аналогичной проблемой в Chrome, разрабатывая расширение, которое загружает изображения на той же странице ( всплывающее окно, на самом деле) замена старых изображений новыми. Память, используемая старыми образами (удаленными из DOM), никогда не освобождается, потребляя всю память ПК за короткое время. Пробовал различные трюки с CSS, но безуспешно. При использовании оборудования с меньшим объемом памяти, чем у ПК, например iPad, эта проблема возникает раньше, естественно.