Создание превью изображений на клиенте: борьба с прожорливыми браузерами

    Всем привет! Сегодня задача у нас следующая: необходимо создать интерфейс для загрузки картинок, который бы генерировал перед загрузкой превьюшки небольшого формата. На данный момент HTML5 вовсю шествует по планете, и, казалось бы, как это реализовать должно быть предельно ясно. Есть несколько русскоязычных статей на эту тему (вот, например). Но тут есть одно но. В рассматриваемом там подходе не уделено никакого внимания расходу памяти браузером. А расход может доходить до гигантских размеров. Разумеется, если загружать одновременно не более 5-10 картинок небольшого формата, то все остается в пределах нормы; но наш интерфейс должен позволять загружать сразу много изображений формата не меньше, чем у современных фотоаппаратов-мыльниц. И вот тогда-то свободная память начинает таять на глазах.

    Для начала, чтобы оценить масштаб проблемы, реализуем подход, описываемый практически без изменений во всех статьях на эту тему, и попробуем проследить за использованием памяти. Код примеров я постарался сделать настолько простым, насколько это было возможно для демонстрации именно создания превью. Как реализовать Drag&Drop и загрузку можно посмотреть хотя бы даже в моей предыдущей статье

    Код
    var listen = function(element, event, fn) {
        return element.addEventListener(event, fn, false);
    };
    
    listen(document, 'DOMContentLoaded', function() {
    
        var fileInput = document.querySelector('#file-input');
        var listView = document.querySelector('#list-view');
    
        listen(fileInput, 'change', function(event) {
            var files = fileInput.files;
            if (files.lenght == 0) {
                return;
            }
            for(var i = 0; i < files.length; i++) {
                generatePreview(files[i]);
            }
            fileInput.value = "";
        });
    
        var generatePreview = function(file) {
            var reader = new FileReader();
            reader.onload = function(e) {
                var dataUrl = e.target.result;
                var li = document.createElement('LI');
                var image = new Image();
                image.width = 100;
                image.onload = function() {
                    // some action here
                };
                image.src = dataUrl;
                li.appendChild(image);
                listView.appendChild(li);
            };
            reader.readAsDataURL(file);
        };
    });
    



    Для тестов я использовал папку ничем не примечательных фотографий размером 3648х2736 пикселей и средним объемом 4 мегабайта. А также набор браузеров актуальных версий: Chrome (31.0), Yandex (13.12), Firefox (26.0), и IE (11.0.1). Ну и обычный Task Manager (Win 8.1).

    Итак, выбираем в поле 20 фотографий. Смотрим:
    Браузер Потребляемая память, МБ
    Chrome 994
    Yandex 1045
    Firefox 1388
    IE 1080


    Тут стоит отметить два момента: 1) Yandex и Chrome держат под каждую вкладку отдельный процесс, а Firefox и IE — нет, поэтому для последних двух в измерения попадают также некоторые накладные расходы, напрямую не связанные с нашим испытанием; 2) я снимал измерения (здесь и далее) приблизительно через 20 секунд после подгрузки всех картинок, чтобы дать возможность браузерам освободить память по горячим следам, что они и делают, хотя и совсем незначительно — в пределах 50Мб, т.е. продолжают удерживать все еще слишком большие объемы. После обновления/закрытия страницы все браузеры потихоньку освобождают память до нормальных объемов.

    Итак, понятно, что такая ситуация нас решительным образом не устраивает. Думаем, что можно предпринять…

    Первый подход к снаряду


    Первой моей мыслью было: «а что если такой перерасход получается от попытки загружать все картинки параллельно? Может быть, попытаться делать это последовательно, тем самым давая браузерам возможность немножко отдышаться?». Что ж, пробуем реализовать простейшую очередь.

    Код
    // ... откинул повторяющийся код ...
    var queue = [];
    var isProcessing = false;
    
    listen(fileInput, 'change', function(event) {
        var files = fileInput.files;
        if (files.lenght == 0) {
            return;
        }
        for(var i = 0; i < files.length; i++) {
            queue.push(files[i]);
        }
        fileInput.value = "";
        processQueue();
    });
    
    var processQueue = function() {
        if (isProcessing) {
            return;
        }
        if (queue.length == 0) {
            isProcessing = false;
            return;
        }
        isProcessing = true;
        file = queue.pop();
        var reader = new FileReader();
        reader.onload = function(e) {
            var dataUrl = e.target.result;
            var li = document.createElement('LI');
            var image = new Image();
            image.width = 100;
            image.src = dataUrl;
            li.appendChild(image);
            listView.appendChild(li);
            isProcessing = false;
            processQueue();
        };
        reader.readAsDataURL(file);
    };
    



    Результаты (на тех же самых 20-ти фотках):
    Браузер Потребляемая память, МБ
    Chrome 979
    Yandex 1119
    Firefox 1360
    IE 399


    Видим, что помогло это только в случае с IE. Ну что ж поделать — рекомендуем всем пользователям отказаться от использования каких-либо браузеров, помимо IE. Шутка. Думаем дальше…

    Второй подход


    После сеанса некоторого шаманства, приходит в голову мысль: «а может быть, проблема в том, что браузерам приходится держать в памяти широченные изображения, хотя по факту нам нужно всего лишь один раз ужать картинку до размера превью? Что если вместо обычного img использовать canvas, куда помещать уже ужатое изображение?». Так и поступим.

    Код
    var queue = [];
    var isProcessing = false;
    
    listen(fileInput, 'change', function(event) {
        // ...
    });
    
    var processQueue = function() {
        // ... те же проверки и установка флага
        file = queue.pop();
        var reader = new FileReader();
        reader.onload = function(e) {
            var dataUrl = e.target.result;
            var li = document.createElement('LI');
            var canvas = document.createElement('CANVAS');
            var ctx = canvas.getContext('2d');
            var image = new Image();
            listView.appendChild(li);
            image.onload = function() {
                var newWidth = 100;
                var newHeight = image.height * (newWidth / image.width);
                ctx.drawImage(image, 0, 0, newWidth, newHeight);
                li.appendChild(canvas);
            };
            image.src = dataUrl;
            isProcessing = false;
            processQueue();
        };
        reader.readAsDataURL(file);
    };
    



    Результаты (все те же 20 картинок):
    Браузер Потребляемая память, МБ
    Chrome 188 (в пиковые моменты доходил до ~800МБ, но быстро скинул)
    Yandex 201 (в пиковые моменты доходил до ~1ГБ, но сразу скинул, как и Хром)
    Firefox 661 (пик ~900. надо отметить, что подождав еще с минуту, скинул до 300)
    IE 103 (пик ~260)


    Несмотря на большой расход в процессе (у всех, кроме IE), браузеры хотя бы начали сразу освобождать память. Это уже не может не радовать. Но все же праздновать окончательную победу пока рановато. Думаем, что можно еще предпринять…

    Третий подход


    В процессе дальнейших метаний и не слишком удачных экспериментов, вспоминаем, что когда-то попадался на глаза такой API, как ObjectURL (создание и утилизация), который позволяет создавать локальные ссылки на любые бинарные данные, хранимые в кеше браузера, а также утилизировать их. В теории, это может помочь нам избежать обработки гигантских DataURL. Скорее пробуем

    Код
    // ... создание таких же переменных
    var processQueue = function() {
        // ... проверки и установка флага
        isProcessing = true;
        file = queue.pop();
        var li = document.createElement('LI');
        var canvas = document.createElement('CANVAS');
        var ctx = canvas.getContext('2d');
        var image = new Image();
        listView.appendChild(li);
        image.onload = function() {
            var newWidth = 100;
            var newHeight = image.height * (newWidth / image.width);
            ctx.drawImage(image, 0, 0, newWidth, newHeight);
            URL.revokeObjectURL(image.src);
            li.appendChild(canvas);
            isProcessing = false;
            processQueue();
        };
        image.src = URL.createObjectURL(file);
    };
    



    Результаты:
    Браузер Потребляемая память, МБ
    Chrome 881
    Yandex 927
    Firefox 140 (пик ~860)
    IE 36 (пик ~70)


    Что же мы получили? Ну, во-первых, отличные результаты в IE. Более или менее приемлемые в FF. А вот с WebKit'овыми браузерами как-будто отскочили обратно. Справедливости ради надо отметить, что при этом во всех браузерах картинки стали обрабатываться быстрее чисто по ощущениям, но при этом в IE возникали кратковременные фризы. Не исключено также, что FF и IE по-честному сразу освобождают ресурсы после вызова URL.revokeObjectURL(), а вебкитовым браузерам нужно какое-то время для этого (возможно даже, что они будут шустрее это делать в условиях нехватки памяти). Дальше можно пойти двумя путями: 1) разделить подходы — в браузерах на вебките вернуться ко второму подходу (с этим все понятно — дело техники); и 2) попробовать везде довести до ума третий подход. Попробуем последний вариант…

    Подход четвертый (и последний): что-бы еще такое заоптимизировать?


    Немного поднатужившись, выжимаем из себя еще пару улучшений. Первое, это выносим создание элемента img из обработчика очереди: теперь будем повторно использовать один и тот же заранее созданный объект. Забегая вперед скажу, что это помогло существенно улучшить ситуацию с памятью в вебкитенышаховых браузерах — что и требовалось. А второе, это давно известный трюк — немного откладываем каждую очередную обработку при помощи setTimeout(), это помогло улучшить ситуацию с кратковременными фризами. Итак, результат:

    Код
    // Привожу код целиком
    var listen = function(element, event, fn) {
        return element.addEventListener(event, fn, false);
    };
    
    listen(document, 'DOMContentLoaded', function() {
    
        var fileInput = document.querySelector('#file-input');
        var listView = document.querySelector('#list-view');
    
        var queue = [];
        var isProcessing = false;
    
        var image = new Image(); // теперь сразу создаем элемент img
        var imgLoadHandler;
    
        listen(fileInput, 'change', function(event) {
            var files = fileInput.files;
            if (files.lenght == 0) {
                return;
            }
            for(var i = 0; i < files.length; i++) {
                queue.push(files[i]);
            }
            fileInput.value = "";
            processQueue();
        });
    
        var processQueue = function() {
            if (isProcessing) {
                return;
            }
            if (queue.length == 0) {
                isProcessing = false;
                return;
            }
            isProcessing = true;
            file = queue.pop();
            var li = document.createElement('LI');
            var canvas = document.createElement('CANVAS');
            var ctx = canvas.getContext('2d');
            // теперь необходимо снимать старый обработчик
            image.removeEventListener('load', imgLoadHandler, false);
            imgLoadHandler = function() {
                var newWidth = 100;
                var newHeight = image.height * (newWidth / image.width);
                ctx.drawImage(image, 0, 0, newWidth, newHeight);
                URL.revokeObjectURL(image.src);
                li.appendChild(canvas);
                isProcessing = false;
                setTimeout(processQueue, 200); // добавили краткий таймаут
            };
            listView.appendChild(li);
            listen(image, 'load', imgLoadHandler);
            image.src = URL.createObjectURL(file);
        };
    });
    



    Тестируем:
    Браузер Потребляемая память, МБ
    Chrome 103 (пик ~150)
    Yandex 113 (пик ~150, как и у Хрома)
    Firefox 107 (пик ~510)
    IE 40 (а выше и не подымалось. Как будто вообще никакой работы не происходило)


    Заодно протестируем еще и на 100 картинках аналогичного размера:
    Браузер Потребляемая память, МБ
    Chrome 98 (пик ~150)
    Yandex 150 (пик ~180)
    Firefox 104 (пик ~520)
    IE 40 (все те же 40МБ!)


    Видим, что увеличение числа обрабатываемых изображений практически не увеличивает расход. На этом, пожалуй, и остановимся.

    Заключение


    Признаться, после самых первых изысканий, я в какой-то момент подумал, что при нынешнем состоянии дел не выйдет реализовать данную возможность без чрезмерного перерасхода памяти. Все-таки, некрасиво подвешивать пользователю [пожелавшему загрузить 100 картинок разом] его гипотетический нетбук. Но приятно, что эти сомнения удалось побороть :)

    Итак, нам удалось выяснить несколько моментов. Номер раз: использование DataURL годится только для работы с картинками очень небольшого формата (для больших предпочтительней использовать API objectURL, состоящий всего из двух методов). Номер два: надо быть осторожным с созданием большого количества объектов Image. Номер три: не производить всю обработку одновременно.

    Ну и, коль скоро это небольшое исследование непостижимым образом привело к сравнению браузеров, пробежимся по каждому в отдельности.

    Firefox проиграл?

    Несмотря на выделяющийся по сравнению с остальными пиковый расход, думаю, что все же ситуация вполне приемлемая. Во-первых (не упомянул выше), еще через 30 секунд после замеров память опускалась до 60МБ, что даже ниже по сравнению с вебкитовыми; а во-вторых, вполне вероятно, что в условиях жесткой нехватки Firefox периодически подчищал бы память в процессе обработки и в конце концов даже на пике не отъедал бы столько. В общем, ставим зачет.

    Всем равняться на IE!

    Это опять шутка :) Но если говорить объективно, то надо признать, что IE сейчас — не просто инструмент для скачивания нормального браузера, а как минимум еще один годный обозреватель.

    Яндекс.Браузер немного отстает от старших братьев?

    Не думаю. И вот почему: дело в том, что он у меня сейчас используется как основной, а соответственно нельзя назвать эксперимент кристально чистым. Пара плагинов, история, синхронизация — все это вполне могло и вызвать это небольшое отставание.

    А где же Опера?

    Очень не хотелось ставить отдельно под этот эксперимент 12-ю версию. Не смотря на то, что она еще кое-кем используется, скоро и это число преданных поклонников вынуждено будет либо обновиться, либо мигрировать на другой браузер. А по поводу новой, вебкитовой — есть все основания полагать, что ситуация схожа с Yandex'ом и Chrome'ом.

    UPD: Проверил все-таки в 12-й Опере. Результаты следующие: 3-й (а соответственно и 4-й) подходы не работают. Второй подход отъедает 500МБ на пике и 300МБ после окончания обработки.

    Ну а как же Safari?

    На win это редкий зверь, но на маке протестировал итоговый вариант (все на тех же 20-ти фотографиях). В процессе обработки расход памяти вообще не увеличивался.

    Что с мобильными браузерами?

    Проверил также в Safari на iPhone 5S. Наблюдается кратковременный фриз, но при этом память количество свободной памяти практически не уменьшилось. Я не нашел сходу, можно ли как-то увидеть, сколько конкретно резервирует каждый процесс в отдельности, буду признателен, если кто-то подскажет в каментах. Устройства на Android, к сожалению, под рукой не оказалось. Быть может, кто-то не поленится проверить самостоятельно и поделиться результатами.

    Спасибо за внимание. Надеюсь, кому-то статья поможет не тратить время на аналогичные изыскания. И с прошедшими праздниками тебя, %username%!
    Share post

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 34

      +4
      Спасибо за статью, примерно так и хотел поступить. Теперь не придётся на себе всё это испытывать.
        +4
        Очень не хотелось ставить отдельно под этот эксперимент 12-ю версию. Не смотря на то, что она еще кое-кем используется, скоро и это число преданных поклонников вынуждено будет либо обновиться, либо мигрировать на другой браузер.


        В стандартном установщике Opera 12 есть возможность поставить её переносную версию — быстро и без всякого внедрения в ОС.
        Да и насчёт миграции — вопрос спорный. Достойной альтернативы в обозримом будущем не предвидится (не считая FF с расширениями — но он заметно медленнее), а комфортно обозревать интернет с помощью настоящей Opera можно будет ещё несколько лет. Так что рано ещё её списывать со счетов, по крайней мере в рунете.
          +4
          Насчет достойной альтернативы — вопрос спорный. Да, в той самой Опере, возможно, есть некоторые фичи, которые Вам дороги. И в то же время с внедрением новых фич у остальных, 12-я версия все более безнадежно будет устаревать и в конце концов минусы перевесят плюсы.

          Но ок, пока не буду списывать со счетов. По крайней мере мысленно :) Если будет время, попробую в ней прогнать.
            +2
            Рано или поздно в новой Опере вернут и допилят старые фишки. Так что мы все будем довольны. Я как поклонник оперы с малых ногтей (ещё сижу на старой дома, но на новой в офисе), и я как веб-разработчик будем рады обрести вновь Нашу Прелесть.
              +1
              Жду не дождусь синхронизации в 19 версии ) Уже и дома и на работе перешёл на новую полностью.
                0
                Так синхронизация уже работает. Правда пока только переносит Экспресс панель и копилку. Но уже что-то. Скоро пароли допилят. (Opera Next последняя). А если пароли у вас в lastpass так вообще можно сказать все готово)
            +1
            а комфортно обозревать интернет с помощью настоящей Opera можно будет ещё несколько лет.
            Уже давно нельзя — ужаснейшие тормоза при любом действии, даже при скролле. Многие сайты (даже gmail) тормозят так, что работать практически нельзя.
            Можно еще подождать новую оперу, но судя по производительности разработчиков и скорости возвращения функционала, ждать придется несколько лет.
              +1
              >ужаснейшие тормоза при любом действии, даже при скролле
              Правда? Что-то не замечаю…
              Вот тот расход памяти, о котором написал Safron, может оказаться реальным минусом.

              Отправлено из 12.16, открыто четыре окна, в каждом — пара-тройка десятков вкладок. Памяти съедено около 1Гб.

              –1
              Насчет альтернативы это вы зря.

              Например:
              Maxthon — самый достойный браузер с большинство фичей Opera реализовано идаже есть уникальные фичи. Работает на WebKit движке с возможностью переключения на Trident.

              Реализованого функционала HTML5 Больше чем у хрома. HTML5TEST: Maxthon 4.2 — 515. Тогда ка у ХРОМА 31 — 503, Opera 18 — 494, FF26 — 446.
              По скорости не намного отстает от Chroma. 2708 против 3015 по peacekeeper.futuremark.com. И намного быстрее Ослика 11 (1719 мирный попугай). И совсем чуть чуть медленней Opera 18 (2878 попугаев).
              +1
              Надеюсь, что созданные на клиенте превьюхи не записываются на сервере без доп. проверок/обработок и не показываются потом посетителям «как есть».
                +1
                Конкретно в моем приложении они пока еще даже не отправляются на сервер :) Но, разумеется, и не планирую так делать. Тем более, мне не нравится как некоторые браузеры сглаживают уменьшенные таким образом изображения.

                Скажу пару слов о том, какова конечная цель задуманного. Пользователю необходимо иметь возможность загружать сразу большие альбомы и подборки фотографий. При этом, закинув все фотографии из папки, он, возможно, захочет какие-то тут же убрать из списка. А некоторые, вероятно, сразу же повернуть. Если делать по старинке, т.е. грузить сразу после добавления на сервер, а в ответ получать ссылки на уже уменьшенные изображения, то а) серверу надо будет заниматься обработкой как можно скорее, и б) нужно проделывать лишнюю работу: удаление ненужных фотографий и осуществление поворота картинок. Теперь же, обработку можно отложить, но при этом пользователь будет моментально видеть результат (пусть и приблизительный). Плюс данные о повороте будут отправляться сразу с изображением и на сервере можно сразу же повернуть изображение как надо, параллельно с созданием превьюхи. Более того, я думаю пойти еще дальше и на клиенте считать md5 (или sha1) хеш изображения и предупреждать пользователя о вероятных дублях.
                  +3
                  Я это на всякий случай написал, а то мало-ли кто забудет.
                  Многие фотики в exif пишут ориентацию фотки. Если она там есть, то надо сразу её использовать.
                    0
                    Кстати, интересно. Спасибо за инфо. Вот теперь придется еще и exif пытаться на клиенте расковыривать…
                      +1
                      Если в фотошопе, лайтруме и т.п. редакторах правились фотки, то экзиф информация сотрется.
                      Не забудьте и это учесть:)
                        0
                        Ок. Ну а где-то ее и вовсе не будет изначально. Так что в любом случае — это опционально. А не знаете, кстати, готовых JS-библиотек?
                          0
                          Неа, не встречал.
                          В основном встречается что-то наподобие вот такого: github.com/blueimp/JavaScript-Load-Image
                          У Вас уже есть готовый функционал ресайза изображения, а добавить парсер exif-инфы дело техники.

                          Кстати, если будет время и желание, то можно оформить этот парсер в виде либы, залить на гитхаб, опубликовать решение на Хабре и Вы получите комментарии благодарности местных жителей:))
                            0
                            Окей, договорились — если решусь реализовать это для своего проекта, то обязательно опубликую :)
                              0
                              Присмотрелся подробнее к библиотеке по ссылке. Хорошая библиотека. Думаю, нет смысла городить велосипед с собственным EXIF-парсером, там это уже есть
                      0
                      может вы смотрели, какие именно операции приводят к увеличению потребления памяти (выбор файлов в диалоговом окне, чтение в FileObject через readAsDataURI(), добавление картинки в DOM и т.п.)?
                      хочу понять, имеет-ли все это смысл, если оригиналы терять нельзя — например для последующей отправки их на сервер — и какую стратегию лучше выбрать.
                        +1
                        Смотрел. Имеет смысл. Вы можете сохранять где-то в массиве (или каком-то объекте, отображающем загрузку) исходные объекты типа File. Сами по себе они ничего практически не весят, но их можно приаттачивать к запросу, тем самым отправляя оригинал. Выбор файлов, естественно тоже ничего не жрет (еще бы вывод системного окошка требовал ресурсов! :), а от readAsDataURL нам удалось отказаться.
                          0
                          спасибо!
                    +2
                    А по поводу новой, вебкитовой — есть все основания полагать, что ситуация схожа с Yandex'ом и Chrome'ом.
                    довольно странный выбор браузеров. Яндекс и Хром это практически один и тот же браузер. Ключевая фраза автора, при тестировании яндекса — «как и у хрома».

                    Я подозреваю, что опера имеет куда меньше сходств с хромом, нежели яндекс. И логичнее было бы использовать ее в тестах. Разве нет?
                      0
                      Понимаете, изначально вообще не было цели устраивать тесты браузеров. Цель была найти подход, позволяющий генерить превью для относительно больших картинок, не выходя за разумные рамки в плане использования памяти. Я сейчас пользуюсь Яндексом, поэтому все смотрю изначально в нем. Но при этом важно было понимать, что применяемые оптимизации проявляются не только в моем браузере, поэтому я стал параллельно проверять в остальных, установленных в системе. Можно было бы, наверное, и не включать Хром. Но тогда для многих выбор показался бы еще более странным :)

                      Если посмотреть на все это немного под другим углом, то выбор и не покажется таким уж странным: представлено три основных движка, при этом самый популярный из них в двух исполнениях.

                      P.S. По 12-й Опере добавил краткий UPD
                      0
                      Как вы меряли потребляемую память? Уверен, это прольет свет на выдающийся результат IE.
                        0
                        В статье упоминается, что я попросту наблюдал за стандартным таск менеджером. Никаких других, более точных замеров не производил. А что, ИЕ куда-то «прячет» память?
                          0
                          Если колонка «Память» на вкладке Процессы, то это текущий рабочий набор процесса или процессов (кстати для Хрома суммировали?).

                          IE не прячет. Но имеет определенное преимущество: работу с памятью его разработчикам нужно оптимизировать только под одну ось. А минимизировать рабочий набор их там заставляют жестко. :)
                          Деталей я конечно не знаю, но могу предположить, что в других браузерах блоки памяти с освобожденными объектами сразу не используются повторно, а ждут, например, GC.

                          P.S. По теме, вот еще один свежий бенчмарк: www.ghacks.net/2014/01/02/chrome-34-firefox-29-internet-explorer-11-memory-use-2014/
                            0
                            Спасибо за пояснения и ссылку.

                            Да, я смотрел именно в колонку «Память». Конечно, я понимаю, что использовать таск менеджер для серьезного исследования расхода памяти не стоит, но в контексте данной задачи этого было достаточно чтобы зафиксировать резкие возрастания ее расхода. Важно было скорее решить проблему, слишком глубоко не вдаваясь в причины ее возникновения.

                            Согласен, что ИЕ скорее всего более эффективно взаимодействует с родной ОСью и в этом у него есть преимущество (что характерно, подобным образом ведет себя и Сафари на маке — т.е. выглядит так, будто ему вообще не требуется никакой дополнительной памяти для этих операций).
                        0
                        А не пробовали освобождать память через delete image?
                          0
                          Пробовал. Не помогает. Возможно, освобождение откладывается до очередного вызова GC. Но пока этот вызов произойдет, уже слишком вырастает расход.
                          0
                          Это я ерунду написал, через delete атрибуты объекта удаляют.
                          Внимательно посмотрел на код, и мне кажется, Вы не совсем правильно сделали выводы. Очевидно, основной профит тут получен от использования [canvas]. Просто при ее использовании можно забывать про объект Image. Если в способ с [canvas], где используется много объектов Image, добавить обнуление src: image.src = '' , то все будет так же замечательно (я проверил), как и в последнем примере, с одним Image.
                          Плюс, из кода можно поудалять телодвижения насчет isProcessing — все равно же одна картинка обрабатывается в каждый момент времени вплоть до ее полной загрузки. С другой стороны, можно попробовать увеличить количество картинок, обрабатываемых одномоментно хотя бы до трех, вряд ли будет фриз.
                          Но вообще, Вы сделали полезное исследование, даже ребята из mozilla не стали этим заморачиваться, например developer.mozilla.org/en-US/docs/Using_files_from_web_applications#Example.3A_Using_object_URLs_to_display_images
                          PS Код для второго подхода приведен неполностью — нет добавления [canvas] в DOM.
                            0
                            Да, к delete я не стал цепляться, поняв основную суть вопроса. На самом деле я пробовал просто
                            image = null;
                            и что-то не особо помогало. Возможно, если именно src обнулять будет другой результат. Впрочем, все равно, зачем каждый раз создавать, если делать поочередно. Если по три одновременно, то да, тогда без этого не обойтись.

                            isProcessing нужен для тех случаев, если в момент обработки добавили еще картинок через форму.

                            Да, я и начал свои исследования с MDN и в конце концов понял, что никто особо не парился над этим, или по крайней мере не спешил публиковать результаты.
                              0
                              Впрочем, все равно, зачем каждый раз создавать, если делать поочередно.

                              Если каждый раз «обнулять» Image.src, то может быть выгода по сравнению со случаем, когда останется один объект Image с «необнуленным» src.
                            0
                            Итоговым способом получаются несглаженные превьюшки, некрасивые.
                              0
                              К сожалению, есть такая проблема. Однако, это меньшее из зол, по сравнению с подвешиванием браузера из-за перерасхода памяти, либо с отсутствием превьюшек вообще. В конце концов, превью призваны не радовать глаз, а более наглядно показать, с какими именно изображениями мы имеем дело. И эта задача решена. Никто не мешает Вам после загрузки на сервер делать красивые, супер-сглаженные превью.

                            Only users with full accounts can post comments. Log in, please.