简体   繁体   中英

Issue with zoom in and zoom out image in canvas Angular

I am having issues when I try to zoom in and out of a canvas image.

The aim is to allow users to draw rectangles on the image. I am using canvas to display the image and then draw rectangles on image, this is working fine. But now I am trying to add zoom in and zoom out functionality. I am facing following issue:

When I zoom in or zoom out, I need to keep the rectangles in same place with respect to the image. The rectangle stays in the same coordinate location, but as the image is moved, the part of image in the rectangle has changed. How to keep the rectangle to cover same part of image even after zoom in/zoom out.

I am new to canvas and not sure how to get this working.

Here is the component that I am having trouble with:

tagger.component.html

<div class="row">
  <div class="text-image-content">
 
    <div class="column1">
        <div style="position: relative;">
            <div *ngFor="let drawItem of drawItems; let i = index">
                <input [ngStyle]="{'left':drawItem.x0 + 'px' , 'top': drawItem.y0 + 'px'}"
                    style="position: absolute; z-index: 999;" placeholder="Enter Object Name"
                    [(ngModel)]="drawItem.name" type="text">
                <input [ngStyle]="{'left':drawItem.x0 + 'px' , 'top': drawItem.y0 + 'px'}"
                    style="position: absolute; z-index: 999;" type="button" value="X" (click)="delete(i)">
            </div>
            
            <canvas #layer1 id="layer1" style="position: absolute; left: 0; top: 0; z-index: 0;"></canvas>
            <div class="row">
            </div>
        </div>
    </div>
    </div>
<button class="zoomBtn" (click)="zoomIn()">Zoom in</button>
<button class="zoomBtn" (click)="zoomOut()">Zoom out</button>
    <div class="column2">
        <div *ngFor="let drawItem of originalItems; let i = index">
            <div class="details">
              <div >Name: {{drawItem.name | titlecase}} </div>
               <div>({{drawItem.x0}}, {{drawItem.y0}} , {{drawItem.x0 + drawItem.x1}}, {{drawItem.y0 + drawItem.y1}})</div>
            </div>
        </div>
    </div>
</div>

tagger.component.ts

import {
    Component,
    ViewChild,
    Input,
    Output,
    OnInit,
    EventEmitter,
    ElementRef
} from '@angular/core';

@Component({
    selector: "app-tagger",
    templateUrl: "./tagger.component.html",
    styleUrls: ["./tagger.component.scss"]
})
export class TaggerComponent implements OnInit {

    name = "Angular";
    constructor() { }
    drawItems = []
    originalItems = []
    count = 0
    @Input('CanvasHeight') CanvasHeight
    @Input('CanvasWidth') CanvasWidth
    @Output() selected = new EventEmitter();
    taggedItem = ""
    showInput: boolean = false;
    isMoving: boolean;
    public imgWidth: number;
    public uniX: number;
    public uniY: number;
    public uniX2: number;
    public uniY2: number;
    public initX: number;
    public initY: number;
    public imgHeight: number;
    public url: string;
    public image;
    public originalImageWidth;
    public originalImageHeight;
    public hRatio;
    public vRatio;
    public translatePos = {x: this.CanvasWidth / 2, y: this.CanvasHeight / 2};
    public scale = 1.0;
    public scaleMultiplier = 0.8;

    @ViewChild("layer1", { static: false }) layer1Canvas: ElementRef;
    private context: CanvasRenderingContext2D;
    private layer1CanvasElement: any;

  ngOnInit(){
    this.imageLoad();
  }

    delete(i) {
        console.log(i)
        this.drawItems.splice(i, 1);
        this.originalItems.splice(i,1);
        this.drawRect("red", 0, 0, 1);
    }

    imageLoad() {
      this.image = new Image();
      this.image.src = "https://i.ibb.co/12TJSNy/patio.jpg";
      this.image.onload = () => {
        console.log(this.CanvasWidth, this.CanvasHeight);
        console.log(this.image.width, this.image.height);
        this.originalImageWidth = this.image.width;
        this.originalImageHeight = this.image.height;
        this.image.width = this.CanvasWidth;
        this.image.height = this.CanvasHeight;
        this.hRatio =   this.originalImageWidth/this.CanvasWidth;
        this.vRatio =  this.originalImageHeight/this.CanvasHeight; 
        this.layer1CanvasElement = this.layer1Canvas.nativeElement;
        this.layer1CanvasElement.width = this.CanvasWidth;
        this.layer1CanvasElement.height = this.CanvasHeight;
        this.showImage();
      }
    }

