Reimplementation of moving component

This commit is contained in:
Piotr Pekala 2019-04-01 10:06:31 -07:00
parent 5a71481d47
commit 3405f89a4a
8 changed files with 123 additions and 179 deletions

View File

@ -55,8 +55,9 @@ import { RectangleElementFactory } from './helpers/drawings-factory/rectangle-el
import { LineElementFactory } from './helpers/drawings-factory/line-element-factory';
import { TextEditorComponent } from './components/text-editor/text-editor.component';
import { DrawingAddingComponent } from './components/drawing-adding/drawing-adding.component';
import { MovingModeComponent } from './components/moving-mode/moving-mode.component';
import { MovingEventSource } from './events/moving-event-source';
import { MovingCanvasDirective } from './directives/moving-canvas.directive';
import { ZoomingCanvasDirective } from './directives/zooming-canvas.directive';
@NgModule({
imports: [CommonModule, MatMenuModule, MatIconModule],
@ -70,7 +71,8 @@ import { MovingEventSource } from './events/moving-event-source';
SelectionControlComponent,
SelectionSelectComponent,
DraggableSelectionComponent,
MovingModeComponent
MovingCanvasDirective,
ZoomingCanvasDirective
],
providers: [
CssFixer,

View File

@ -1,4 +1,4 @@
<svg #svg class="map" preserveAspectRatio="none">
<svg #svg class="map" preserveAspectRatio="none" movingCanvas zoomingCanvas>
<filter id="grayscale"><feColorMatrix id="feGrayscale" type="saturate" values="0" /></filter>
</svg>
@ -8,4 +8,3 @@
<app-selection-select></app-selection-select>
<app-text-editor #textEditor [svg]="svg"></app-text-editor>
<app-draggable-selection [svg]="svg"></app-draggable-selection>
<app-moving-mode [svg]="svg"></app-moving-mode>

Before

Width:  |  Height:  |  Size: 520 B

After

Width:  |  Height:  |  Size: 499 B

View File

@ -1,72 +0,0 @@
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { Context } from '../../models/context';
import { MovingModeComponent } from './moving-mode.component';
import { MovingEventSource } from '../../events/moving-event-source';
class SvgMock {
addEventListener() {}
}
describe('MovingModeComponent', () => {
let component: MovingModeComponent;
let fixture: ComponentFixture<MovingModeComponent>;
let movingEventSource = new MovingEventSource();
let svg = new SvgMock();
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [NoopAnimationsModule],
providers: [
{ provide: MovingEventSource, useValue: movingEventSource },
{ provide: Context, useClass: Context }
],
declarations: [MovingModeComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MovingModeComponent);
component = fixture.componentInstance;
component.svg = svg as unknown as SVGSVGElement;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should activate listener when moving mode changed to true', () => {
spyOn(component, 'activate');
movingEventSource.movingModeState.emit(true);
expect(component.activate).toHaveBeenCalled();
});
it('should deactivate listener when moving mode changed to false', () => {
spyOn(component, 'deactivate');
movingEventSource.movingModeState.emit(false);
expect(component.deactivate).toHaveBeenCalled();
});
it('should add moousemove listener on activation', () => {
spyOn(component, 'addMoveListener');
spyOn(component.svg, 'addEventListener').and.returnValue({});
component.activate();
expect(component.addMoveListener).toHaveBeenCalled();
});
it('should add mousewheel listener on activation', () => {
spyOn(component, 'addZoomListener');
spyOn(component.svg, 'addEventListener').and.returnValue({});
component.activate();
expect(component.addZoomListener).toHaveBeenCalled();
});
});

View File

@ -1,103 +0,0 @@
import { Component, OnInit, OnDestroy, Input } from '@angular/core';
import { Context } from '../../models/context';
import { MovingEventSource } from '../../events/moving-event-source';
import { Subscription } from 'rxjs';
import { select } from 'd3-selection';
@Component({
selector: 'app-moving-mode',
templateUrl: './moving-mode.component.html',
styleUrls: ['./moving-mode.component.scss']
})
export class MovingModeComponent implements OnInit, OnDestroy {
@Input('svg') svg: SVGSVGElement;
private scrollListener: Function;
private mouseupListener: Function;
private mousedownListener: Function;
private mousemoveListener: Function;
private movingModeState: Subscription;
constructor(
private context: Context,
private movingEventSource: MovingEventSource
) {}
ngOnInit() {
this.movingModeState = this.movingEventSource.movingModeState.subscribe((event: boolean) => {
event ? this.activate() : this.deactivate();
});
}
activate() {
this.addMoveListener();
this.addZoomListener();
}
addMoveListener() {
this.mousemoveListener = (event: MouseEvent) => {
const view = select(this.svg);
const canvas = view.selectAll<SVGGElement, Context>('g.canvas').data([this.context]);
canvas.attr('transform', () => {
this.context.transformation.x = this.context.transformation.x + event.movementX;
this.context.transformation.y = this.context.transformation.y + event.movementY;
const xTrans = this.context.getZeroZeroTransformationPoint().x + this.context.transformation.x;
const yTrans = this.context.getZeroZeroTransformationPoint().y + this.context.transformation.y;
const kTrans = this.context.transformation.k;
return `translate(${xTrans}, ${yTrans}) scale(${kTrans})`;
});
};
this.mousedownListener = (event: MouseEvent) => {
this.mouseupListener = (event: MouseEvent) => {
this.removelisteners();
};
this.svg.addEventListener('mouseup', this.mouseupListener as EventListenerOrEventListenerObject);
this.svg.addEventListener('mousemove', this.mousemoveListener as EventListenerOrEventListenerObject);
};
this.svg.addEventListener('mousedown', this.mousedownListener as EventListenerOrEventListenerObject);
}
addZoomListener() {
this.scrollListener = (event: WheelEvent) => {
event.stopPropagation();
event.preventDefault();
let zoom = event.deltaY;
zoom = event.deltaMode === 0 ? zoom/100 : zoom/3;
const view = select(this.svg);
const canvas = view.selectAll<SVGGElement, Context>('g.canvas').data([this.context]);
canvas.attr('transform', () => {
this.context.transformation.k = this.context.transformation.k - zoom/10;
const xTrans = this.context.getZeroZeroTransformationPoint().x + this.context.transformation.x;
const yTrans = this.context.getZeroZeroTransformationPoint().y + this.context.transformation.y;
const kTrans = this.context.transformation.k;
return `translate(${xTrans}, ${yTrans}) scale(${kTrans})`;
});
};
document.addEventListener('wheel', this.scrollListener as EventListenerOrEventListenerObject);
}
removelisteners() {
this.svg.removeEventListener('mouseup', this.mouseupListener as EventListenerOrEventListenerObject);
this.svg.removeEventListener('mousemove', this.mousemoveListener as EventListenerOrEventListenerObject);
}
deactivate() {
this.svg.removeEventListener('mouseup', this.mouseupListener as EventListenerOrEventListenerObject);
this.svg.removeEventListener('mousedown', this.mousedownListener as EventListenerOrEventListenerObject);
this.svg.removeEventListener('mousemove', this.mousemoveListener as EventListenerOrEventListenerObject);
document.removeEventListener('wheel', this.scrollListener as EventListenerOrEventListenerObject);
}
ngOnDestroy() {
this.movingModeState.unsubscribe();
}
}

