# Урок 7. Анимация

В этом уроке ближе познакомимся с командой [AnimatedValueSetCommand](https://wfsys.gitbook.io/workflow-mobile-forms-syntax/workflow_mobile_forms/commands/animated_value_set_command), которая позволяет анимировать изменение свойства объекта. Например, можно настроить выезд панели из-за края экрана, задав начальную и конечную точки для координаты Left или Right.

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

Скачайте архив с изображениями и разархивируйте его в папку проекта **\Template\Projects\1. Template\MobileForms\Images**.



## Боковое меню <a href="#side-menu" id="side-menu"></a>

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

Удалим кнопку BackPictureBox, по которой возвращаемся на экран входа - для выхода из учетки будем использовать пункт меню _Выйти_, который создадим позже. Не забудем удалить условие и Execution, связанные с этой кнопкой.

Создадим иконку для вызова бокового меню, которую назовем MenuPictureBox:

{% code title="TemplateMainForm.xml" %}
```xml
<MyObject Name="MenuPictureBox" Type="PictureBox" Assembly="BaseControls">
  <Top>
    <Calculate>
      <Expression>Ceiling(({0} - {1}) / 2)</Expression>
      <Items>
        <Item>
          <Object Name="HeadPanel">
            <Property Name="Height" />
          </Object>
        </Item>
        <Item>
          <Object Name="MenuPictureBox">
            <Property Name="Height" />
          </Object>
        </Item>
      </Items>
    </Calculate>
  </Top>
  <Right>50</Right>
  <Height>30</Height>
  <Width>30</Width>
  <Image>
    <Switch>
      <Case>
        <When>
          <Condition Name="AppThemeDarkEqualCondition" />
        </When>
        <Then>Images\menu-light.png</Then>
      </Case>
      <Case>Images\menu-dark.png</Case>
    </Switch>
  </Image>
</MyObject>
```
{% endcode %}

Для HeadTitleLabel скорректируем координату Width с учетом MenuPictureBox. Если не исправить расчет ширины Label, то объект будет перекрывать иконку вызова меню.

Запустим приложение:

<div><figure><img src="https://936892128-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F3leufWq98b2vkRUth1Rx%2Fuploads%2FWlokyodoo5tb7z66fYVa%2F01_MenuButton_Light_iOS.png?alt=media&#x26;token=7005e69c-d0bc-46af-8c97-048a2df57bd3" alt="" width="311"><figcaption></figcaption></figure> <figure><img src="https://936892128-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F3leufWq98b2vkRUth1Rx%2Fuploads%2FItHzTsc4InhDVDx6BsRo%2F01_MenuButton_Dark_iOS.png?alt=media&#x26;token=bd2fb792-8ba3-404c-950c-46b6f3725d69" alt="" width="311"><figcaption></figcaption></figure></div>



Теперь добавим описание бокового меню, которое будет состоять из:

* панели InnerMenuPanel, содержащей описание пунктов меню;
* панели FogPanel, создающей затемнение основного контента;
* панели MenuPanel, являющейся контейнером для FogPanel и InnerMenuPanel.

{% code title="TemplateMainForm.xml" %}
```xml
<MyObject Name="MenuPanel" Type="Panel" Assembly="BaseControls">
  <Top>0</Top>
  <Left>0</Left>
  <Height>
    <Form>
      <Property Name="Height" />
    </Form>
  </Height>
  <Width>
    <Form>
      <Property Name="Width" />
    </Form>
  </Width>
  <Visible>False</Visible>

  <MyObject Name="FogPanel" Type="Panel" Assembly="BaseControls">
    <Top>0</Top>
    <Left>0</Left>
    <Height>
      <Object Name="MenuPanel">
        <Property Name="Height" />
      </Object>
    </Height>
    <Width>
      <Object Name="MenuPanel">
        <Property Name="Width" />
      </Object>
    </Width>
    <Opacity>0</Opacity>
    <BackColor>
      <Switch>
        <Case>
          <When>
            <Condition Name="AppThemeDarkEqualCondition" />
          </When>
          <Then>Black</Then>
        </Case>
        <Case>MostlyBlack</Case>
      </Switch>
    </BackColor>
  </MyObject>

  <MyObject Name="InnerMenuPanel" Type="Panel" Assembly="BaseControls">
    <Top>0</Top>
    <Left>
      <Object Name="MenuPanel">
        <Property Name="Width" />
      </Object>
    </Left>
    <Height>
      <Object Name="MenuPanel">
        <Property Name="Height" />
      </Object>
    </Height>
    <Width>
      <Calculate>
        <Expression>{0} - 56</Expression>
        <Items>
          <Item>
            <Object Name="MenuPanel">
              <Property Name="Width" />
            </Object>
          </Item>
        </Items>
      </Calculate>
    </Width>
    <BackColor>
      <Switch>
        <Case>
          <When>
            <Condition Name="AppThemeDarkEqualCondition" />
          </When>
          <Then>MostlyBlack</Then>
        </Case>
        <Case>VeryLightGray</Case>
      </Switch>
    </BackColor>
  </MyObject>
</MyObject>
```
{% endcode %}

Контейнер MenuPanel будем использовать для включения/выключения видимости всех элементов меню. По умолчанию для свойства Visible укажем значение False - элементы меню скрыты от пользователя и не перехватят фокус при тапе по экрану.

Панель FogPanel будет занимать все пространство формы, чтобы затемнять контент под меню. Для свойства Opacity укажем значение 0 - панель прозрачна на старте анимации выезжания бокового меню.

Панель InnerMenuPanel будет уже контейнера, что позволит видеть часть основного контента. Для свойства Left укажем значение равное ширине контейнера - панель находится за пределами экрана на старте анимации.

Отлично, теперь займемся настройкой анимации. И начнем с выезжания бокового меню.

## Анимация <a href="#animation" id="animation"></a>

### Показываем меню <a href="#show-side-menu" id="show-side-menu"></a>

Показывать боковое меню будем, если пользователь тапнул по иконке меню. Чтобы отловить это событие, создадим условие типа [ClickCondition](https://wfsys.gitbook.io/workflow-mobile-forms-syntax/workflow_mobile_forms/conditions/event_condition/click_condition):

{% code title="TemplateMainForm.xml" %}
```xml
<Condition Name="MenuPictureBoxClickCondition" Type="ClickCondition" Assembly="Conditions">
  <Object Name="MenuPictureBox" />
</Condition>
```
{% endcode %}

Так как панель MenuPanel используем для управления видимостью всех элементов меню, то создадим команду для изменения значения свойства Visible:

{% code title="TemplateMainForm.xml" %}
```xml
<Command Name="MenuPanelVisibleValueSetCommand" Type="ValueSetCommand" Assembly="Commands">
  <Object Name="MenuPanel">
    <Property Name="Visible">
      <Input />
    </Property>
  </Object>
</Command>
```
{% endcode %}

Теперь можем создать команду AnimatedValueSetCommand для анимации выезжания панели:

{% code title="TemplateMainForm.xml" %}
```xml
<Command Name="ShowMenuPanelAnimatedValueSetCommand" Type="AnimatedValueSetCommand" Assembly="Commands">
  <Object Name="InnerMenuPanel">
    <Property Name="Left" />
  </Object>
  <StartValue>
    <Object Name="MenuPanel">
      <Property Name="Width" />
    </Object>
  </StartValue>
  <FinishValue>
    <Formula>
      <Minus DataType="IntegerDataType">
        <Item>
          <Object Name="MenuPanel">
            <Property Name="Width" />
          </Object>
        </Item>
        <Item>
          <Object Name="InnerMenuPanel">
            <Property Name="Width" />
          </Object>
        </Item>
      </Minus>
    </Formula>
  </FinishValue>
  <Length>200</Length>
  <Easing>Linear</Easing>
</Command>
```
{% endcode %}

Так как панель меню будет выезжать в правой части экрана, то будет удобнее менять значение свойства Left. В качестве начального значения в тэге `<StartValue>` укажем значение равное ширине контейнера - панель находится за пределами экрана. А в качестве конечного значения анимации в тэге `<FinishValue>` укажем разность координат Width контейнера MenuPanel и панели InnerMenuPanel.&#x20;

В тэге `<Length>` укажем длительность анимации в миллисекундах, а в тэге `<Easing>` укажем имя режима скорости воспроизведения анимации - в нашем случае это будет воспроизведение с равномерной скоростью без скачков и ускорений.

Соберем условие и команды в Execution:

{% code title="TemplateMainForm.xml" %}
```xml
<Execution>
  <ConditionExpression>
    <Condition Name="MenuPictureBoxClickCondition" />
  </ConditionExpression>
  <Commands>
    <Command Name="MenuPanelVisibleValueSetCommand">True</Command>
    <Command Name="ShowMenuPanelAnimatedValueSetCommand" />
  </Commands>
</Execution>
```
{% endcode %}

Первым делом делаем видимым контейнер с элементами меню, а затем вызываем анимацию появления панели.

### Скрываем меню <a href="#hide-side-menu" id="hide-side-menu"></a>

Создадим вторую команду AnimatedValueSetCommand для изменения значения свойства Left у панели InnerMenuPanel, чтобы панель плавно уезжала за границы экрана. Для этого значения в тэгах  `<StartValue>` и `<FinishValue>` поменяем местами:

{% code title="TemplateMainForm.xml" %}
```xml
<Command Name="HideMenuPanelAnimatedValueSetCommand" Type="AnimatedValueSetCommand" Assembly="Commands">
  <Object Name="InnerMenuPanel">
    <Property Name="Left" />
  </Object>
  <StartValue>
    <Formula>
      <Minus DataType="IntegerDataType">
        <Item>
          <Object Name="MenuPanel">
            <Property Name="Width" />
          </Object>
        </Item>
        <Item>
          <Object Name="InnerMenuPanel">
            <Property Name="Width" />
          </Object>
        </Item>
      </Minus>
    </Formula>
  </StartValue>
  <FinishValue>
    <Object Name="MenuPanel">
      <Property Name="Width" />
    </Object>
  </FinishValue>
  <Length>200</Length>
  <Easing>Linear</Easing>
  <FinishedCommands>
    <Command Name="MenuPanelVisibleValueSetCommand">False</Command>
  </FinishedCommands>
</Command>
```
{% endcode %}

Стоит учитывать, что команды типа AnimatedValueSetCommand выполняются параллельно, а значит команды, описанные в последовательности команд после вызова команды AnimatedValueSetCommand, начнут выполнятся не дожидаясь завершения анимации.

В нашем случае, если в Execution вызывать команду MenuPanelVisibleValueSetCommand следом за вызовом команды HideMenuPanelAnimatedValueSetCommand, то элементы меню станут невидимыми еще до окончания анимации.

Поэтому команды типа AnimatedValueSetCommand обладают тэгом `<FinishedCommands>`, в котором указывается последовательность команд, выполняемая после завершения анимации.&#x20;

Для закрытия бокового меню будем отслеживать жест смахивания панели InnerMenuPanel вправо. Для этого создадим событийное условие типа [SwipeCondition](https://wfsys.gitbook.io/workflow-mobile-forms-syntax/workflow_mobile_forms/conditions/event_condition/swipe_condition):

{% code title="TemplateMainForm.xml" %}
```xml
<Condition Name="InnerMenuPanelSwipeRightCondition" Type="SwipeCondition" Assembly="Conditions">
  <Object Name="InnerMenuPanel" />
  <Direction>Right</Direction>
</Condition>
```
{% endcode %}

В тэге `<Object>` указываем имя объекта, для которого будем отслеживать событие смахивания, а в тэге `<Direction>` - направление свайпа.

Соберем условие и команду в Execution:

{% code title="TemplateMainForm.xml" %}
```xml
<Execution>
  <ConditionExpression>
    <Condition Name="InnerMenuPanelSwipeRightCondition" />
  </ConditionExpression>
  <Commands>
    <Command Name="HideMenuPanelAnimatedValueSetCommand" />
  </Commands>
</Execution>
```
{% endcode %}

### Затенение экрана <a href="#screen-shading" id="screen-shading"></a>

Для создания эффекта плавного затемнения будем менять прозрачность панели FogPanel через ее set-проперти Opacity, используя команду типа AnimatedValueSetCommand:

{% code title="TemplateMainForm.xml" %}
```xml
<Command Name="FogPanelСhangeOpacityAnimatedValueSetCommand" Type="AnimatedValueSetCommand" Assembly="Commands">
  <Object Name="FogPanel">
    <Property Name="Opacity" />
  </Object>
  <StartValue>
    <Switch>
      <Case>
        <When>
          <Input Name="Show" />
        </When>
        <Then>0</Then>
      </Case>
      <Case>0.5</Case>
    </Switch>
  </StartValue>
  <FinishValue>
    <Switch>
      <Case>
        <When>
          <Input Name="Show" />
        </When>
        <Then>0.5</Then>
      </Case>
      <Case>0</Case>
    </Switch>
  </FinishValue>
  <Length>200</Length>
  <Easing>Linear</Easing>
</Command>
```
{% endcode %}

С помощью `<Input Name="Show" />` будем определять, надо затемнять или убирать затемнение - менять местами начальное и конечное значение анимации.&#x20;

Скорректируем Execution на отображение и сворачивание бокового меню:

{% code title="TemplateMainForm.xml" %}
```xml
<Execution>
  <ConditionExpression>
    <Condition Name="MenuPictureBoxClickCondition" />
  </ConditionExpression>
  <Commands>
    <Command Name="MenuPanelVisibleValueSetCommand">True</Command>
    <Command Name="FogPanelСhangeOpacityAnimatedValueSetCommand">
      <Input Name="Show">True</Input>
    </Command>
    <Command Name="ShowMenuPanelAnimatedValueSetCommand" />
  </Commands>
</Execution>

<Execution>
  <ConditionExpression>
    <Condition Name="InnerMenuPanelSwipeRightCondition" />
  </ConditionExpression>
  <Commands>
    <Command Name="FogPanelСhangeOpacityAnimatedValueSetCommand">
      <Input Name="Show">False</Input>
    </Command>
    <Command Name="HideMenuPanelAnimatedValueSetCommand" />
  </Commands>
</Execution>
```
{% endcode %}

Запустим приложение, чтобы проверить отображение бокового меню и затемнение фона.

<div><figure><img src="https://936892128-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F3leufWq98b2vkRUth1Rx%2Fuploads%2Fe4MY3nMitn5y6ChIMJ8g%2F02_FogPanel_Light_iOS.png?alt=media&#x26;token=57786f72-f806-401e-9681-18108e80e07b" alt="" width="311"><figcaption></figcaption></figure> <figure><img src="https://936892128-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F3leufWq98b2vkRUth1Rx%2Fuploads%2F190Vdo1pFEe1ORXIr1fC%2F02_FogPanel_Dark_iOS.png?alt=media&#x26;token=4aa11dc5-2798-4bb8-a988-81583acdc849" alt="" width="311"><figcaption></figcaption></figure></div>

Отлично! Теперь можем добавить элементы бокового меню.

## Пункты меню <a href="#menu-items" id="menu-items"></a>





<div><figure><img src="https://936892128-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F3leufWq98b2vkRUth1Rx%2Fuploads%2Fp9SG4zkQnCyGKEOKIrqi%2F03_Profile_Light_iOS.png?alt=media&#x26;token=a38523ab-ae55-4d38-ba00-70c8512a09ee" alt="" width="311"><figcaption></figcaption></figure> <figure><img src="https://936892128-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F3leufWq98b2vkRUth1Rx%2Fuploads%2F6O5ZymtAvb4tIEzXyl1N%2F03_Profile_Dark_iOS.png?alt=media&#x26;token=92471a56-c7e1-453c-984d-316e147e4ecb" alt="" width="311"><figcaption></figcaption></figure></div>



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

<div><figure><img src="https://936892128-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F3leufWq98b2vkRUth1Rx%2Fuploads%2F2gLQVS77emaGFCv2nyjp%2F04_Full_Menu_Light_iOS.png?alt=media&#x26;token=8a21cb4d-9545-476a-a5a3-f11972c92378" alt="" width="311"><figcaption></figcaption></figure> <figure><img src="https://936892128-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F3leufWq98b2vkRUth1Rx%2Fuploads%2FwQlkCW9V2UlcU0NqqZ5R%2F04_Full_Menu_Dark_iOS.png?alt=media&#x26;token=ebd8e020-6633-4faa-b74d-07f2abd2018c" alt="" width="311"><figcaption></figcaption></figure></div>



### Доп Анимация

{% code title="TemplateMainForm.xml" %}
```xml
<Execution>
  <ConditionExpression>
    <!--<Or>-->
    <Condition Name="MenuPictureBoxClickCondition" />
    <!--<Condition Name="OrderCollectionPanelSwipeLeftCondition" />-->
    <!--</Or>-->
  </ConditionExpression>
  <Commands>
    <Command Name="MenuPanelVisibleValueSetCommand">True</Command>
    <Command Name="FogPanelСhangeOpacityAnimatedValueSetCommand">
      <Input Name="Show">True</Input>
    </Command>
    <Command Name="ShowMenuPanelAnimatedValueSetCommand" />
  </Commands>
</Execution>

<Execution>
  <ConditionExpression>
    <!--<Or>-->
    <!--<Condition Name="MenuFogPanelClickCondition" />-->
    <Condition Name="InnerMenuPanelSwipeRightCondition" />
    <!--</Or>-->
  </ConditionExpression>
  <Commands>
    <Command Name="FogPanelСhangeOpacityAnimatedValueSetCommand">
      <Input Name="Show">False</Input>
    </Command>
    <Command Name="HideMenuPanelAnimatedValueSetCommand" />
  </Commands>
</Execution>
```
{% endcode %}





## Итоги <a href="#results" id="results"></a>

На уроке мы рассмотрели процесс аутентификации пользователя, .

## Ответы <a href="#answer" id="answer"></a>

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

{% file src="broken-reference" %}

{% hint style="warning" %}
Архив _не содержит_ xml-файлы форм десктопного приложения.
{% endhint %}
