Support canvas transformation when dragged, Fixes: #96

This commit is contained in:
ziajka 2018-04-09 10:37:58 +02:00
parent 01f2518323
commit 74e60bd8d0
8 changed files with 98 additions and 38 deletions

View File

@ -84,9 +84,9 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
this.graphContext = new Context(true); this.graphContext = new Context(true);
if (this.windowFullSize) { if (this.windowFullSize) {
this.graphContext.setSize(this.getSize()); this.graphContext.size = this.getSize();
} else { } else {
this.graphContext.setSize(new Size(this.width, this.height)); this.graphContext.size = new Size(this.width, this.height);
} }
this.graphLayout = new GraphLayout(); this.graphLayout = new GraphLayout();
@ -113,19 +113,14 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
} }
private changeLayout() { private changeLayout() {
if (this.windowFullSize) {
if (this.parentNativeElement != null) { if (this.parentNativeElement != null) {
this.graphContext.setSize(this.getSize()); this.graphContext.size = this.getSize();
}
} else {
} }
if (this.graphContext != null) { if (this.graphContext != null) {
this.svg this.svg
.attr('width', this.graphContext.getSize().width) .attr('width', this.graphContext.size.width)
.attr('height', this.graphContext.getSize().height); .attr('height', this.graphContext.size.height);
} }
this.graphLayout.setNodes(this.nodes); this.graphLayout.setNodes(this.nodes);

View File

@ -1,25 +1,27 @@
import {Size} from "./size"; import {Size} from "./size";
import {Point} from "./point"; import {Point} from "./point";
export class Transformation {
constructor(
public x: number,
public y: number,
public k: number
) {}
}
export class Context { export class Context {
private size: Size; public transformation: Transformation;
public size: Size;
constructor(private centerZeroZeroPoint = false) { constructor(public centerZeroZeroPoint = true) {
this.size = new Size(0, 0); this.size = new Size(0, 0);
} this.transformation = new Transformation(0, 0, 1);
public getSize(): Size {
return this.size;
}
public setSize(size: Size): void {
this.size = size;
} }
public getZeroZeroTransformationPoint() { public getZeroZeroTransformationPoint() {
if (this.centerZeroZeroPoint) { if (this.centerZeroZeroPoint) {
return new Point(this.getSize().width / 2., this.getSize().height / 2.); return new Point(this.size.width / 2., this.size.height / 2.);
} }
return new Point(0, 0); return new Point(0, 0);
} }

View File

@ -2,6 +2,7 @@ import { Context } from "../models/context";
import { SVGSelection } from "../models/types"; import { SVGSelection } from "../models/types";
import { MovingTool } from "./moving-tool"; import { MovingTool } from "./moving-tool";
import { TestSVGCanvas } from "../../testing"; import { TestSVGCanvas } from "../../testing";
import { Size } from "../models/size";
describe('MovingTool', () => { describe('MovingTool', () => {
@ -47,7 +48,41 @@ describe('MovingTool', () => {
it('canvas should transformed', () => { it('canvas should transformed', () => {
expect(svg.canvas.attr('transform')).toEqual('translate(100, 100) scale(1)'); expect(svg.canvas.attr('transform')).toEqual('translate(100, 100) scale(1)');
}); });
it('context transformation should be updated', () => {
expect(context.transformation.x).toEqual(100);
expect(context.transformation.y).toEqual(100);
expect(context.transformation.k).toEqual(1);
}); });
});
describe('MovingTool can move canvas with ZeroZeroTransformationPoint', () => {
beforeEach(() => {
context.centerZeroZeroPoint = true;
context.size = new Size(1000, 1000);
svg.svg.node().dispatchEvent(
new MouseEvent('mousedown', {
clientX: 100, clientY: 100, relatedTarget: svg.svg.node(),
screenY: 1024, screenX: 1024, view: window
})
);
window.dispatchEvent(new MouseEvent('mousemove', {clientX: 200, clientY: 200}));
window.dispatchEvent(new MouseEvent('mouseup', {clientX: 200, clientY: 200, view: window}));
});
it('canvas should transformed', () => {
expect(svg.canvas.attr('transform')).toEqual('translate(600, 600) scale(1)');
});
it('context transformation should be updated', () => {
expect(context.transformation.x).toEqual(100);
expect(context.transformation.y).toEqual(100);
expect(context.transformation.k).toEqual(1);
});
});
describe('MovingTool can be deactivated', () => { describe('MovingTool can be deactivated', () => {
beforeEach(() => { beforeEach(() => {

View File

@ -1,8 +1,10 @@
import {SVGSelection} from "../models/types";
import {Context} from "../models/context";
import { D3ZoomEvent, zoom, ZoomBehavior} from "d3-zoom"; import { D3ZoomEvent, zoom, ZoomBehavior} from "d3-zoom";
import { event } from "d3-selection"; import { event } from "d3-selection";
import { SVGSelection} from "../models/types";
import { Context} from "../models/context";
export class MovingTool { export class MovingTool {
private selection: SVGSelection; private selection: SVGSelection;
private context: Context; private context: Context;
@ -33,8 +35,16 @@ export class MovingTool {
const e: D3ZoomEvent<SVGSVGElement, any> = event; const e: D3ZoomEvent<SVGSVGElement, any> = event;
canvas.attr( canvas.attr(
'transform', 'transform',
`translate(${self.context.getSize().width / 2 + e.transform.x}, ` + () => {
`${self.context.getSize().height / 2 + e.transform.y}) scale(${e.transform.k})`); self.context.transformation.x = e.transform.x;
self.context.transformation.y = e.transform.y;
self.context.transformation.k = e.transform.k;
const xTrans = self.context.getZeroZeroTransformationPoint().x + self.context.transformation.x;
const yTrans = self.context.getZeroZeroTransformationPoint().y + self.context.transformation.y;
const kTrans = self.context.transformation.k;
return `translate(${xTrans}, ${yTrans}) scale(${kTrans})`;
});
}; };
this.zoom.on('zoom', onZoom); this.zoom.on('zoom', onZoom);

View File

@ -113,4 +113,16 @@ describe('SelectionTool', () => {
expect(path_selection.attr('visibility')).toEqual('hidden'); expect(path_selection.attr('visibility')).toEqual('hidden');
}); });
}); });
describe('SelectionTool correctly transform points with context transformation', () => {
beforeEach(() => {
context.transformation.x = 100;
context.transformation.y = 100;
svg.svg.node().dispatchEvent(new MouseEvent('mousedown', {clientX: 100, clientY: 100}));
});
it('path should have d adjusted for transformation', () => {
expect(path_selection.attr('d')).toEqual('M-5,-14 l0,0 l0,0 l0,0z');
});
});
}); });

View File

@ -105,7 +105,10 @@ export class SelectionTool {
private transformation(point) { private transformation(point) {
const transformation_point = this.context.getZeroZeroTransformationPoint(); const transformation_point = this.context.getZeroZeroTransformationPoint();
return [point[0] - transformation_point.x, point[1] - transformation_point.y]; return [
point[0] - transformation_point.x - this.context.transformation.x,
point[1] - transformation_point.y - this.context.transformation.y
];
} }

View File

@ -27,8 +27,6 @@ export class GraphLayout implements Widget {
private movingTool: MovingTool; private movingTool: MovingTool;
private layersWidget: LayersWidget; private layersWidget: LayersWidget;
private centerZeroZeroPoint = true;
constructor() { constructor() {
this.linksWidget = new LinksWidget(); this.linksWidget = new LinksWidget();
this.nodesWidget = new NodesWidget(); this.nodesWidget = new NodesWidget();
@ -92,11 +90,16 @@ export class GraphLayout implements Widget {
.append<SVGGElement>('g') .append<SVGGElement>('g')
.attr('class', 'canvas'); .attr('class', 'canvas');
if (this.centerZeroZeroPoint) { canvas
canvas.attr( .merge(canvasEnter)
.attr(
'transform', 'transform',
(ctx: Context) => `translate(${ctx.getSize().width / 2}, ${ctx.getSize().height / 2})`); (ctx: Context) => {
} const xTrans = ctx.getZeroZeroTransformationPoint().x + ctx.transformation.x;
const yTrans = ctx.getZeroZeroTransformationPoint().y + ctx.transformation.y;
const kTrans = ctx.transformation.k;
return `translate(${xTrans}, ${yTrans}) scale(${kTrans})`;
});
const layersManager = new LayersManager(); const layersManager = new LayersManager();
layersManager.setNodes(this.nodes); layersManager.setNodes(this.nodes);