View File

@ -0,0 +1,65 @@
import { HostListener, ElementRef, Renderer, Directive, Input, OnInit, OnDestroy } from '@angular/core'
import { Subscription } from 'rxjs';
import { MovingEventSource } from '../events/moving-event-source';
import { Context } from '../models/context';
import { select } from 'd3-selection';
@Directive({
selector: '[movingCanvas]',
})
export class MovingCanvasDirective implements OnInit, OnDestroy {
private mouseupListener: Function;
private mousemoveListener: Function;
private movingModeState: Subscription;
private activated: boolean = false;
constructor(
private element: ElementRef,
private movingEventSource: MovingEventSource,
private context: Context
) {}
ngOnInit() {
this.movingModeState = this.movingEventSource.movingModeState.subscribe((event: boolean) => {
this.activated = event;
if (!event) this.removelisteners();
});
}
ngOnDestroy() {
this.movingModeState.unsubscribe();
}
@HostListener('mousedown', ['$event'])
onMouseDown(event: MouseEvent) {
if (this.activated) {
this.mousemoveListener = (event: MouseEvent) => {
const view = select(this.element.nativeElement);
const canvas = view.selectAll<SVGGElement, Context>('g.canvas').data([this.context]);
canvas.attr('transform', () => {
this.context.transformation.x = this.context.transformation.x + event.movementX;
this.context.transformation.y = this.context.transformation.y + event.movementY;
const xTrans = this.context.getZeroZeroTransformationPoint().x + this.context.transformation.x;
const yTrans = this.context.getZeroZeroTransformationPoint().y + this.context.transformation.y;
const kTrans = this.context.transformation.k;
return `translate(${xTrans}, ${yTrans}) scale(${kTrans})`;
});
};
this.mouseupListener = (event: MouseEvent) => {
this.removelisteners();
};
this.element.nativeElement.addEventListener('mouseup', this.mouseupListener as EventListenerOrEventListenerObject);
this.element.nativeElement.addEventListener('mousemove', this.mousemoveListener as EventListenerOrEventListenerObject);
}
}
removelisteners() {
this.element.nativeElement.removeEventListener('mouseup', this.mouseupListener as EventListenerOrEventListenerObject);
this.element.nativeElement.removeEventListener('mousemove', this.mousemoveListener as EventListenerOrEventListenerObject);
}
}

View File

@ -0,0 +1,53 @@
import { HostListener, ElementRef, Renderer, Directive, Input, OnInit, OnDestroy } from '@angular/core'
import { Subscription } from 'rxjs';
import { MovingEventSource } from '../events/moving-event-source';
import { Context } from '../models/context';
import { select } from 'd3-selection';
@Directive({
selector: '[zoomingCanvas]',
})
export class ZoomingCanvasDirective implements OnInit, OnDestroy {
private movingModeState: Subscription;
private activated: boolean = false;
constructor(
private element: ElementRef,
private movingEventSource: MovingEventSource,
private context: Context
) {}
ngOnInit() {
this.movingModeState = this.movingEventSource.movingModeState.subscribe((event: boolean) => {
this.activated = event;
});
}
ngOnDestroy() {
this.movingModeState.unsubscribe();
}
@HostListener('window:wheel', ['$event'])
onWheel(event: WheelEvent) {
if (this.activated) {
event.stopPropagation();
event.preventDefault();
let zoom = event.deltaY;
zoom = event.deltaMode === 0 ? zoom/100 : zoom/3;
const view = select(this.element.nativeElement);
const canvas = view.selectAll<SVGGElement, Context>('g.canvas').data([this.context]);
canvas.attr('transform', () => {
this.context.transformation.k = this.context.transformation.k - zoom/10;
const xTrans = this.context.getZeroZeroTransformationPoint().x + this.context.transformation.x;
const yTrans = this.context.getZeroZeroTransformationPoint().y + this.context.transformation.y;
const kTrans = this.context.transformation.k;
return `translate(${xTrans}, ${yTrans}) scale(${kTrans})`;
});
}
}
}