mirror of
https://github.com/GNS3/gns3-web-ui.git
synced 2025-05-08 19:48:07 +00:00
Reimplementation of moving component
This commit is contained in:
parent
5a71481d47
commit
3405f89a4a
@ -55,8 +55,9 @@ import { RectangleElementFactory } from './helpers/drawings-factory/rectangle-el
|
|||||||
import { LineElementFactory } from './helpers/drawings-factory/line-element-factory';
|
import { LineElementFactory } from './helpers/drawings-factory/line-element-factory';
|
||||||
import { TextEditorComponent } from './components/text-editor/text-editor.component';
|
import { TextEditorComponent } from './components/text-editor/text-editor.component';
|
||||||
import { DrawingAddingComponent } from './components/drawing-adding/drawing-adding.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 { MovingEventSource } from './events/moving-event-source';
|
||||||
|
import { MovingCanvasDirective } from './directives/moving-canvas.directive';
|
||||||
|
import { ZoomingCanvasDirective } from './directives/zooming-canvas.directive';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [CommonModule, MatMenuModule, MatIconModule],
|
imports: [CommonModule, MatMenuModule, MatIconModule],
|
||||||
@ -70,7 +71,8 @@ import { MovingEventSource } from './events/moving-event-source';
|
|||||||
SelectionControlComponent,
|
SelectionControlComponent,
|
||||||
SelectionSelectComponent,
|
SelectionSelectComponent,
|
||||||
DraggableSelectionComponent,
|
DraggableSelectionComponent,
|
||||||
MovingModeComponent
|
MovingCanvasDirective,
|
||||||
|
ZoomingCanvasDirective
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
CssFixer,
|
CssFixer,
|
||||||
|
@ -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>
|
<filter id="grayscale"><feColorMatrix id="feGrayscale" type="saturate" values="0" /></filter>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
@ -8,4 +8,3 @@
|
|||||||
<app-selection-select></app-selection-select>
|
<app-selection-select></app-selection-select>
|
||||||
<app-text-editor #textEditor [svg]="svg"></app-text-editor>
|
<app-text-editor #textEditor [svg]="svg"></app-text-editor>
|
||||||
<app-draggable-selection [svg]="svg"></app-draggable-selection>
|
<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 |
@ -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();
|
|
||||||
});
|
|
||||||
});
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
65
src/app/cartography/directives/moving-canvas.directive.ts
Normal file
65
src/app/cartography/directives/moving-canvas.directive.ts
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
53
src/app/cartography/directives/zooming-canvas.directive.ts
Normal file
53
src/app/cartography/directives/zooming-canvas.directive.ts
Normal 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})`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user