diff --git a/src/app/cartography/cartography.module.ts b/src/app/cartography/cartography.module.ts index 03e60c68..226ebbae 100644 --- a/src/app/cartography/cartography.module.ts +++ b/src/app/cartography/cartography.module.ts @@ -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, diff --git a/src/app/cartography/components/d3-map/d3-map.component.html b/src/app/cartography/components/d3-map/d3-map.component.html index a7adf7b8..5ca089d5 100644 --- a/src/app/cartography/components/d3-map/d3-map.component.html +++ b/src/app/cartography/components/d3-map/d3-map.component.html @@ -1,4 +1,4 @@ - + @@ -8,4 +8,3 @@ - diff --git a/src/app/cartography/components/moving-mode/moving-mode.component.html b/src/app/cartography/components/moving-mode/moving-mode.component.html deleted file mode 100644 index e69de29b..00000000 diff --git a/src/app/cartography/components/moving-mode/moving-mode.component.scss b/src/app/cartography/components/moving-mode/moving-mode.component.scss deleted file mode 100644 index e69de29b..00000000 diff --git a/src/app/cartography/components/moving-mode/moving-mode.component.spec.ts b/src/app/cartography/components/moving-mode/moving-mode.component.spec.ts deleted file mode 100644 index e94ffc4a..00000000 --- a/src/app/cartography/components/moving-mode/moving-mode.component.spec.ts +++ /dev/null @@ -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; - 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(); - }); -}); diff --git a/src/app/cartography/components/moving-mode/moving-mode.component.ts b/src/app/cartography/components/moving-mode/moving-mode.component.ts deleted file mode 100644 index 4b5a6e33..00000000 --- a/src/app/cartography/components/moving-mode/moving-mode.component.ts +++ /dev/null @@ -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('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('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(); - } -} diff --git a/src/app/cartography/directives/moving-canvas.directive.ts b/src/app/cartography/directives/moving-canvas.directive.ts new file mode 100644 index 00000000..983b2e3b --- /dev/null +++ b/src/app/cartography/directives/moving-canvas.directive.ts @@ -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('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); + } +} diff --git a/src/app/cartography/directives/zooming-canvas.directive.ts b/src/app/cartography/directives/zooming-canvas.directive.ts new file mode 100644 index 00000000..7c6d3c54 --- /dev/null +++ b/src/app/cartography/directives/zooming-canvas.directive.ts @@ -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('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})`; + }); + } + } +}