Skip to main content

Angular Konva Z-Index Tutorial

To control shape stacking order in Angular, update the order of the data array that renders your shapes.

The demo shows how to:

  1. Create an array of circle shapes with random positions and colors
  2. Handle drag events to update the visual order of shapes
  3. Maintain the correct stacking order by manipulating the array order
  4. Keep rendering driven by Angular state instead of imperative zIndex() calls

Instructions: Try to drag a circle. When you start dragging, it will automatically move to the top of the stack. This is achieved by manipulating the array of circles in our data, not by manually changing zIndex.

import { Component, OnInit } from '@angular/core';
import { StageConfig } from 'konva/lib/Stage';
import { CircleConfig } from 'konva/lib/shapes/Circle';

import {
  CoreShapeComponent,
  StageComponent,
} from 'ng2-konva';

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    <ko-stage [config]="configStage">
      <ko-layer>
        @for (item of items; track trackById($index, item)) {
          <ko-circle
            [config]="item"
            (dragstart)="handleDragstart($event.event)"
            (dragend)="handleDragend()"
          ></ko-circle>
        }
      </ko-layer>
    </ko-stage>
  `,
  imports: [StageComponent, CoreShapeComponent],
})
export default class App implements OnInit {
  public configStage: StageConfig = {
    width: window.innerWidth,
    height: window.innerHeight,
  };
  
  public items: CircleConfig[] = [];
  private dragItemId: string | null = null;

  ngOnInit() {
    this.generateItems();
  }

  private generateItems(): void {
    const newItems: CircleConfig[] = [];
    for (let i = 0; i < 10; i++) {
      newItems.push({
        x: Math.random() * this.configStage.width!,
        y: Math.random() * this.configStage.height!,
        radius: 50,
        id: "node-" + i,
        fill: this.getRandomColor(),
        draggable: true
      });
    }
    this.items = newItems;
  }

  private getRandomColor(): string {
    const colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', '#DDA0DD', '#98D8C8', '#F7DC6F', '#BB8FCE', '#85C1E9'];
    return colors[Math.floor(Math.random() * colors.length)];
  }

  public handleDragstart(event: any): void {
    this.dragItemId = event.target.id();
    const item = this.items.find(i => i.id === this.dragItemId);
    if (item) {
      this.items = [
        ...this.items.filter((i) => i.id !== this.dragItemId),
        item
      ];
    }
  }

  public handleDragend(): void {
    this.dragItemId = null;
  }

  public trackById(index: number, item: CircleConfig): string {
    return item.id as string;
  }
}