2016-03-17 33 views
9

Mój użytkownik powinien móc przenosić (lub obracać) obiekt za pomocą myszy w obszarze roboczym. Kiedy wystąpią zdarzenia myszy, współrzędne ekranu są używane do obliczenia delty (kierunku i długości) do ostatniego zdarzenia. Nic specjalnego ...Angular2: obsługa zdarzeń myszy (ruch względem aktualnej pozycji)

  1. mouseDown (get pierwsza współrzędna)
  2. mousemove (Get nth współrzędnych, oblicz deltaXY przesunąć obiekt o deltaXY)
  3. mouseUp (taki sam jak krok 2 i zatrzymać mouseMove i mouseUp obsługa zdarzeń)

Po tym łańcuchu wydarzeń powinno być możliwe powtórzenie tego samego działania.

Ten przestarzały przykład działa zgodnie z oczekiwaniami po usunięciu wywołań toRx. Ale tutaj delta do pierwszego współrzędnych jest określana: github.com:rx-draggable

Oto mój wysiłek, aby dostosować kod z przykładu:

@Component({ 
    selector: 'home', 
    providers: [Scene], 
    template: '<canvas #canvas id="3dview"></canvas>' 
}) 
export class Home { 
    @ViewChild('canvas') canvas: ElementRef; 
    private scene: Scene; 
    private mousedrag = new EventEmitter(); 
    private mouseup = new EventEmitter<MouseEvent>(); 
    private mousedown = new EventEmitter<MouseEvent>(); 
    private mousemove = new EventEmitter<MouseEvent>(); 
    private last: MouseEvent; 
    private el: HTMLElement; 

    @HostListener('mouseup', ['$event']) 
    onMouseup(event: MouseEvent) { this.mouseup.emit(event); } 

    @HostListener('mousemove', ['$event']) 
    onMousemove(event: MouseEvent) { this.mousemove.emit(event); } 

    constructor(@Inject(ElementRef) elementRef: ElementRef, scene: Scene) { 
    this.el = elementRef.nativeElement; 
    this.scene = scene; 
    } 

    @HostListener('mousedown', ['$event']) 
    mouseHandling(event) { 
    event.preventDefault(); 
    console.log('mousedown', event); 
    this.last = event; 
    this.mousemove.subscribe({next: evt => { 
     console.log('mousemove.subscribe', evt); 
     this.mousedrag.emit(evt); 
    }}); 
    this.mouseup.subscribe({next: evt => { 
     console.log('mousemove.subscribe', evt); 
     this.mousedrag.emit(evt); 
     this.mousemove.unsubscribe(); 
     this.mouseup.unsubscribe(); 
    }}); 
    } 

    ngOnInit() { 
    console.log('init'); 
    this.mousedrag.subscribe({ 
     next: evt => { 
     console.log('mousedrag.subscribe', evt); 
     this.scene.rotate(
      evt.clientX - this.last.clientX, 
      evt.clientY - this.last.clientY); 
     this.last = evt; 
     } 
    }); 
    } 
    ... 
} 

To działa tylko na jednym cyklu. Po zakończeniu imprezy mouseup mam ten błąd:

Uncaught EXCEPTION: Error during evaluation of "mousemove"

ORIGINAL EXCEPTION: ObjectUnsubscribedError

ERROR CONTEXT: EventEvaluationErrorContext

Anulowanie subskrypcji mousemove nie działa. Błąd jest powtarzany dla wszystkich następujących po nim ruchów myszy.

Czy masz pojęcie, co jest nie tak z moim kodem? Czy istnieje inne eleganckie podejście do rozwiązania tego problemu?

Odpowiedz

15

wierzę, problem leży z różnicy pomiędzy unsubscribe() i remove(sub : Subscription) w sprawie EventEmitter. Ale można to zrobić bez korzystania z subskrypcji (z wyjątkiem tych utworzonych przez @HostListener) i ułatwić czytanie. Przepisałem trochę twój kod. Możesz rozważyć umieszczenie swojego mouseup na document lub window, w przeciwnym razie zachowasz się dziwnie, jeśli zwolnisz mysz poza obszarem roboczym.

Ostrzeżenie: Kod niesprawdzone naprzód

@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

dzięki, działa świetnie. –

+0

Twój stary kod, który używa polecenia "usuń", nie działa, masz pojęcie, dlaczego? Czy znasz dobry zasób, aby nauczyć się tego reaktywnego materiału za pomocą angular2? –

+0

@Meiko Mmm, nie przetestowałem 'remove', ale użyłem go wcześniej i tak działało. Nie mam dobrych zasobów i dopiero się uczę podczas gry :). Dziękuję za przyjęcie. Cieszę się, że mogę pomóc – PierreDuc

3

Problem, że masz to, że kod nie jest reaktywny. W programowaniu reaktywnym wszystkie zachowania powinny być zdefiniowane w czasie dekoracji i wymagana jest tylko jedna subskrypcja.

Oto przykład: 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 {}