Angular2: mouse event handling (movement relative to current position)

前端 未结 2 1653
独厮守ぢ
独厮守ぢ 2021-02-04 03:21

My user should be able to move (or rotate) an object by mouse in a canvas. When mouse events occur the screen coordinates are used to calculate the delta (direction and length)

相关标签:
2条回答
  • 2021-02-04 03:55

    The problem that you have is that the code is not reactive. In reactive programming all behaviors should be defined at decoration time and only one subscription is required.

    Here is an example: Angular2/rxjs mouse translation/rotation

    import {Component, NgModule, OnInit, ViewChild} from '@angular/core'
    import {BrowserModule, ElementRef, MouseEvent} from '@angular/platform-browser'
    import {Observable} from 'rxjs/Observable';
    import 'rxjs/add/observable/fromEvent';
    import 'rxjs/add/operator/map';
    import 'rxjs/add/operator/switchMapTo';
    import 'rxjs/add/operator/takeUntil';
    import 'rxjs/add/operator/combineLatest';
    import 'rxjs/add/operator/startWith';
    
    @Component({
      selector: 'my-app',
      styles: [`
      canvas{
        border: 1px solid red;
      }`],
      template: `
        <div>
          <h2>translate/Rotate by mouse</h2>
          <canvas #canvas id="3dview"></canvas>
          <p>Translate by delta: {{relativeTo$|async|json}}</p>
          <p>Rotate by angle: {{rotateToAngle$|async|json}}</p>
        </div>
      `
    })
    export class App extends OnInit {
    
        @ViewChild('canvas') 
        canvas: ElementRef;
    
        relativeTo$: Observable<{dx:number, dy:number, start: MouseEvent}>;
        rotateToAngle$: Observable<{angle:number, start: MouseEvent}>;
    
        ngOnInit() {
          const canvasNE = this.canvas.nativeElement;
    
          const mouseDown$ = Observable.fromEvent(canvasNE, 'mousedown');
          const mouseMove$ = Observable.fromEvent(canvasNE, 'mousemove');
          const mouseUp$ = Observable.fromEvent(canvasNE, 'mouseup');
    
          const moveUntilMouseUp$= mouseMove$.takeUntil(mouseUp$);
          const startRotate$ = mouseDown$.switchMapTo(moveUntilMouseUp$.startWith(null));
    
          const relativePoint = (start: MouseEvent, end: MouseEvent): {x:number, y:number} => 
          (start && end && {
            dx: start.clientX - end.clientX,
            dy: start.clientY - end.clientY,
            start: start
          } || {});
    
          this.relativeTo$ = startRotate$
            .combineLatest(mouseDown$)
            .map(arr => relativePoint(arr[0],arr[1]));
    
          this.rotateToAngle$ = this.relativeTo$
            .map((tr) => ({angle: Math.atan2(tr.dy, tr.dx), start: tr.start}));
    
    //      this.relativeTo$.subscribe(console.log.bind(console,'rotate:'));
    //      this.rotateToAngle$.subscribe(console.log.bind(console,'rotate 0:'));
        }
    }
    
    @NgModule({
      imports: [ BrowserModule ],
      declarations: [ App ],
      bootstrap: [ App ]
    })
    export class AppModule {}
    
    0 讨论(0)
  • 2021-02-04 04:00

    I believe your problem lies in the difference between unsubscribe() and remove(sub : Subscription) on an EventEmitter. But it is possible to do it without the use of subscriptions (except the ones created by a @HostListener) and make it easy to read. I've rewritten your code a little. You might consider though placing your mouseup event on the document or window, otherwise you get weird behaviour if you release your mouse outside the canvas.

    Warning: untested code ahead

    @Component({
        selector: 'home',
        providers: [Scene],
        template: '<canvas #canvas id="3dview"></canvas>'
    })
    export class Home {
        @ViewChild('canvas') 
        canvas: ElementRef;
    
        private scene: Scene;
        private last: MouseEvent;
        private el: HTMLElement;
    
        private mouseDown : boolean = false;
    
        @HostListener('mouseup')
        onMouseup() {
            this.mouseDown = false;
        }
    
        @HostListener('mousemove', ['$event'])
        onMousemove(event: MouseEvent) {
            if(this.mouseDown) {
               this.scene.rotate(
                  event.clientX - this.last.clientX,
                  event.clientY - this.last.clientY
               );
               this.last = event;
            }
        }
    
        @HostListener('mousedown', ['$event'])
        onMousedown(event) {
            this.mouseDown = true;
            this.last = event;
        }
    
        constructor(elementRef: ElementRef, scene: Scene) {
            this.el = elementRef.nativeElement;
            this.scene = scene;
        }
    }
    
    0 讨论(0)
提交回复
热议问题