    showImage() {
        this.count ++;
        this.layer1CanvasElement = this.layer1Canvas.nativeElement;
        this.context = this.layer1CanvasElement.getContext("2d");
        this.context.clearRect(0, 0, this.CanvasWidth, this.CanvasHeight);
        this.context.save();
        this.context.translate(this.translatePos.x, this.translatePos.y);
        this.context.scale(this.scale, this.scale);
        this.context.drawImage(this.image, 0,0, this.image.width, this.image.height);
        this.context.restore()
        let parent = this;
        if(this.count==1){
          this.layer1CanvasElement.addEventListener("mousedown", (e) => {
            this.isMoving = true
            this.initX = e.offsetX;
            this.initY = e.offsetY;
          });

        this.layer1CanvasElement.addEventListener("mouseup", (e) => {
            this.isMoving = false
            this.showInput = true
            this.drawItems.push({
                name: "",
                x0: this.initX,
                y0: this.initY,
                x1: this.uniX,
                y1: this.uniY
            });
            this.originalItems.push({
                name: "",
                x0: Math.ceil(this.initX * this.hRatio),
                y0: Math.ceil(this.initY * this.vRatio),
                x1: Math.ceil(this.uniX *this.hRatio),
                y1: Math.ceil(this.uniY * this.vRatio)
            });
            parent.drawRect("red", e.offsetX - this.initX, e.offsetY - this.initY, 0);
            this.uniX = undefined
            this.uniY = undefined
        });
      }
        

        this.layer1CanvasElement.addEventListener("mousemove", (e) => {
            if (this.isMoving) {
                parent.drawRect("red", e.offsetX - this.initX, e.offsetY - this.initY, 0);
            }
        });

        this.drawRect("red", 0, 0, 1);

    }


    drawRect(color = "black", height, width, flag) {
      if (this.uniX | flag) {
        this.context.clearRect(0, 0, this.CanvasWidth, this.CanvasHeight);
        this.context.save();
        this.context.translate(this.translatePos.x, this.translatePos.y);
        this.context.scale(this.scale, this.scale);
        this.context.drawImage(this.image, 0,0, this.image.width, this.image.height);
        this.context.restore()
      }
      // console.log(this.image.width, this.image.height)
      this.uniX = height
      this.uniY = width
      this.uniX2 = height
      this.uniY2 = width
      for (var i = 0; i < this.drawItems.length; i++) {
          this.context.beginPath();
          this.context.rect(
              this.drawItems[i].x0,
              this.drawItems[i].y0,
              this.drawItems[i].x1,
              this.drawItems[i].y1
          );
          this.context.lineWidth = 3;
          this.context.strokeStyle = color;
          this.context.stroke();
      }
    }

    zoomIn(){
      console.log("zooming in")
      this.scale /= this.scaleMultiplier;
      this.showImage();
    }
    zoomOut(){
      console.log("zooming out")
      this.scale *= this.scaleMultiplier;
      this.showImage();
      
    }
}

tagger.component.scsss

.button {
    color: transparent;
    cursor: pointer;
}

.column1 {
    float: left;
    width: 70%;
    padding: 10px;
    height: 300px; /* Should be removed. Only for demonstration */
}
.column2 {
    float: left;
    width: 25%;
    padding: 10px;
    height: 300px; /* Should be removed. Only for demonstration */
}

/* Clear floats after the columns */
.row:after {
    content: "";
    display: table;
    clear: both;
}

.details {
    margin: 10px;
}

.text-image-content{
border: 1px solid #979797;
    border-radius: 8px;
    padding: 0 10px;
    margin-left:20px;
    margin-right:20px;
    height: 300px;
    overflow-y: scroll;
    overflow-x: scroll;
    scrollbar-width: thin;
}

.zoomBtn{
  // height:10px;
}

The complete code can be found in: https://stackblitz.com/edit/angular-tsotfa?file=src%2Fapp%2Ftagger%2Ftagger.component.ts

Thanks!

In your Zoomin or Zoomout function, reduce or increase the value of the coordinates of the bounding boxes ie this.drawItems. That will do your work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM