Статьи

Создание лучшей растровой кнопки в AS3

Создание кнопки из растрового изображения может быть утомительным. Если вы используете Flash IDE, вы можете создать маску, чтобы определить, какие пиксели являются частью кнопки, а какие нет, но в любом другом рабочем процессе весь прямоугольник, содержащий растровое изображение, включая любые прозрачные пиксели, будет кликабельным. , В этом уроке вы узнаете, как автоматически сделать все прозрачные пиксели не кликабельными всего за несколько строк кода.


Давайте посмотрим на конечный результат, к которому мы будем стремиться:

Обратите внимание, что курсор в виде руки появляется только при наведении курсора на фактическое изображение; даже щели в волосах не вызывают его появления. И это не только для показа: нажатия кнопок регистрируются только при нажатии на эти области.


На первый взгляд, SWF выше выглядит очень просто. Но посмотрите поближе! Обратите внимание, что в приведенной выше демонстрации нажатие кнопки считается только в том случае, если вы щелкнете где-то на фактическом изображении. Это не то, что обычно происходит. Поскольку растровое изображение всегда является прямоугольником, нажатие в любом месте его прямоугольника обычно считается нажатием кнопки. Взгляните на пример ниже, где я обрисовал границу прямоугольника нашего изображения. Здесь вы можете щелкнуть в любом месте внутри прямоугольника, включая прозрачные области.

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

Если вы еще не знакомы с объектно-ориентированным программированием или FlashDevelop , я рекомендую проверить предоставленные ссылки перед началом этого урока.


Откройте FlashDevelop и создайте новый проект AS3 (« Проект»> «Новый проект» ) и назовите его как BitmapButtonProj . Справа откройте папку src и дважды щелкните Main.as чтобы открыть ее. Добавьте новый класс в ваш проект ( щелкните правой кнопкой мыши / src /> Добавить> Новый класс ) с именем BitmapButton


Теперь нам нужно изображение для работы. Вот тот, который я использую:

Изображение лица для класса BitmapButton

Чтобы использовать растровое изображение (например, файл .jpeg или файл .png) в ActionScript 3, мы должны встроить его. FlashDevelop делает это легко. После сохранения указанного выше изображения где-нибудь щелкните правой кнопкой мыши папку lib справа, наведите курсор мыши на кнопку « Добавить» и выберите параметр « Актив библиотеки» .

Как добавить изображение в библиотеку в FlashDevelop

Если вы хотите использовать собственное изображение, обязательно выберите его с прозрачностью.

Изображение, которое вы выбрали, теперь появится в папке lib в FlashDevelop. Щелкните правой кнопкой мыши изображение и выберите « Создать код для вставки» .

1
2
3
4
public class Main extends Sprite
{
    [Embed(source = «../lib/face.png»)]
    private var ButtonImg:Class;

Этот код embeds изображение в наш проект. Всякий раз, когда вы встраиваете изображение, вам нужно определить на следующей строке класс, который представляет внедренное изображение. В этом случае наш класс называется ButtonImg.


Далее мы добавим TextField чтобы отобразить, сколько раз мы нажали кнопку (которая будет добавлена ​​далее). Добавьте это в Main() :

1
2
3
4
5
6
clicksTextField = new TextField();
clicksTextField.width = stage.stageWidth;
clicksTextField.defaultTextFormat = new TextFormat(null, 14, 0, true, false, false, null, null, TextFormatAlign.CENTER);
clicksTextField.text = «Button Presses: » + numClicks;
clicksTextField.mouseEnabled = false;
addChild(clicksTextField);

Приведенный выше код просто форматирует текст для отображения в верхней части нашего проекта. Не пропустите, как мы объявили наш TextField в строке 15.


Этот код также должен идти в Main() :

Когда мы создаем экземпляр BitmapButton в строке 36, мы передаем наш класс встроенного изображения в качестве параметра. Это будет использоваться классом BitmapButton . После этого мы можем просто обработать наш экземпляр BitmapButton как любой другой DisplayObject : мы можем позиционировать его и добавить прослушиватель MouseEvent.CLICK как обычно.


Добавьте эту функцию обработчика событий в Main.as :

1
2
3
4
5
private function onButtonClick(e:MouseEvent):void
{
    numClicks++;
    clicksTextField.text = «Button Presses: » + numClicks;
}

Последний фрагмент кода в нашем Main классе — это прослушиватель событий для нажатий кнопок. В нем мы просто добавляем один к количеству кликов, numClicks, и обновляем текст в clicksTextField.


Переверните на BitmapButton.as . Сначала импортируйте эти классы:

1
2
3
4
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.MouseEvent;

Затем объявите это:

1
2
private var bitmapData:BitmapData;
private const THRESHOLD:Number = 0;

Вы должны убедиться, что класс BitmapButton расширяет Sprite , поскольку Bitmap само по себе не может иметь никакой интерактивности мыши. (Класс Bitmap не расширяет InteractiveObject .)

01
02
03
04
05
06
07
08
09
10
public function BitmapButton(ImageData:Class)
{
    var image:Bitmap = new ImageData();
    addChild(image);
         
    bitmapData = image.bitmapData;
     
    addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
    addEventListener(MouseEvent.CLICK, onClick);
}

В нашем конструкторе BitmapButton мы выполнили несколько важных вещей.

