使用 Angular 拖放图像图标

Drag and Drop image icons using Angular

我使用 angular 设计了一个网格。我将从图像列表中拖出一些图像并将它们放入网格图块中。下面附上设计图

这是我的打字稿代码

officeItems = [Image's URL];

drop(event: CdkDragDrop<string[]>) {
if (event.previousContainer === event.container) {
  moveItemInArray(
    event.container.data,
    event.previousIndex,
    event.currentIndex
  );
} else {
  copyArrayItem(
    event.previousContainer.data,
    event.container.data,
    event.previousIndex,
    event.currentIndex
  );
}
}

HTML代码

<div class="row">
<div class="col-10" cdkDropList #cellList="cdkDropList" [cdkDropListData]="cells" [cdkDropListConnectedTo]="[itemList]">
  <mat-grid-list cols="10" >
    <mat-grid-tile *ngFor="let cell of cells" [style.background]="cell.color">
      <img *ngIf="cell.url" src="{{cell.url}}"/>
    </mat-grid-tile>
</mat-grid-list>
</div>

<div class="col-2" cdkDropList #itemList="cdkDropList" [cdkDropListData]="officeItems" [cdkDropListConnectedTo]="[cellList]" (cdkDropListDropped)="drop($event)">
  <pre *ngFor="let item of officeItems" cdkDrag><img src= "{{item}}" title="Sofa" style="margin-top: 10px;"/></pre>
</div>
</div>

ts文件中有两个数据数组

cells= [
{ id: 1, minDistanceToA: 0.0 },
{ id: 2, minDistanceToA: 1.0 },
{ id: 3, minDistanceToA: 2.0 },
{ id: 4, minDistanceToA: 3.0 },
{ id: 5, minDistanceToA: 4.0 }];

officeItems: any = ['../assets/images/sofa1.png', '../assets/images/chair.png'}];

我可以拖动图像但不能放下它们。当我尝试删除时,它再次替换为之前的 div

我试过使用您的代码,并在一天的前半天进行了处理。 我得出的结论是,结合 mat-grid 和 cdkDropList 是个坏主意。 不过,我可以告诉您的是,使用 copyArrayItem 并不能使您到达想要的位置。

改用这个:

onDrop(event: CdkDragDrop<cell[]>) {
      if (!(event.previousContainer === event.container)){
         event.container.data[event.currentIndex] = 
         event.previousContainer.data[event.previousIndex];
      }
}

这将做什么:

容器:包含 cdkDragDrop 项目的数组

Index: 用于挑选单件商品

  1. 获取您正在将另一个项目拖入的项目(当前索引处的当前容器数组)
  2. 将其替换为您当前正在拖动的项目(上一个索引处的上一个容器数组)

我知道我迟到了,但是...

在“网格”中管理 cdk 拖放非常复杂,因为 material angular cdkDropList 考虑单维

想法总是一样的,而不是使用唯一的 cdkDropList,我们可以创建如此多的 cdkDropList 作为我们拥有的元素 - 在外面的情况下,网格。全部包含在 div 和 cdkDropListGroup-

<div class="content" cdkDropListGroup>
  <div #board class="board">
    <mat-grid-list cols="5">
      <mat-grid-tile *ngFor="let cell of cells; let i = index">
        <div
          class="cell"
          cdkDropList
          [cdkDropListData]="cell"
          (cdkDropListDropped)="drop($event)"
          [style.background]="i == indexOver ? 'red' : 'yellowgreen'"
          (mouseover)="this.indexOver = i"
          (mouseout)="indexOver = -1"
        >
          <div cdkDrag>
            <img
              *ngIf="cell.src"
              [src]="cell.src"
              width="100%"
            />
            <span *ngIf="!cell.src"></span>
            <div *cdkDragPlaceholder></div>
          </div>
        </div>
      </mat-grid-tile>
    </mat-grid-list>
  </div>
  <div
    class="side"
    cdkDropList
    sortingDisabled="true"
    [cdkDropListData]="icons"
    (cdkDropListDropped)="drop($event)"
  >
    <div *ngFor="let icon of icons">
      <div cdkDrag (mousedown)="calculeMargin($event)">
        <img [src]="icon" />
        <img
          *cdkDragPreview
          [src]="icon"
          [ngStyle]="previewStyle ? previewStyle : null"
        />
        <div *cdkDragPlaceholder></div>
      </div>
    </div>
  </div>
