【问题标题】:Animate the position of two or more DIV positions in Angular动画 Angular 中两个或多个 DIV 位置的位置
【发布时间】:2020-07-05 10:58:12
【问题描述】:

我需要帮助来动态渲染 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>

结果是修改了源Array,改变了元素的位置。

我可以使用哪种解决方案?我可以使用更复杂的对象来处理这个问题,以便在需要时修改代码。

谢谢

【问题讨论】:

    标签: angular


    【解决方案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>
    

    我们在 ngAfterViewInit 中获取 ViewChildren 中的“原点”和“副本”,我们将“副本”定位在他的位置

     @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())
    

    查看new stackblitz

    【讨论】:

    • 这个解决方案太棒了,我喜欢!
    • @Pennywise83,小心我忘了添加 window.scrollX 和 window.scrollY。而且我不确定是否使用原始的getBoundClientRec 或offsetTop 和offsetLeft。无论如何,我很难处理对象的“边距”。我不太确定会按时纠正这个问题。我用这个改进了一些stackblitz,并在添加时为“animate”添加了一个新属性“pos0”
    【解决方案2】:

    我研究后最好的解决方案是使用 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);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-11-13
      • 1970-01-01
      • 2018-07-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-20
      • 2013-05-12
      相关资源
      最近更新 更多