我需要帮助来动态分配2个或更多div的位置开关。 这是一个简单的示例:
let objects: Array<number> = [];
objects.push(1);
objects.push(2);
模板:
<div *ngFor="let obj of objects">
content {{object}}
</div>
用户可以从模板中更改对象的位置,将对象“ 1”移动到第二个位置,对象“ 2”将成为第一个。
我需要一个动画来向用户清楚地显示动植物。实际上,DOM中的结果就是这样的:
之前:
<div>
content 1
</div>
<div>
content 2
</div>
之后:
<div>
content 2
</div>
<div>
content 1
</div>
通过修改源数组,更改元素位置获得结果。
我可以使用哪种解决方案?我可以使用更复杂的对象来处理此问题,因此可以根据需要修改代码。
谢谢
答案 0 :(得分:1)
我不知道这是否是一个好的解决方案,但这是我唯一能想到的。我的想法是将div的“副本”与绝对位置保持不透明度= 0或可见性=隐藏的“原始”。更改数组时,将其包含在setTimeout中,以手动动画方式更改“副本”的顶部和左侧。
在代码中。假设您有一个数组数据
data:any[]=[1,2,3,4,5]
类似html的
<div >
<ng-container *ngFor="let item of data;let i=index">
<div #origin style="opacity:0" >{{item}}</div>
<div #copy style="position:absolute" [style.z-index]="i" >{{item}}</div>
</ng-container>
</div>
然后在ViewChildren中的ngAfterViewInit中获得“原点”和“副本”,我们将“副本”放置在他的位置
@ViewChildren("origin") bars: QueryList<ElementRef>;
@ViewChildren("copy") copies: QueryList<ElementRef>;
ngAfterViewInit()
{
const bars:any[]=this.bars.toArray().map(x=>x.nativeElement.getBoundingClientRect());
this.copies.forEach((x,index) => {
x.nativeElement.style.top=bars[index].top+"px"
x.nativeElement.style.left=bars[index].left+"px"
});
}
好吧,点击一下,我重新排列数组并制作动画
click() {
this.data=this.data
.map(x=>({item:x,value:Math.random()}))
.sort((a,b)=>a.value-b.value)
.map(x=>x.item)
setTimeout(()=>{
const bars:any[]=this.bars.toArray().map
(x=>x.nativeElement.getBoundingClientRect());
this.copies.forEach((x,index) => {
this.animate(x.nativeElement, bars[index].top+"px",bars[index].left+"px");
});
})
}
animate(element: any, top: string,left:string) {
const myAnimation = this.builder.build([
animate(this.timing, style({ top: top,left:left }))
]);
this.player = myAnimation.create(element);
this.player.play();
}
您可以在stackblitz
中看到更新为什么不制定指令?
更新2 我使用offsetTop和offsetLeft并添加window.scrollX和window.scrollY更新指令。此外,我添加了一个新属性:pos0,如果为true,则首先在pos 0,0中创建“复制”
@Directive({ selector: "[animate]" })
export class AnimateDirective implements OnInit {
original: any;
copy: any;
timing:string;
private player: AnimationPlayer;
@Input() set animate(value: string) {
this.timing = value || "450ms ease-in-out";
}
@Input('animatePos0') pos0:boolean=false;
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef,
private builder: AnimationBuilder,
private renderer: Renderer2
) {}
ngOnInit() {
this.original = this.viewContainer.createEmbeddedView(
this.templateRef
).rootNodes[0];
setTimeout(() => {
this.copy = this.viewContainer.createEmbeddedView(
this.templateRef
).rootNodes[0];
this.renderer.setStyle(this.original, "visibility","hidden");
const rect = !this.pos0?
{top:this.original.offsetTop,left:this.original.offsetLeft}:
{top:0,left:0};
this.renderer.setStyle(this.copy, "position", "absolute");
this.renderer.setStyle(this.copy, "top", rect.top+ window.scrollY + "px");
this.renderer.setStyle(this.copy, "left", rect.left+ window.scrollX + "px");
});
}
animateGo() {
setTimeout(() => {
const rect = {top:this.original.offsetTop,left:this.original.offsetLeft}
const myAnimation = this.builder.build([
animate(this.timing,
style({ top: rect.top+ window.scrollY,
left: rect.left+ window.scrollX }))
]);
this.player = myAnimation.create(this.copy);
this.player.play();
});
}
}
好吧,我们需要一些类似的东西
<ng-container *ngFor="let item of data;let i=index">
<div *animate="'150ms ease-in-out'"> {{item}}-{{i}}
</div>
</ng-container>
在我们的组件中还有一个ViewChildren
@ViewChildren(AnimateDirective) items:QueryList<AnimateDirective>
因此,当我们更改数据时,我们需要制作
this.items.forEach(x=>x.animateGo())
答案 1 :(得分:0)
研究之后,最好的解决方案是使用 cdk / drag-drop 。您可以导入该库并将其用于拖放元素。
您只需在代码中添加以下几行,就可以正常工作。
您的.ts文件应包含以下代码。
export class CdkDragDropSortingExample {
movies = [
'Episode I - The Phantom Menace',
'Episode II - Attack of the Clones'
];
drop(event: CdkDragDrop<string[]>) {
moveItemInArray(this.movies, event.previousIndex, event.currentIndex);
}
}
您的html应该是:
<div cdkDropList class="example-list" (cdkDropListDropped)="drop($event)">
<div class="example-box" *ngFor="let movie of movies" cdkDrag>{{movie}}</div>
</div>
您的css文件应包含以下类:
.example-list {
width: 500px;
max-width: 100%;
border: solid 1px #ccc;
min-height: 60px;
display: block;
background: white;
border-radius: 4px;
overflow: hidden;
}
.example-box {
padding: 20px 10px;
border-bottom: solid 1px #ccc;
color: rgba(0, 0, 0, 0.87);
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
cursor: move;
background: white;
font-size: 14px;
}
.cdk-drag-preview {
box-sizing: border-box;
border-radius: 4px;
box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),
0 8px 10px 1px rgba(0, 0, 0, 0.14),
0 3px 14px 2px rgba(0, 0, 0, 0.12);
}
.cdk-drag-placeholder {
opacity: 0;
}
.cdk-drag-animating {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
.example-box:last-child {
border: none;
}
.example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}