Компонент "Горячая область"

Источник: delphikingdom
Петр Смирнов

Давным давно, в языке HTML появилась возможность задавать карты "горячих областей". Это, как правило, рисунки, на которых отмечались некоторые зоны (те самые "горячие области"), нажатие на которые приводило к нужному эффекту. Область применения подобных вещей весьма широка. Я видел, например, интерактивные карты, написанные на HTML без применения всевозможных "вебдванольных" примочек, где щелчок по кнопке, обозначающей город, приводил к открытию страницы с описанием выбранного населенного пункта. На мой взгляд, это весьма и весьма удобно и наглядно. Точно также, я видел игру, написанную также на базе этого метода - клик по нужной дороге на перекрестке приводил к перемещению по этой самой дороге в нужном направлении, клик на лодке приводил к выполнению команды "сесть в лодку" и так далее. Квест в чистом виде, только очень ограниченный, так как все было основано на статических HTML картинках, даже без примеси JavaScript кода, но все это выглядело очень и очень неплохо. Фантазии на что-то более серьезное не хватает, поэтому не обессудьте за неказистый пример:

Интерактивная карта мира
Рисунок 1. Кликните по материку, или океану, и узнаете его название

Хотелось бы иметь на Delphi что-то подобное? Наверное, было бы такое сделать неплохо. Разумеется, ложить на форму WebBrowser было бы чересчур для такой простой задачи. Я предлагаю альтернативное решение - использовать предлагаемый компонент. При создании, он был назван TSkinPanel и менять это название я пока не собираюсь. Итак, что же это за зверь такой и что он предлагает Вашему вниманию.

Первое применение: автозапуск для дисков
Рисунок 2. Первый пример использования компонента - автозапуск для дисков.
Мы имеем четыре фильма, при наведении мыши на картинку с фильмом, он "подцвечивается" и выводится подсказка в заголовке формы. При нажатии кнопки мыши, картинка "вдавливается", затем выдается сообщение об имени фильма и катринка запрещается. На рисунке Вы видите неактивные картинки для фильмов "Step brothers", "Моя ужасная няня", подсвеченную картинку (на нее наведен курсор) для фильма "Заложница" и запрещенную картинку (по ней уже был произведен клик) для фильма "Mamma mia". Программа содержит всего лишь две процедуры, состоящие из большого case'а!

