Ответ 1
Я изменил PanelComponent
в вашем образце StackBlitz, чтобы он работал так, как показывает предоставленная анимация.
Вам нужно всего три состояния. Один, когда компонент изначально снаружи справа. Оттуда это становится очевидным, это второе состояние. После этого он выходит из поля зрения третьего состояния. Как только он окажется вне поля зрения слева, вы вернете его в правильное исходное состояние, чтобы он мог вернуться, когда это необходимо.
Вот код для измененного компонента панели:
import { Component, ContentChild, QueryList,HostBinding,Input,ElementRef } from '@angular/core';
import {
trigger,
state,
style,
animate,
transition
} from '@angular/animations';
@Component({
selector: 'my-panel',
templateUrl: './panel.component.html',
styleUrls:['./panel.component.css'],
animations: [
trigger('transition', [
state('right', style({
transform: 'translateX(100%)',
opacity: 0
})),
state('inview', style({
})),
state('left', style({
transform: 'translateX(-100%)',
opacity: 0
})),
transition('right => inview', [
animate('${PanelComponent.ANIMATION_DURATION}ms 0ms ease-out',style({
transform: 'translateX(0)',
opacity: 1 }))
]),
transition('inview => left', [
animate('${PanelComponent.ANIMATION_DURATION}ms 0ms ease-in',style({
transform: 'translateX(-100%)',
opacity: 0 }))
])
])]
})
export class PanelComponent {
public static readonly ANIMATION_DURATION = 500;
@Input() destroyOnHidden: boolean = false;
@Input() id : string = null;
@ContentChild('layout') contentChild : QueryList<ElementRef>;
@HostBinding('style.width') componentWidth = null;
@HostBinding('style.height') componentHeight = null;
@HostBinding('style.overflow') componentOverflow = null;
public state: string = null;
public getId() {
return this.id;
}
constructor() {
this.state = 'right';
}
public setInViewStyle(width: string, height: string, overflow: string): void {
this.componentWidth = width + '%';
this.componentHeight = height + '%';
this.componentOverflow = overflow;
this.state = 'inview';
}
public setDefault(): void {
this.state = 'right';
}
public moveOut(): void {
this.state = 'left';
}
public transitionDoneHide(): void {
if(this.state === 'right') {
console.log('hiding transition done');
this.componentWidth = '0' + '%';
this.componentHeight = '0' + '%';
this.componentOverflow = 'hidden';
}
}
}
Как видите, я разделил setStyle
на два метода setInViewStyle
и moveOut
. setInViewStyle
устанавливает стиль панели и перемещает его в представление. Метод moveOut
перемещает панель из поля зрения влево. Из-за этого я также изменил метод менеджера макета panelTransformation
.
Вот измененный код:
panelTransformation(transitions) {
if (transitions) {
let movement = null;
let panelsToRemove = [];
let panelsToAdd = [];
if (this.previousPanels) {
panelsToRemove = this.previousPanels.filter((panel) => transitions.panel.indexOf(panel) < 0);
panelsToAdd = transitions.panel.filter((panel) => this.previousPanels.indexOf(panel) < 0);
} else {
panelsToAdd = transitions.panel
}
if (panelsToRemove.length > 0) {
for (let panelToRemove of panelsToRemove) {
this.idPanelMap.get(panelToRemove).moveOut();
}
// wait for fade out to finish then start fade in
timer(PanelComponent.ANIMATION_DURATION + 100).subscribe(() => {
for (let panelToAdd of panelsToAdd) {
this.idPanelMap.get(panelToAdd).setInViewStyle(transitions.width[transitions.panel.indexOf(panelToAdd)], '100', 'initial');
}
for (let panelToRemove of panelsToRemove) {
this.idPanelMap.get(panelToRemove).setDefault();
}
});
} else { // first time so just fade in
for (let panelToAdd of panelsToAdd) {
this.idPanelMap.get(panelToAdd).setInViewStyle(transitions.width[transitions.panel.indexOf(panelToAdd)], '100', 'initial');
}
}
this.previousPanels = transitions.panel;
}
}
Как вы можете видеть, я полностью изменил логику, поэтому сначала вынимаю панели, которые нужно удалить, подождите, пока закончится анимация, а затем переместите новые панели. Вот ссылка на мой пример StackBlitz, который реализует все это, так что вы также можете увидеть, как он работает.
В соответствии с просьбой в комментарии я также приведу еще один пример движения в обоих направлениях. Это усложняет ситуацию. Мне пришлось добавить еще один переход для движения в другом направлении. И добавлена возможность установить направление в методе moveOut
. По моему мнению, это не выглядит так хорошо, потому что может иметь некоторое мерцание. Новый код компонента панели:
import { Component, ContentChild, QueryList,HostBinding,Input,ElementRef } from '@angular/core';
import {
trigger,
state,
style,
animate,
transition
} from '@angular/animations';
import { timer } from 'rxjs';
@Component({
selector: 'my-panel',
templateUrl: './panel.component.html',
styleUrls:['./panel.component.css'],
animations: [
trigger('transition', [
state('right', style({
transform: 'translateX(100%)',
opacity: 0
})),
state('inview', style({
})),
state('left', style({
transform: 'translateX(-100%)',
opacity: 0
})),
transition('right => inview', [
animate('${PanelComponent.ANIMATION_DURATION}ms 0ms ease-out',style({
transform: 'translateX(0)',
opacity: 1 }))
]),
transition('inview => left', [
animate('${PanelComponent.ANIMATION_DURATION}ms 0ms ease-in',style({
transform: 'translateX(-100%)',
opacity: 0 }))
]),
transition('inview => right', [
animate('${PanelComponent.ANIMATION_DURATION}ms 0ms ease-in',style( {
transform: 'translateX(100%)',
opacity: 0
}))
]),
transition('left => inview', [
animate('${PanelComponent.ANIMATION_DURATION}ms 0ms ease-out',style({
transform: 'translateX(0)',
opacity: 1 }))
])
])]
})
export class PanelComponent {
public static readonly ANIMATION_DURATION = 500;
public static readonly ANIMATION_DELAY = 100;
@Input() destroyOnHidden: boolean = false;
@Input() id : string = null;
@ContentChild('layout') contentChild : QueryList<ElementRef>;
@HostBinding('style.width') componentWidth = null;
@HostBinding('style.height') componentHeight = null;
@HostBinding('style.overflow') componentOverflow = null;
public state: string = null;
private lastDirection: 'left' | 'right';
public getId() {
return this.id;
}
constructor() {
this.state = 'right';
}
public setInViewStyle(width: string, height: string, overflow: string): void {
this.componentWidth = width + '%';
this.componentHeight = height + '%';
this.componentOverflow = overflow;
this.state = 'inview';
}
public setDefault(): void {
this.state = 'right';
}
public moveOut(direction: 'left' | 'right'): void {
this.lastDirection = direction;
this.state = direction;
}
public transitionDoneHide(): void {
if(this.state === this.lastDirection) {
if (this.lastDirection === 'right') {
timer(PanelComponent.ANIMATION_DELAY).subscribe(() => this.hide());
} else {
this.hide();
}
console.log('hiding transition done');
}
}
private hide() {
this.componentWidth = '0' + '%';
this.componentHeight = '0' + '%';
this.componentOverflow = 'hidden';
}
}
В методе panelTransformation
я добавил логику, чтобы задать направление движения вправо, если это первая панель. Это обновленный код:
panelTransformation(transitions) {
if (transitions) {
let movement = null;
let panelsToRemove = [];
let panelsToAdd = [];
if (this.previousPanels) {
panelsToRemove = this.previousPanels.filter((panel) => transitions.panel.indexOf(panel) < 0);
panelsToAdd = transitions.panel.filter((panel) => this.previousPanels.indexOf(panel) < 0);
} else {
panelsToAdd = transitions.panel
}
if (panelsToRemove.length > 0) {
for (let panelToRemove of panelsToRemove) {
let direction: 'left' | 'right' = 'left';
// if it is the first panel we move out right
if (this.previousPanels.indexOf(panelToRemove) === 0) {
direction = 'right';
}
this.idPanelMap.get(panelToRemove).moveOut(direction);
}
// wait for fade out to finish then start fade in
timer(PanelComponent.ANIMATION_DURATION + PanelComponent.ANIMATION_DELAY).subscribe(() => {
for (let panelToAdd of panelsToAdd) {
this.idPanelMap.get(panelToAdd).setInViewStyle(transitions.width[transitions.panel.indexOf(panelToAdd)], '100', 'initial');
}
for (let panelToRemove of panelsToRemove) {
this.idPanelMap.get(panelToRemove).setDefault();
}
});
} else { // first time so just fade in
for (let panelToAdd of panelsToAdd) {
this.idPanelMap.get(panelToAdd).setInViewStyle(transitions.width[transitions.panel.indexOf(panelToAdd)], '100', 'initial');
}
}
this.previousPanels = transitions.panel;
}
}
А также образец StackBlitz для этой реализации.