ユーザーは、キャンバス内でマウスを使用してオブジェクトを移動(または回転)できる必要があります。マウスイベントが発生すると、画面座標を使用して最後のイベントまでのデルタ(方向と長さ)が計算されます。特にない...
この一連のイベントの後、同じアクションを繰り返すことができるはずです。
この古い例は、toRx
呼び出しを削除した後、期待どおりに機能します。しかし、ここで最初の座標へのデルタが決定されます: 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
イベントの後、このエラーが発生しました。
キャッチされていない例外:「mousemove」の評価中にエラーが発生しました
元の例外:ObjectUnsubscribedError
エラーコンテキスト:EventEvaluationErrorContext
mousemove
サブスクリプションのキャンセルは機能しません。エラーは、次のすべてのmousemovesで繰り返されています。
私のコードの何が問題なのか考えていますか?この問題を解決する別のエレガントなアプローチはありますか?
あなたの問題は、EventEmitter
上のunsubscribe()
とremove(sub : Subscription)
の違いにあると思います。ただし、サブスクリプションを使用せずにそれを行うことは可能です(@HostListener
)そして読みやすくします。あなたのコードを少し書き直しました。 mouseup
またはevent
にdocument
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;
}
}
あなたが持っている問題は、コードが反応しないということです。リアクティブプログラミングでは、すべての動作を装飾時に定義する必要があり、必要なサブスクリプションは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 {}