Особнности:

  • Нарисовав пять картинок в любом графическом редакторе, том же Photoshop, Вы получаете возможность показать своему пользователю высококачественный пользовательский интерфейс: "подсвечивающиеся" и "нажимающиеся" псевдо-кнопки любой, самой изощренной конфигурации, с глубиной цвета до 1, 4, 8, 32 бит.
  • Как правило, основные трудозатраты требуются при создании первой картинки, остальные ("затемненная", "засеренная", "опущенная" и маска) получаются из первой элементарными действиями.
  • Вы можете хранить картинки внутри программы (переносимость, но большой размер исполняемого файла), или хранить и загружать их отдельно (больше кода).
  • Двойная буферизация уменьшает мерцания при быстром движении мыши.
  • Поддерживаются те же типы картинок, что и у TImage (то есть вы можете использовать, например GIF установив библиотеку GraphicEx.
  • Вы можете использовать в качестве маски картинку любого формата, но компонент работает нормально только если используемый формат без потери качества (BMP, TIF, GIF). При использовании JPG Вы можете столкнуться с разнообразными проблемами. Маска должна рисоваться вручную в формате BMP и храниться только в этом формате.
  • Компонент проверен на работоспособность в Delphi 6.0, 7.0
  • Компонент работает на Windows 98/2000/XP.
  • Компонент полностью бесплатен для любого использования.
  • С удовольствием выслушаю Ваши пожелания относительно расширения функциональности компонента.
  • То, о чем я мечтал, начиная разрабатывать компонент, так и не реализовано. Чуть-чуть похожее изображено на рисунке 2, но это не то. Смысл разработки был в том, чтобы компонент рисовал псевдо-трехмерную картинку и чтобы "карта", на которую наведена мышь, "всплывала" на передний план, это называется X-Window mode и под Windows есть программы, эмулирующие такое поведение. Но оконные компоненты использовать по вполне понятным причинам не хочется - это лишний ресурс, а писать свой микро-Windows у меня желания не было, тем более, что пришлось бы изобретать несколько велосипедов. Со временем, думаю, я реализую то, что мне хочется... а пока оцените то, что получилось.

Демонстрация показывает, как я сам предлагаю использовать его. Плохое качество использованных картинок обусловлено желанием сделать небольшой размер исполняемого файла (плюс к тому я плохой дизайнер), компонент успешно работает с картинками с глубиной цвета 1, 4, 8 и 32 бита. Остальные глубины цветов мне были не нужны, но если будет необходимо - могу поднатужиться и приделать. При создании компонента не были использованы сторонние компоненты, а также чужой исходный код.


Рисунок 3. Второе применение компонента: кнопки сколь угодно сложной формы.
На рисунке 4 Вы можете увидеть, какие же картинки были использованы для такого эффекта.

Маска компонента  Подцвеченное изображение  Обычное, нетронутое изображение  Неактивное изображение  Нажатое изображение 

Рисунок 4. Как создать чудо: картинки, использованные в программе с рисунка 3.
Слева направо: Маска компонента, подцвеченное изображение, обычное (нетронутое) изображение, неактивное изображение, изображение под нажато кнопкой мыши.
В приведенном примере маска и подцвеченное изображение совпадают, но это просто видимость, на самом деле, маска - это BMP файл с глубиной цвета 8 бит (256 цветов), а подцвеченная картинка - JPG с глубиной цвета 32 бита.

Свойства компонента:

  • property PictureMask:TPicture - маска. Это единственная картинка, без которой компонент совершенно бесполезен. Это единственная картинка, которая должна быть в формате BMP обязательно. Это происходит оттого, что именно маска задает формы "горячих" областей компонента, в отличии от, скажем, HTML тега AREA, которому канфигурацию областей задают посредством HTML кода. Картинки формата JPG не могут гарантировать 100% сохранности цвета, поэтому используйте только формат BMP. Пиксели каждого цвета определяют свою активную область. Каждая область должна иметь свой уникальный цвет, чтобы компонент работал правильно. Посмотрите пример на рисунке 4, или скачайте демонстрационную программу с исходниками.
  • property PicturePassive:TPicture - обычная картинка. Отображается в тех областях компонента, где на маске находятся разрешенные цвета (ниже) и где нет курсора мыши.
  • property PictureActive:TPicture - подцвеченная картинка. Отображается на месте той "горячей" области маски, где находится мышь. Если мышь находится на цвете, объявленном прозрачным (ниже), то компонент рисует обычную картинку.
  • property PictureDisabled:TPicture - запрещенная картинка. Если компонент запрещен целиком (Enabled=false), он рисует целиком эту картинку (иначе - PicturePassive). Эта картинка также рисуется на месте запрещенных (см. ниже) цветов (иначе область запрещенных цветов рисуется с использованием PicturePassive).
  • property PicturePressed:TPicture - нажатая картинка. Эта картинка рисуется на месте области маски, где находится нажатая кнопка мыши. При перемещении курсора мыши эта область также смещается.
  • property ActiveColor:TColor - цвет пиксела маски, на котором сейчас расположен курсор мыши. Вы будете использовать это свойство в обработчиках OnClick, OnEnter. В обработчике OnLeave он будет иметь значение Transparent, то есть использовать можно, но смысла это не имеет.
  • property EnabledColor[Color:TColor]:boolean - разрешенные цвета. По умолчанию, все цвета разрешены, но Вы можете запретить часть из них (а потом повторно их разрешить). При этом те области маски, что имеют цвета из списка запрещенных, будут отображаться особым образом (см. PictureDisabled), наведение курсора мыши на них не будет сопровождаться событием OnEnter, а при событии OnClick значение ActiveColor будет равно Transparent.
  • property Transparent:TColor - прозрачный цвет. Перевод мыши в область маски, имеющую цвет Transparent будет сопровождаться событием OnLeave.
  • property OnClick:TNotifyEvent - клик мыши по компоненту. Вам следует проверить, что цвет ActiveColor<>Transparent, в противном случае Вам, скорее всего, нечего обрабатывать. Я специально не стал отключать срабатывание OnClick - Вам может по каким-либо причинам обработать щелчок по неактивной области компонента, например, для того, чтобы недовольно "пискнуть". Самостоятельно предпринимать подобные шаги я не буду.
  • property OnEnter:TNotifyEvent - срабатывает при входе мыши в область маски с цветом, отличным от Transparent. Вы будете использовать этот обработчик для того, чтобы вывести всплывающую подсказку в зависимости от ActiveColor. При срабатывании этого события, Вы можете быть уверены, что мышь находится на действительном элементе, который не запрещен, и маска в этом месте не имеет цвет Transparent.
  • property OnLeave:TNotifyEvent - срабатывает, когда мышь покидает компонент, или перемещается в область маски с цветом Transparent. Вы будете использовать этот обработчик для того, чтобы убрать всплывающую подсказку.

Как работать с компонентом

  • Для работы с компонентом, Вам потребуется создать пять картинок. В принципе, достаточно и трех, но для полного эффекта, требуется именно пять. Поясню. Вы можете задать только картинку маски (без нее компонент просто теряет смысл) - тогда (если остальные картинки не заданы) компонент будет рисоваться, как если бы все картинки были одинаковыми и были бы равны PictureMask. Если Вы зададите еще картинку PicturePassive, или PictureActive, но не зададите парную ей картинку, парная картинка станет равной заданной. Если Вы зададите PictureActive и/или PicturePassive, но не зададите PictureMask, то компонент будет отображать только PicturePassive и не будет нормально функционировать. Остальные картинки украшают интерфейс, но на нормальную работу компонента их наличие или отсутствие не сказывается.
  • В первую очередь, Вы должны нарисовать активную, "подсвеченную" картинку. Именно с нее я рекомендую начать разработку программы с использованием моего компонента. Не принципиально, чем Вы ее нарисуете. Я пользовался PhotoShop, но Вы можете использовать тот инструмент, к которому привыкли. При разработке демонстрации с рисунка 2 я просто взял четыре картинки от произвольно выбранных фильмов, положил их на единый холст средствами PhotoShop и немного поигрался с прозрачностью слоев, после чего сохранил рисунок как JPG под именем CDActive.jpg. Оригинальную картинку (со слоями) я оставил, она мне будет нужна в дальнейшем. Не забудьте отменить изменения, которые Вы сделали, чтобы получить CDPressed.jpg. Я для этого сохранил картинку до обработки, а потом просто переоткрыл ее.
  • Далее я создал картинку, соответствующую нажатому состоянию псевдокнопок. Для этого я взял оригинальную картинку в PhotoShop (со слоями) и чуточку уменьшил каждый рисунок (в PhotoShop это делается на раз-два :-)). Полученный рисунок я опять сохранил как JPG, но уже под именем CDPressed.jpg. Картинку со слоями теперь можно удалить, а дальнейшую работу производить с CDActive.jpg, но я все-таки продолжил работу с этой "родной" для PhotoShop картинкой.
  • Теперь у меня имеется картинка, из которой я получал CDActive.jpg. Я уменьшил контраст на -75 и сохранил картинку как CDPassive.jpg, а затем вернулся к исходной картинке (закрыв и открыв файл повторно).
  • Теперь осталось только сделать запрещенное изображение. Я перевел изображение в Gray и сохранил его под именем CDDisabled.jpg.
  • Осталось самое нудное. Это создание маски. Для этого я выделил полигональным выделителем полигональные области вокруг картинок. Места, где картинки перекрываются, я просто поделил между картинками пополам - не особенно красиво, но больше идей у меня не было. Выделенные области я залил произвольно выбранными уникальными цветами из палитры PhotoShop.
  • Теперь все просто. Области, которые меня интересуют, залиты несколькими уникальными цветами. Остальное нужно залить белым цветом, который я задам как "прозрачный". Для этого я "волшебной палочкой" выделил все области, инвертировал выделение и залил новое выделение белым цветом. Получилось то, что Вы можете посмотреть в примере (ссылка в конце статьи). Картинку я сохранил как 8 битный BMP в файл CDMask.bmp.
  • Слоистую картинку из PhotoShop теперь точно можно удалить, а сам PhotoShop закрыть. Пришла пора Delphi. Я думаю, комопонент Вы уже установили, а если нет, то прочитаете справку по установке компонентов самостоятельно :-). Создайте новый проект и бросьте на форму компонент TSkinPanel со вкладки Samples. Теперь задайте картинки: сперва PictureMask установите в CDMask.bmp, затем я устанавливал PictureActive в CDActive.jpg, PicturePassive в CDPassive.jpg, PictureDisabled в CDDisabled.jpg и PicturePressed в CDPressed.jpg.
  • Последним шагом стало выставить Align в alClient, чтобы компонент расползся на всю форму и стал масштабироваться вместе с ней, а также установку свойства Transparent в clWhite (мы ведь белым цветом залили фон маски, не так ли?). Компонент готов... но не полностью! Мы забыли прописать для него хоть какой-нибудь исполняемый код. Давайте рассмотрим, как же мы можем работать с компонентом.
  • Пусть все будет просто. Пока при движении мыши мы будем выводить подсказку о цвете. Хорошим местом был бы штатный Hint компонента, и он работает, но мне захотелось экзотики и я предлагаю вывести цвет в заголовок. Создайте обработчик OnEnter для компонента и пропишите в нем примерно такой код:

    with TSkinPanel(Sender) do Caption:=inttohex(ActiveColor,4);
    

  • Что мы сделали? Теперь каждый раз при входе в зону маски, мы будем выводить шестнадцатеричный код цвета в заголовок. Очень просто, но не совсем красиво: когда мы уведем мышь на "прозрачный" цвет, или когда уведем ее с компонента вовсе, заголовок останется старым. Что делать? Назначить обработчик события OnLeave, прописав в нем простейший код:

    Caption:='';
    

  • Как же теперь работает программы? При наведении на активную кнопку, в заголовке отображается номер цвета в шестнадцатеричном виде, а сама картинка подцвечивается, а при щелчке - заданным образом выделяется. Но ничего особенного при щелчке не происходит, а также выводимый в заголовок цвет маски не несет для пользователя никакой информации. Но ничего страшного - мы можем сравнить цвет, выводимый в заголовок и смысл, который должна нести картинка и исправить выводимый заголовок в соответствии не с абстрактным кодом, а со смыслом, который должна нести картинка. Попробуем модифицировать код. Если Вы используете мои картинки, а не самодельные, то код в обработчике OnEnter получится примерно таким:

    case TSkinPanel(Sender).ActiveColor of
      $FF0000:Caption:='Сводные братья';
      $F5FF:Caption:='Заложница';
      $B234:Caption:='Mamma mia';
      0:Caption:='Моя ужасная няня';
      else Caption:=inttohex(TSkinPanel(Sender).ActiveColor,4);
    end;
    

  • Ну как? Повеселело? Теперь в заголовке выводится не нечто непонятное пользователю, а название фильма, на который наведена мышка. Но по нажатию кнопки мыши по-прежнему ничего полезного не происходит. Придется заняться и этим. Пусть пока мы просто выведем текстовое сообщение. А для демонстрации того, как работает запрет цвета, запретим нажатие только что нажатой кнопки. После нескольких минут работы, должен получиться примерно следующий код:

    with TSkinPanel(Sender) do if ActiveColor<>Transparent then begin
      EnabledColor[ActiveColor]:=false; // запретить редактирование
      case ActiveColor of
        $FF0000:ShowMessage('Вы посмотрели фильм "Сводные братья"');
        $F5FF:ShowMessage('Вы посмотрели фильм "Заложница"');
        $B234:ShowMessage('Вы посмотрели фильм "Mamma mia"');
        0:ShowMessage('Вы посмотрели фильм "Моя ужасная няня"');
        else ShowMessage('Вы посмотрели неизвестно знает что!');
      end;
    

  • Ну что, красиво? Вроде бы, неплохо, тем более, для столь небольшого времени. Теперь Вы можете легко и просто добавить более сложный обработчик - выводить подробное описание в TMemo, или при нажатии кнопки запускать на просмотр реальный фильм. Простора для фантазии много. Тем более, если Вы умеете работать в Photoshop, в котором можно творить чудеса.

Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=21420