  • Сначала мы создаем новое Bitmap image с именем image из класса изображения, передаваемого в качестве параметра конструктору.
  • Затем мы добавляем этот Bitmap как дочерний элемент нашего Sprite .
  • Затем мы устанавливаем значение bitmapData равным bitmapData нашего изображения.
  • Наконец, мы добавляем MOUSE_MOVE событий CLICK и MOUSE_MOVE .

1
2
3
4
5
private function onMouseMove(e:MouseEvent):void
{
    var pixel:uint = bitmapData.getPixel32(mouseX, mouseY);
    useHandCursor = buttonMode = ((pixel >>>24) > THRESHOLD);
}

Наш простой выглядящий слушатель событий MOUSE_MOVE — это настоящий мозг нашего класса. Его основная цель — определить, находится ли курсор мыши над прозрачным пикселем или нет. Давайте посмотрим, как это происходит.

Первая строка получает цвет и прозрачность пикселя, над которым в данный момент находится курсор. Для этого мы используем метод getPixel32() переменной bitmapData (который, помните, является представлением растровых данных нашего изображения).

Мы должны передать координаты x и y в getPixel32() , поэтому, естественно, мы используем положение мыши.

Затем вызов возвращает uint представляющую цвет и прозрачность пикселя в указанном нами месте.

Цвета во Flash обычно обрабатываются как шестнадцатеричная uint в формате RRGGBB. Первые две цифры представляют количество красного цвета, следующие две — зеленый, а последние две — синий. Однако getPixel32() предоставляет нам специальную uint представляющую наш пиксель в формате AARRGGBB. Здесь первые две цифры представляют альфа или степень прозрачности от 00 (прозрачный) до FF (непрозрачный).

Так, например, FF980000 будет представлять полностью непрозрачный красный цвет, а 00980000 будет представлять полностью прозрачный красный цвет. Обычно вы видите их в виде 0xFF980000 или 0x0098000 : «0x» позволяет вам (и Flash!) Знать, что число в шестнадцатеричном (0-f), а не десятичном (0-9).

На данный момент у нас есть uint называемый pixel который содержит цвет и альфа пикселя под нашей мышью в формате AARRGGBB. К сожалению, это слишком много информации. Все, что нас волнует, это прозрачность этого пикселя или части АА.

Вы можете написать математическое выражение, чтобы получить этот раздел — фактически, int(pixel/Math.pow(16,6)) будет работать. Это несколько неловкое утверждение, однако, и медленнее с точки зрения производительности, чем другой вариант, который у нас есть: побитовый беззнаковый оператор правого сдвига, >>> .

Наша переменная pixel — это просто двоичное число для Flash. Обычно мы пишем его в шестнадцатеричном формате, чтобы сделать его более читабельным. Не вдаваясь в подробности, каждая цифра шестнадцатеричного числа может быть представлена ​​строкой из четырех двоичных цифр, каждая из которых либо 0, либо 1. (Таким образом, шестнадцатеричное число использует цифры 0-f, десятичное число — 0-9, и двоичный использует 0-1.)

Скажем, у нас есть шестнадцатеричное число, D4. В двоичном виде это будет представлено как 11010100. Обратите внимание, как мы используем восемь двоичных цифр для двоичного представления: в четыре раза больше, чем в шестнадцатеричном.

Имея это в виду, давайте посмотрим, что на самом деле делает оператор >>> . Давайте используем наш предыдущий пример, шестнадцатеричное число D4 или 0xD4 для ясности. Теперь давайте использовать >>> как так:

1
0xD4 >>> 4

Обратите внимание, что 4 — нормальное десятичное представление числа (в начале нет «0x»). Это выражение, по сути, сдвигает каждую двоичную цифру в D4 на четыре позиции вправо и забывает о любой цифре, которая будет выходить за конец числа.

0xD4 в двоичном виде — 11010100. Примените четыре смены, и оно станет 1101. В шестнадцатеричном виде это 0xD.

Если у вас возникли проблемы с пониманием этого, представьте двоичные цифры как блоки, расположенные справа от таблицы. Оператор >>> аналогичен перемещению блоков вправо. Вот наш оригинальный двоичный номер:

Двоичное представление D4 до применения побитового сдвига

Теперь вот наш новый номер, после того как мы сделаем 0xD4 >>> 4 :

Двоичное представление D4 после применения побитового сдвига

Заметьте, как после того, как мы сместили 0xD4 на 4 бита, мы получили всего 0xD? Это не совпадение. Как сказано выше, каждая шестнадцатеричная цифра состоит из 4 двоичных цифр — поэтому, каждый раз, когда мы сдвигаем ее вправо на 4, мы по существу выбиваем одну шестнадцатеричную цифру с конца. Вы, вероятно, можете увидеть, куда мы идем с этим!

Вернемся к нашему пикселю в формате 0xAARRGGBB. Если мы сместим его на 24, мы фактически сместимся на 6 шестнадцатеричных цифр. Это означает, что часть RRGGBB удаляется, и в итоге у нас остается только часть 0xAA, которая является нашим альфа-компонентом.

Быстрый числовой пример: скажем, наш пиксель равен FF980000. В двоичном виде это 1111 1111 1001 1000 0000 0000 0000 0000. (Каждая группа из 4 цифр представляет одну шестнадцатеричную цифру.) Когда мы сдвигаем это значение на 24, мы просто получаем 1111 1111, или FF, наши две цифры прозрачности.

Посмотрите на это снова:

1
useHandCursor = buttonMode = ((pixel >>> 24) > THRESHOLD);

Хорошо, часть (pixel >>> 24) имеет смысл сейчас, но как насчет остальных?

Это просто. Мы проверяем, больше ли наш альфа-компонент (результат pixel >>> 24 ), чем значение THRESHOLD (которое в настоящее время установлено в 0). Если это так, useHandCursor и buttonMode установлены в true, что заставит курсор поменяться на руку. Это делает наше изображение похожим на кнопку.

Если наш альфа-компонент меньше или равен THRESHOLD , курсор останется нормальным, поскольку мышь находится над (полупрозрачным) пикселем. Поскольку мы установили его на 0, только полностью прозрачные пиксели не будут включены как часть нашей кнопки, но вы можете установить, скажем, 0x80, и тогда он будет отображать курсор в виде руки для всего, что более чем наполовину прозрачно.

1
2
3
4
5
6
7
private function onClick(e:MouseEvent):void
{
    if (!useHandCursor)
    {
        e.stopImmediatePropagation();
    }
}

Последняя часть нашего класса BitmapButton — слушатель событий MouseEvent.CLICK . Эта функция будет вызываться при каждом нажатии на наше изображение, независимо от того, прозрачен этот пиксель или нет. (Изменение курсора мыши, как мы делали раньше, не повлияет на само MouseEvent .)

Итак, каждый раз, когда происходит щелчок, мы проверяем свойство useHandCursor . Если это true , это означает, что мышь находится над нормальным пикселем в нашем изображении, и нам не нужно ничего делать. Это имеет смысл — событие будет продолжено до прослушивателя событий, который мы добавили в Main.as Однако, если useHandCursor имеет значение false, мы должны что-то сделать, чтобы событие не useHandCursor к другим прослушивателям событий.

Для этого мы используем метод stopImmediatePropagation() который есть у всех объектов Event . Проще говоря, это останавливает поток событий, и слушатели событий больше его не получат. Таким образом, наша функция прослушивания событий в Main.as никогда не будет вызываться.

Предупреждение : это может иметь неприятный побочный эффект — любой глобальный слушатель события также не получит событие. Если вас это беспокоит, попробуйте добавить строку parent.dispatchEvent(e.clone()); после e.stopImmediatePropogation() . Хотя это выходит за рамки данного руководства, я рекомендую прочитать больше о системе событий здесь .


Это завершает наш учебник! Спасибо за чтение, и я надеюсь, что вы узнали что-то новое.

Следует проявлять осторожность при использовании нашего класса BitmapButton — другие MouseEvents прежнему будут работать в обычном режиме, поскольку мы имели дело только с MouseEvent.CLICK . Если вы хотите, вы можете использовать ту же технику, которую мы использовали для MouseEvent.CLICK и применить ее к другим событиям, таким как MouseEvent.MOUSE_DOWN .

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