Давно не делал демок для журнала. Сегодня решил исправить этот недостаток и сел за Delphi, чтобы продолжить цикл сообщений о GUI. В поисках интересных интерфейсных решений, набрел сюда, но сделать такое за короткое время не удалось. Видимо подзабыл я математику и нужно садиться за листочек бумажки, чтобы накидать основные формулы для такой карусельки. Надеюсь, как-нибудь и такой PageSlider добавится в мою коллекцию GUI-элементов. А сегодня я остановился на анимированной подсветке элементов горизонтального списка. В итоге я скачал несколько иконок и соорудил небольшую демку. В принципе, такой подход может использоваться как для элементов меню, так и для подсветки пунктов в списке апгредов юнита, выбора магии или чего-то подобного. | ![]() |
Так как вся ставка идет на анимации, поэтому записал видео:
Времени было мало, поэтому обкручивать все элементы в отдельные классы не стал и оставил маленький винегрет в исходниках. Если будет свободная минутка - обязательно постараюсь "причесать" код.
Графических элементов на экране не так много - простенький градиентный фон, иконки, полоска-заголовок снизу и текстовые надписи повсюду. Основным являются анимация, переходы между состояниями и реакция на мышь. Так как уже мы делали что-то подобное, то сейчас я коснусь только основных и новых моментов демы.
Кнопкой-иконкой у нас является такая конструкция:
TItem = class fMainHud: TGlHudSprite; fTextHud: TGlHudText; fBottomText: String; fPosition: TVector; fY: Single; fSelected: Boolean; fWidthArea: Single; end;
где fMainHud - основное изображение элемента, fTextHud - текст, прячущийся под иконкой, fBottomText - текст, который выезжает слева, fPosition - базовая позиция элемента, fY - single-значение для реализации tween'инга, fSelected - наведена ли мышь?, fWidthArea - можно использовать, если визуальная часть иконки не совпадает с областью реакции на мышь.
Сама структура такого элемента заполняется здесь:
Function TfrmMain.AddItem(aMaterial: String; aText: String; aPosX, aPosY, aWidthArea: Single; aBottomText: String): TItem; begin result := TItem.Create; with result do begin fPosition := VectorMake(80 + aPosX * 70, aPosY, 0); fTextHud := TGlHudText.CreateAsChild(fMainDummy); with fTextHud do begin BitmapFont := fWinFont; ModulateColor.SetColor(0, 0, 0.3, 0.7); Alignment := taCenter; Text := aText; Position.SetPoint(fPosition[0], fPosition[1] - 10, 0); end; fMainHud := TGlHudSprite.CreateAsChild(fMainDummy); with fMainHud do begin Material.MaterialLibrary := fMatLib; Material.LibMaterialName := aMaterial; with Material.GetActualPrimaryTexture.Image do SetSize(Width, Height); Position.SetPoint(fPosition); end; fBottomText := aBottomText; fWidthArea := aWidthArea; fY := aPosY; end; fMenuItems.Add(result); end;
"Магические числа" нужны для более опрятного вида списка на экране. Поэкспериментируйте, изменив те или иные значения при заполнении элемента списка!
Остальные вспомогательные методы вроде InitTweener(), InitMaterials(), InitFontAndSprites() мы уже разбирали ранее и теперь я просто перейду к описанию основного динамического метода UpdateSelection():
Function TfrmMain.UpdateSelection(x, y: Integer): integer; var i: integer; begin result := -1; for i := 0 to fMenuItems.Count - 1 do with GetItem(i), GetItem(i).fMainHud do if (x > Position.X - Width/2 * fWidthArea) and (x < Position.X + Width/2 * fWidthArea) and(y > Position.Y - fPosition[1] - Height) and (y < fPosition[1] + Height)then begin result := i; if not fSelected then begin fTweener.DeletePSingle(@fY); fTweener.AddTweenPSingle(@fY, ts_ExpoEaseIn, fY, fPosition[1] + 45, 1, 0); end; fSelected := true; end else begin if fSelected then begin fTweener.DeletePSingle(@fY); fTweener.AddTweenPSingle(@fY, ts_ElasticEaseOut, fY, fPosition[1], 2.5, 0); end; fSelected := false; end; end;
Очевидно, что здесь мы проходим по списку элементов и проверяем, наведена ли мышь на один из них. Здесь стоит пояснить новый вызов fTweener.DeletePSingle(@fY), который удаляет все Tween'ы, связанные с адресом переменной. Это необходимо, чтобы прежние анимации не накладывались на новые, что вызывает порой жуткие скачки при движениях элементов. А далее, как обычно:
- если объект выделен, а на предыдущем кадре нет, тогда запускаем ts_ExpoEaseIn
- если объект был выделен, а теперь мышь ушла, тогда запускаем ts_ElasticEaseOut в обратном направлении
При клике левой кнопки мыши, запускаются два tween'а на движение панельки и текста внизу:
if IsKeyDown(vk_LButton) and not MousePressed and (Selected <> -1)then begin fMenuText.Text := GetItem(Selected).fTextHud.Text; fBottomText.Text := GetItem(Selected).fBottomText; fTweener.DeletePSingle(@fTextBarX); fTweener.AddTweenPSingle(@fTextBarX, ts_ExpoEaseIn, -500, 250, 1.5, 0); fTweener.DeletePSingle(@fBottomTextX); fTweener.AddTweenPSingle(@fBottomTextX, ts_ExpoEaseIn, -400, 80, 1.5, 0.4); end;
А сами позиции применяются простым присвоением:
fTextBar.Position.X := fTextBarX; fMenuText.Position.X := 530 - fTextBarX; fBottomText.Position.X := fBottomTextX;
Можно заметить, что панелька fTextBar и сам текст fMenuText движутся благодаря одному и тому же tween-элементу, но выползают они с разных сторон окна, что придает их движению разнообразия.
Наверно это все, что можно пояснить в тексте... какие-то отдельные моменты наверно все же придется смотреть в коде, чтобы понять более детально устройство самой демы...
Само демонстрационное приложение можно скачать здесь, либо воспользуйтесь svn для выкачивания сразу всех примеров, написанных мною. Данная демка добавилась в 9ой ревизии.
Само демонстрационное приложение можно скачать здесь, либо воспользуйтесь svn для выкачивания сразу всех примеров, написанных мною. Данная демка добавилась в 9ой ревизии.
p.s. Последний месяц для fun'а я занимался только конкурсом, поэтому журнал не наполнялся gui-вкусностями и различными демками. Через месяц-другой, как станет посвободнее, обязательно вернусь к славной традиции добавления новых интерфейсных элементов на страницы журнала. Ну а пока в следующих записях буду рассказывать о текущих проектах, сложностях в познании flash, результатах конкурса, создании своего сайта и прочего... Следите за новостями!