2016-03-17 15 views
9

私のユーザーは、キャンバス内でマウスでオブジェクトを移動(または回転)できる必要があります。マウスイベントが発生すると、最後のイベントまでのデルタ(方向と長さ)を計算するために画面座標が使用されます。特別何も...Angular2:マウスイベント処理(現在の位置に対する相対移動)

  1. マウスダウン(第1の座標取得)
  2. のMouseMove(n番目deltaXYによってオブジェクトを移動、deltaXYを計算し、座標取得)
  3. ステップ2と同様のmouseup(とのMouseMoveとのmouseupを停止イベント処理)

この一連のイベントの後、同じアクションを繰り返すことができます。

この古くなった例は、toRxコールを削除した後、期待通りに機能します。しかし、ここで第1の座標にデルタが決定されます。ここではgithub.com:rx-draggable

は例からコードを適応させるために私の努力です:

@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; 
     } 
    }); 
    } 
    ... 
} 

それは、1サイクルの間だけ動作します。 mouseupイベント後、私はこのエラーを得た:

Uncaught EXCEPTION: Error during evaluation of "mousemove"

ORIGINAL EXCEPTION: ObjectUnsubscribedError

ERROR CONTEXT: EventEvaluationErrorContext

mousemoveサブスクリプションのキャンセルは動作しません。次のすべてのマウスでエラーが繰り返されます。

私のコードに何が間違っていると思いますか?この問題を解決するためのエレガントなアプローチがありますか?

答えて

15

あなたの問題はunsubscribe()remove(sub : Subscription)の違いがEventEmitterにあると思います。しかし、サブスクリプション(@HostListenerによって作成されたものを除く)を使用せずにそれを行うことができ、読みやすくします。私はあなたのコードを少し書き直しました。 mouseupeventdocumentまたはwindowに置いても構わないかもしれません。そうしないと、キャンバスの外にマウスを放すと奇妙な動作をします。

警告:先にテストされていないコード

@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

あなたの古いコードは、 'remove'を使っていませんでした。何故でしょうか?あなたはangle2で反応性のものを学ぶ良いリソースを知っていますか? –

+0

@Meikoええと、私は 'remove'をテストしませんでしたが、これの前に使っていました。私は良い資源がなく、遊んでいる間だけ学んでいます:)。同意していただきありがとうございます。うれしいことに – PierreDuc

3

あなたが持っている問題は、コードが反応しないということです。リアクティブプログラミングでは、装飾時にすべての動作を定義し、1回のサブスクリプションのみが必要です。 、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 {} 
関連する問題