cordova-plugin-camera-preview / cordova-plugin-camera-preview

Cordova plugin that allows camera interaction from HTML code
MIT License
570 stars 560 forks source link

Pinch zoom #397

Open lemartva opened 6 years ago

lemartva commented 6 years ago

Do you have any plan to implement pinch zoom? Thanks for a great plugin.

ngt14 commented 6 years ago

Hello @lemartva, you can use Hammer.JS to get pinch zoom event. [https://hammerjs.github.io/]

lemartva commented 6 years ago

Awesome @ngt14 ! I'm going to check hammerjs, Thanks.

yehudahkay commented 6 years ago

+1

Bengejd commented 6 years ago

Here is how you handle pinch events for zooming.

Import this directive into your module

import { Directive, HostListener, ElementRef, Output, EventEmitter, AfterViewInit, OnDestroy } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/fromEvent';
import 'rxjs/add/operator/distinctUntilChanged';

@Directive({
  selector: '[pinch]'
})

export class PinchDirective implements AfterViewInit, OnDestroy {

  // flag that is set when 2 fingers touch the screen
  private isPinching = false;

  // initial scale no need to change
  private scale = 1;

  // store the initial distance between the fingers
  // so we can calculate with it
  private initalDistance;

  // used to store the Observable created from the touchmove event
  // so we can unsubscribe from it when ngDestroy is called
  private touchMove;

  constructor(

    // the actual element
    private el: ElementRef

  ) { }

  public ngAfterViewInit() {

    // start Observing the touchmove event
    this.touchMove = Observable
    .fromEvent(this.el.nativeElement, 'touchmove')
      // since both fingers trigger a move event, broadcast only
      // a new scale what it actually changed
      .distinctUntilChanged()
      .subscribe((event) => {

        // call the onTouchMove
        this.onTouchMove(event);

      });

  }

  // extend the outputs to your needs, for now we just use
  // the defaults, tag move, start and end
  @Output()
  public pinchmove: EventEmitter<any> = new EventEmitter();

  @Output()
  public pinchstart: EventEmitter<any> = new EventEmitter();

  @Output()
  public pinchend: EventEmitter<any> = new EventEmitter();

  @HostListener('touchstart', ['$event'])
  public onTouchStart(e) {

    // set the flag to true, only if 2 fingers touch te screen
    if (e.touches.length === 2) {

      // emit the touchStart event
      this.pinchstart.emit(e);

      this.isPinching = true;

    }
  }

  @HostListener('touchend', ['$event'])
  public onTouchEnd(e) {

    if (this.isPinching) {

      // reset the initial distance
      this.initalDistance = undefined;

      // we are not scaling anymore
      this.isPinching = false;

      this.pinchend.emit(e);

    }
  }

  private onTouchMove(e) {

    // are we pinching?
    if (this.isPinching) {

      // calculate the initial distance if not set
      if (!this.initalDistance) {
        this.initalDistance = this.getDistance(e);
      }

      // round the scale to 2 decimals, so the distinctUntilChanged
      // can do its work properly
      // console.log((this.getScale(e) * 100) / 100);
      // this.scale = Math.round(this.getScale(e) * 100) / 100;
      this.scale = this.getScale(e);

      // emit the scale!
      this.pinchmove.emit({ scale: this.scale });

    }
  }

  // calculate the scale
  private getScale(e) {
    return this.getDistance(e) / this.initalDistance;
  }

  // just some basic math to calculate the distance between two points
  private getDistance(e) {

    let touch0 = e.touches[0];
    let touch1 = e.touches[1];

    return Math.sqrt(
      (touch0.pageX - touch1.pageX) * (touch0.pageX - touch1.pageX) +
      (touch0.pageY - touch1.pageY) * (touch0.pageY - touch1.pageY)
    );

  }

  // clean things up
  public ngOnDestroy() {
    this.touchMove.unsubscribe();
  }

}

Set up your template file to handle the pinch events.

<div    pinch
           (pinchstart)="onPinchStart($event)"
           (pinchend)="onPinchEnd($event)"
           (pinchmove)="onPinchMove($event)">

The pinch functions go in your component

  onPinchMove(e) {

    // set the cameraZoom so we can track it globally
    this.cameraZoom = e.scale;

    // total amount we cameraZoomd
    let totalScaled = this.currentZoom * e.scale;

    // did we hit the max cameraZoom (pinch out)
    if (totalScaled >= MAX_ZOOM) {
      // fix the cameraZoom by calculating it, don't use the e.cameraZoom
      // scenario: an insane quick pinch out will offset the this.cameraZoom
      this.cameraZoom = MAX_ZOOM / this.currentZoom;
      totalScaled = MAX_ZOOM;
      console.log('Hit the max zoom!');
      return;
      // did we hit the min cameraZoom (pinch in)
    } else if (totalScaled <= MIN_ZOOM) {
      // fix the cameraZoom
      this.cameraZoom = MIN_ZOOM / this.currentZoom;
      totalScaled = MIN_ZOOM;
      console.log('Hit the min zoom!');
      return;
    }
    this.cameraPreview.setZoom(totalScaled);
    console.log(totalScaled);
  }

  onPinchStart(e) {

    // flag that sets the class to disable scrolling
    this.isZooming = true;
  }

  onPinchEnd(e) {

    // flip the flag, enable scrolling
    this.isZooming = false;

    // adjust the amount we already cameraZoomd
    this.currentZoom = this.cameraZoom * this.currentZoom;
  }

Now set the zoom for the camera

setZoom(zoom) {
    return this.cameraPreview.setZoom(zoom)
    .catch(err => {
      log.error('CameraPreviewService: An error occured while setting the camera zoom: ', err);
    });
  }