задача: загрузить картинку, проверить, влезла ли она в заданные габариты, если не влезла, то пропорционально её уменьшить.

Рецепт рассказывает, как решить задачу простым и очевидным способом, работающим и в 6 и в 7-8 версиях флэша. :) Это рецепт для тех, кто только начинает понимать, что 90% флэша — это скрипт. Тем не менее, здесь не объясняются некоторые базовые понятия, вроде "что такое переменная?", "что такое функция?" и т. п. Если вы не знаете этого, обратитесь к другим справочным материалам.

назад к списку уроков и рецептов

1. загружаем картинку

2. определяем, что картинка загрузилась

3. подгоняем размеры картинки после загрузки

4. центруем отмасштабированную картинку

5. результат / исходник



1. Загружаем картинку

Допустим, на сцене есть клип с именем clip, в который надо грузить картинку. Создадим в нем переменные, указывающие на размер области, в которую должна поместиться картинка.
clip.maxWidth = 300;  // максимум по ширине
clip.maxHeight = 400; // максимум по высоте
Загрузим в него картинку:
clip.image.removeMovieClip();
clip.createEmptyMovieClip("image", 0);
clip.image.loadMovie("something.jpg");
Мы создали внутри клипа clip пустой клип с именем image, и вызвали загрузку в него картинки. Естесственно можно было и не создавать "вкладыша", а грузить прямо в clip. но так удобнее, особенно если надо загружать несколько картинок одну за другой в один клип.

Объединить этот код в функцию - хорошая идея.
clip.loadImage = function(file)
{
    this.image.removeMovieClip();
    this.createEmptyMovieClip("image", 0);
    this.image.loadMovie(file);
};
Теперь для того, чтобы загрузить в клип "something.jpg", будем вызываеть не clip.loadMovie("something.jpg"), а clip.loadImage("something.jpg"), а дальше уже пусть этот клип сам разбирается что к чему.

Обратите внимание, что внутри метода loadImage, к клипу мы обращаемся через ключевое слово this, тогда как раньше, до создания функции, мы писали "clip". Это вызвано тем, что ссылка на метод loadImage находится в clip, и поэтому в теле метода не требуется указывать имя клипа, с которым мы работаем — достаточно написать "мой.image.removeMovieClip", "для_меня.createEmptyMovieClip" и т. п.
this как раз и выполняет роль выражения "для_меня" или "мой".

2. Определяем, что картинка загрузилась

Нужно дождаться пока картинка загрузится и когда это произойдет выполнить какие-то действия. Загрузка картинки любого размера и при любых условиях связи НЕ происходит мгновенно, поэтому мы не можем просто взять и сразу же после loadMovie написать какие-то действия с загруженной картинкой.
clip.onEnterFrame = function()
{
    var l = this.image.getBytesLoaded();
    var t = this.image.getBytesTotal();
    if (t>0 && l>=t) { 
        // загрузка закончилась
        delete this.onEnterFrame;
    }
};
Выражение if (t>0 && l>=t) означает "если общий вес картинки больше нуля И вес загруженной части не меньше общего". проверка того, что общий размер больше нуля, нужна из-за того, что в первые моменты после вызова загрузки общий вес может определяться как -1, что означает что данные о весе еще не получены.

Приведенный выше код создает функцию внутри clip, которая срабатывает при каждой смене кадра, и проверяет загрузилась ли картинка. Если загрузилась - эта функция себя же удаляет. В этот момент она должна выполнить какие-то действия по проверке габаритов загруженного, их мы рассмотрим позже.

Обработчик onEnterFrame, проверяющий состояние загрузки картинки, нужно создать только тогда, когда мы вызвали загрузку. Поэтому добавим этот код в loadImage().
clip.loadImage = function(file)
{
    this.image.removeMovieClip();
    this.createEmptyMovieClip("image", 0);
    this.image.loadMovie(file);
    this.onEnterFrame = function()
    {
        var l = this.image.getBytesLoaded();
        var t = this.image.getBytesTotal();
        if (t>0 && l>=t) {
            // загрузка закончилась
            delete this.onEnterFrame;
        }
    };
};
Итак, что получилось:
  • у нас есть функция, которая грузит картинку куда следует
  • эта функция удалит предыдущую картинку (если что-то уже было загружено)
  • эта функция создаст обработчик onEnterFrame, который отследит момент, когда картинка полностью загрузится
  • обработчик onEnterFrame по окончании загрузки картинки запустит действия по её масштабированию
Красота.

3. Подгоняем размеры картинки после загрузки

Теперь надо создать функцию, которая будет заниматься проверкой габаритов того, что загрузилось и "подравниванием". Создадим эту функцию в clip.
clip.resize = function()
{
    // пока что пусто...
};
Как узнать, высоту или ширину картинки нужно изменить? И на сколько?

Поделим заданную нами ширину контейнера на ширину картинки.
var ratio_x = this.maxWidth/this.image._width;
Если ratio_x будет больше 1, значит ширина контейнера больше чем ширина картинки, если ratio_x меньше единицы, то наоборот. Таким образом мы можем определить, нужно ли менять ширину, чтобы картинка вписалась в заданную область. С высотой абсолютно та же ситуация:
var ratio_y = this.maxHeight/this.image._height;
Теперь у нас есть две переменных, которые содержат отношения габаритов заданной нами области к габаритам картинки. Нужно проверить, какой из габаритов картинки больше вылезает за соответствующий габарит контейнера. Т.е. где картинка превышает габариты сильнее: в ширине, или в высоте. Если ширина превышает допустимый размер сильнее, то её нужно приравнять к maxWidth, после чего изменить высоту во столько же раз, во сколько изменилась ширина. Если высота превышает допустимый размер сильнее, то её нужно приравнять к maxHeight, после чего изменить ширину во столько же раз, во сколько изменилась высота. Собственно, это и есть словесная запись нашего алгоритма.

Прежде чем записать это всё в виде кода, надо подумать, каким образом мы "изменим ширину во столько же раз во сколько изменилась высота". В любом клипе существуют свойства _xscale и _yscale, которые показывают масштаб клипа по вертикали и горизонтали. По умолчанию клип имеет масштаб 100%, т.ё. _xscale и _yscale равны 100. Если мы изменим свойство _width, то автоматически изменится и свойство _xscale. Аналогично связаны _height и _yscale. При изменении размеров (_width и _height) флэш самостоятельно пересчитывает значения масштабов (_xscale и _yscale) и наоборот, при изменении масштабов пересчитывает значения размеров. Воспользуемся этим!
clip.resize = function () {
    var ratio_x = this.maxWidth/this.image._width;
    var ratio_y = this.maxHeight/this.image._height;
    if (ratio_x<=ratio_y) {
        // ширина равна максимальной
        // а высота в соответствии с пропорцией
        this.image._width = this.maxWidth;
        // а теперь просто приравниваем масштаб по вертикали масштабу по горизонтали
        this.image._yscale = this.image._xscale;
    } else {
        // высота равна максимальной
        // а ширина в соответствии с пропорцией
        this.image._height = this.maxHeight;
        // а теперь просто приравниваем масштаб по горизонтали масштабу по по вертикали
        this.image._xscale = this.image._yscale;
    }
}
 
Чтобы картинка не исказилась при масштабировании, необходимо чтобы её масштаб по горизонтали был равен её масштабу по вертикали. А нам и не надо считать, каким стал искомый масштаб, флэш сделал это за нас. :)

Итак, первая версия функции готова, можно проверить, как это всё вместе работает. Для этого добавим в обработчик onEnterFrame вызов resize() по окончании загрузки.
clip.maxWidth = 300;
clip.maxHeight = 400;
//
clip.loadImage = function(file)
{
    this.image.removeMovieClip();
    this.createEmptyMovieClip("image", 0);
    this.image.loadMovie(file);
    this.onEnterFrame = function()
    {
        var l = this.image.getBytesLoaded();
        var t = this.image.getBytesTotal();
        if (t>0 && l>=t) {
            // загрузка закончилась
            delete this.onEnterFrame;
            this.resize();
        }
    };
};
//
clip.resize = function()
{
    var ratio_x = this.maxWidth/this.image._width;
    var ratio_y = this.maxHeight/this.image._height;
    if (ratio_x<=ratio_y) {
        // ширина равна максимальной
        // а высота в соответствии с пропорцией
        this.image._width = this.maxWidth;
        this.image._yscale = this.image._xscale;
    } else {
        // высота равна максимальной
        // а ширина в соответствии с пропорцией
        this.image._height = this.maxHeight;
        this.image._xscale = this.image._yscale;
    }
};
Вызовем загрузку:
clip.loadImage("something.jpg");
Всё бы хорошо, но мы нигде не проверили, а нужно ли менять размеры картинки? Может она и так замечательно вписывается в контейнер? :) Сейчас, если мы загрузим маленькую картинку, она пропорционально растянется. Может быть это даже хорошо, но если мы будем грузить .jpg, то после растяжения картинка будет выглядеть некрасиво. Впрочем, совсем убирать возможность увеличивать картинку, если она слишком маленькая, тоже не хочется. Создадим переменную Булева типа, которая будет указывать, разрешаем мы увеличивать картинки или разрешаем их только уменьшать.
clip.allowEnlarge = false;
Такие переменные программисты часто называют "флажками". Если переменная имеет значение true, говорят "флаг установлен". В данном случае мы присвоили переменной значение false, что означает, что растягивать картинки нельзя. Перепишем функцию resize() с учетом флажка:
clip.resize = function()
{
    var ratio_x = this.maxWidth/this.image._width;
    var ratio_y = this.maxHeight/this.image._height;
    if (ratio_x<=ratio_y) {
        // выполняем, если нужно уменьшить, 
        // или увеличить+увеличение разрешено
        if (ratio_x<1 || this.allowEnlarge) {
            // ширина равна максимальной
            // а высота в соответствии с пропорцией
            this.image._width = this.maxWidth;
            this.image._yscale = this.image._xscale;
        }
    } else {
        // выполняем, если нужно уменьшить, 
        // или увеличить+увеличение разрешено
        if (ratio_y<1 || this.allowEnlarge) {
            // высота равна максимальной
            // а ширина в соответствии с пропорцией
            this.image._height = this.maxHeight;
            this.image._xscale = this.image._yscale;
        }
    }
};
Теперь, если нам понадобится и увеличивать и уменьшать картинки, нам не придется ничего переписывать в своем коде. Достаточно будет изменить значение флажка allowEnlarge.

4. Центруем отмасштабированную картинку

Работает? Восхитительно.
Только чувствуется некоторая недосказанность... Почему картинки где-то сбоку? Если один (или оба) из габаритов картинки меньше чем максимальный, то хорошо бы центровать изображение. Т.е. сначала проверить высоту и ширину загрузившейся картинки, изменить их при необходимости, а после этого отцентровать изображение относительно области, заданной maxWidth и maxHeight.

Центровка:
this.image._x = Math.round((this.maxWidth-this.image._width)/2);
this.image._y = Math.round((this.maxHeight-this.image._height)/2);
Math.round округляет получившееся значение для координаты. Это нужно для того, чтобы пиксели картинки не смазывались (если расположить картинку по нецелым координатам, будет действовать сглаживание). Центровку необходимо выполнять в любом случае, даже если ни один из габаритов картинки не превышает максимальное значение. Итого, получаем следующее:
clip.maxWidth = 300;
clip.maxHeight = 400;
//
clip.loadImage = function(file)
{
    this.image.removeMovieClip();
    this.createEmptyMovieClip("image", 0);
    this.image.loadMovie(file);
    this.onEnterFrame = function()
    {
        var l = this.image.getBytesLoaded();
        var t = this.image.getBytesTotal();
        if (t>0 && l>=t) {
            // загрузка закончилась
            delete this.onEnterFrame;
            this.resize();
        }
    };
};
//
clip.resize = function()
{
    var ratio_x = this.maxWidth/this.image._width;
    var ratio_y = this.maxHeight/this.image._height;
    if (ratio_x<=ratio_y) {
        // выполняем, если нужно уменьшить, 
        // или увеличить+увеличение разрешено
        if (ratio_x<1 || this.allowEnlarge) {
            // ширина равна максимальной
            // а высота в соответствии с пропорцией
            this.image._width = this.maxWidth;
            this.image._yscale = this.image._xscale;
        }
    } else {
        // выполняем, если нужно уменьшить, 
        // или увеличить+увеличение разрешено
        if (ratio_y<1 || this.allowEnlarge) {
            // высота равна максимальной
            // а ширина в соответствии с пропорцией
            this.image._height = this.maxHeight;
            this.image._xscale = this.image._yscale;
        }
    }
    // центровка
    this.image._x = Math.round((this.maxWidth-this.image._width)/2);
    this.image._y = Math.round((this.maxHeight-this.image._height)/2);
};
 

5. Результат

Результат наших трудов: [посмотреть] [исходник]
Ай дa мы. :)

Способов достичь такого результата - масса. Данное описание рассматривает только один из них, самый общий и везде применимый.

в начало
назад к списку уроков и рецептов


Корректировать материал помогли: