4-компонентная отзывчивая анимация Angular

Я работаю над отзывчивым приложением Angular, в котором на мобильном телефоне есть ящик.

Мне нравится реализация API веб-анимации в Angular, но я не могу найти место, где я могу настроить анимацию на основе моих точек останова медиа-запросов.

Все, что я могу найти, это отменить анимацию через мой лист css, но это заставляет меня начать распространять код в разных местах моего проекта, и я не уверен, что это то, что мне предназначено для angular...

Пример из жизни

Моя панель приложений использует этот код для анимации

<div class="mobile-menu" [@animateDrawer]="drawerOpened">
  <!-- Drawer's menu goes here -->
</div>

drawerOpened — это логическое значение, которое переключается при нажатии кнопки меню приложения.

Мой компонент выглядит так:

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.scss'],
  animations: [
    trigger('animateDrawer', [
      state('inactive', style({
        transform: 'translate3d(-100%, 0, 0)'
      })),
      state('active', style({
        transform: 'translate3d(0, 0, 0)'
      }))
    ])
  ]
})

CSS-код, в котором я отменяю эффекты анимации.

.mobile-menu {
  opacity: 1 !important;
  display: flex !important;
  transform: none !important;
}

Помимо манипулирования всем в моем коде CSS и отключения свойств CSS в Desktop BreakPoint, существует ли какой-то способ сделать это в контексте Angular?

Благодарю вас!


person neoswf    schedule 13.09.2017    source источник


Ответы (3)


Невозможно определить поведение JavaScript на основе запросов CSS @Media. Вам придется сделать то же, что и я, где я создал службу внешнего вида Angular, которая отслеживает размеры окна просмотра и отправляет команды компонентам на основе того, что обнаружено. Затем вы можете управлять анимацией и внешним видом с помощью логики JavaScript вместо логики CSS.

Добавление примера:

Создайте сервис, который отслеживает ширину области просмотра. Если область просмотра меньше, скажем, 500 пикселей, служба выводит мобильное приложение, в противном случае — настольное.

В своем компоненте объявите один триггер, назовем его @slide, с несколькими состояниями. Мы также установим закрытую переменную с именем stateOfYourChoosing, и вы можете установить ее в виде строки.

Затем в вашем шаблоне

<div [@slide]="stateOfYourChoosing"></div>

Затем вы можете выбрать, какое состояние вы хотите, чтобы состояние stateOfYourChoosing было по умолчанию, и как вы хотите, чтобы ваш компонент переходил на основе логики компонента.

Таким образом, если ваша служба внешнего вида выводит рабочий стол, вашим состоянием stateOfYourChoosing по умолчанию будет slideOut.

transition('slideOut => slideIn', animate('100ms ease-in'))

Если служба внешнего вида выводит мобильный, она устанавливает stateOfYourChoosing в mobileSlideOut, а вместо этого используется код перехода

transition('mobileSlideOut => mobileSlideIn', animate('100ms ease-in'))

Затем вы можете управлять всеми своими адаптивными анимациями в модуле анимации, контролируя, в каком состоянии находится ваш триггер.

person Aaron Martin-Colby    schedule 18.09.2017
comment
Можете ли вы привести пример того, как вы настраиваете объект анимации компонента с помощью этого сервиса? - person neoswf; 18.09.2017
comment
@neoswf Конечно. Смотрите мой расширенный ответ. - person Aaron Martin-Colby; 18.09.2017
comment
Прежде всего - спасибо! Мне понравилось ваше решение, но я должен сказать, что установка слушателя OnResize в стек — не лучшее, что я хотел бы сделать со своим приложением. Уже насрать на прокрутку слушаю. Не нравится, но кажется, что у меня не будет слишком много вариантов здесь.... - person neoswf; 19.09.2017
comment
Боже мой, я был пьян, когда писал это? Он заполнен опечатками. В любом случае, это абсолютно не элегантно, но мощно. Это очень расширяемое решение, которое позволяет вашей реакции быть намного более сложной и функциональной, чем позволяют старые контрольные точки CSS. Очевидно, что компромиссом является значительное увеличение сложности. Я все еще обсуждаю использование этой идеи. - person Aaron Martin-Colby; 19.09.2017
comment
@neoswf В моем случае на моем мониторе с изменением размера есть высокий уровень защиты от дребезга. Раньше я использовал полсекунды, но теперь я использую целую секунду и имею небольшой элемент, который появляется в правом верхнем углу экрана, который говорит об изменении размера после того, как пользователь изменит размер экрана, чтобы предоставить пользователю некоторую информацию о том, что происходит. - person Aaron Martin-Colby; 19.09.2017