</div>

我们的 mat-grid-tile 将是“cdkDrag”,这会根据 cell.src 的值显示 img 或 span。看到我们创建了一个“空”*cdkDragPlaceholder

在另一边,我们有一个典型的列表。我们转换 cdkDragPreview 使我们拖动元素时与我们的“tiles”大小相同(这是 (mousedown)="calculeMargin($event")

的目的

我使用相同的函数“drop”来控制所有的drop。为了检查我们是从“板”还是从“侧面”拖动,我使用了自己的“数据”。如果有一个 属性 "src" 是板上的一个项目,否则是侧面的一个项目。看到我们不使用“传输”或其他“奇怪的功能”,否则只需压入或更改数组的元素

  indexOver:number=-1;
  previewStyle: any = null;
  @ViewChild(MatGridTile, { static: false, read: ElementRef }) model;
  @ViewChild('board', { static: false,read:ElementRef })board:ElementRef;

  icons = [
    'https://picsum.photos/100/100?random=1',
    'https://picsum.photos/100/100?random=2',
    'https://picsum.photos/100/100?random=3',
  ];

  cells = Array(25).fill(' ').map((_,index)=>({src:null,id:index}));
  constructor() {}

  ngOnInit() {
    setTimeout(() => {
      const rect = this.model.nativeElement.getBoundingClientRect();
      this.previewStyle = {
        width: rect.width + 'px',
        height: rect.height + 'px',
      };
    });
  }
  calculeMargin(event: MouseEvent) {
    const rect = this.model.nativeElement.getBoundingClientRect();
    this.previewStyle = {
      width: rect.width + 'px',
        height: rect.height + 'px',
      'margin-top': -event.offsetY + 'px',
      'margin-left': -event.offsetX + 'px',
    };
  }
  drop(event: CdkDragDrop<any>) {
    if (event.previousContainer != event.container) {
      if (event.container.data.src!==undefined)
      {
        if (event.previousContainer.data.src!=undefined)
        {
          event.container.data.src=event.previousContainer.data.src
          event.previousContainer.data.src=null;
        }
        else
        {
          event.container.data.src=event.previousContainer.data[event.previousIndex]
        }
      }
      else
      {
        if (event.container.data.src===undefined && event.previousContainer.data.src!==undefined) 
        event.previousContainer.data.src=null;
      }
    }
  }

stackblitz

Update 如果我们使用两个 mat-grid-list,我们确实可以改进代码。一个用于“board”,另一个用于“side”

<div class="content" cdkDropListGroup>
  <div #board class="board">
    <mat-grid-list cols="5">
        ...
    </mat-grid-list>
  </div>
  <div class="side">
    <mat-grid-list cols="1">
      <mat-grid-tile *ngFor="let cell of icons; let i = index">
        <div
          class="cell"
          cdkDropList
          [cdkDropListData]="cell"
          (cdkDropListDropped)="drop($event)"
        >
          <div cdkDrag>
            <img [src]="cell" width="100%" />
            <div *cdkDragPlaceholder></div>
          </div>
        </div>
      </mat-grid-tile>
    </mat-grid-list>
  </div>
</div>

看到这个想法是相似的,而不是使用一个独特的cdkDropList,因为我们可以使用我们拥有的那么多的cdkDropList。这允许我们在“拖动”时不会重新排列图标列表。此外,我们可以使用 .css - 看到它正在考虑 5x5 的网格 - 使“图标”的大小与我们的棋盘图块相同。这允许我们不使用 cdkPreview 因为所有的“图标”具有相同的大小。

.content
{
  display:flex;
  justify-content: space-between;
  position:relative;

}
.board{
  flex-basis:80%;
  position:relative;
}
.board::affter
{
  content:' '
}
.side{
  flex-basis:16%;
}

.cell{
  width:100%;
  height:100%;
}

代码中唯一的变化是,当我们让 drop 使用并删除函数“CalculeMargin”时

    if (event.previousContainer.data.src!=undefined)
    {
        ....
    }
    else
    {
       //see that the data is the "src" of the icon
      event.container.data.src=event.previousContainer.data
    }

new stackblitz