Один из способов — создать анимацию во время выполнения, а не объявлять ее в декораторе компонентов.

  1. Создайте службу, которая будет использоваться компонентами.
  2. Создайте новый метод, который получил конфигурацию анимации.
  3. Определить размер окна.
  4. Создайте и верните анимацию.

Это будет выглядеть примерно так (следующий код не тестировался и является просто примером):

export interface MediaQueryStyle
{
    minWidth: number;
    initialStyle: any; // Style object to apply before animation
    endStyle: any;     // Style object to apply after animation
}

export interface MediaQueryAnimationConfig
{
    element: ElementRef;
    mediaStyles: MediaQueryStyle[];
    duration?: number | string;

    ... // Whatever you need to create your animation 
}

Сервис:

@Injectable({
    providedIn: 'root'
})
export class MediaQueryAnimationService
{
    constructor(private builder: AnimationBuilder) { }

    public create(config: MediaQueryAnimationConfig): AnimationPlayer
    {
        // Read animation configuration
        const duration = config.duration || 1000;
        const mediaStyle = this.findMediaStyle(config.styles);

        // Build the animation logic (add here any other operation, e.g. query)
        const animation = this.builder.build([
            style(mediaStyle.initialStyle),
            animate(duration, style(mediaStyle.endStyle)
        ]);

        return animation.create(config.element);
    }

    private findMediaStyle(styles: MediaQueryStyle[])
    {
        const viewWidth = window.innerWidth;

        // Some logic to scan the array and return the style that complies with `viewWidth`
        return styles.find(style => ...);
    }
}

В вашем компоненте:

<something ... #someElement></something>
@Component({
    selector: 'something',
    templateUrl: './something.component.html',
    styleUrls: ['./something.component.scss']
})
export class SomethingComponent implements OnInit
{
    @ViewChild('someElement')
    private elementRef: ElementRef;

    constructor(private mqAnimation: MediaQueryAnimationService) { }

    ngOnInit() { ... }

    public onSomeEvent()
    {
        const player = mqAnimation.create({ element: elementRef.element, minWidth: ... });

        player.onDone(player.destroy);

        player.play();
    }
}

Полезное чтение и примеры:

https://angular.io/api/animations/AnimationBuilder

https://stackblitz.com/edit/angular-animation-builder

Анимация внутри MatDialog не работает

Удачи ✌

person Shy Agam    schedule 08.04.2019

Не самые надежные решения, но если конечное состояние анимации одинаково для каждого медиа-запроса (например, вы анимируете оба элемента для просмотра, но хотите разные анимации для мобильных и настольных компьютеров), вы можете определить начальные состояния в css используя медиа-запросы, а в компоненте angular используйте «*» для начального состояния целевых свойств css

trigger('animation', [
  state('out, none, void', style({
    transform: '*'
  })),
  state('in', style({
    transform: 'translate3d(0, 0, 0)'
  })),
  transition('below => in, none => in', animate(`300ms ease`))
])

затем в css

.class {
  //animate from bottom on mobile
  transform: translate3d(0, -100%, 0);

   @media all and (min-width: 920px) {
     //animate from left on desktop
     transform: translate3d(-100%, 0, 0)
   }
}
person Elvis Wohlken    schedule 18.05.2018