From 7bf611988020d465ad9db79ca392d1d69e6a01be Mon Sep 17 00:00:00 2001 From: Jan Bicker Date: Wed, 21 Nov 2018 17:36:06 +0000 Subject: [PATCH] further refactorings --- src/index.ts | 278 +++++++++++++++++++++-------- src/time-graph-axis-scale.ts | 88 ++++++++- src/time-graph-axis.ts | 29 ++- src/time-graph-chart.ts | 26 ++- src/time-graph-component.ts | 9 + src/time-graph-container.ts | 7 +- src/time-graph-interaction.ts | 96 ---------- src/time-graph-model.ts | 4 +- src/time-graph-navigator.ts | 44 +++++ src/time-graph-row-element.ts | 11 +- src/time-graph-state-controller.ts | 74 ++------ src/time-graph-unit-controller.ts | 38 ++++ src/time-graph.ts | 59 ------ 13 files changed, 433 insertions(+), 330 deletions(-) delete mode 100644 src/time-graph-interaction.ts create mode 100644 src/time-graph-navigator.ts create mode 100644 src/time-graph-unit-controller.ts delete mode 100644 src/time-graph.ts diff --git a/src/index.ts b/src/index.ts index 3e1c6e5..601b310 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,143 +1,240 @@ -import { TimeGraph } from "./time-graph"; import { TimeGraphModel } from "./time-graph-model"; import { TimeGraphAxis } from "./time-graph-axis"; import { TimeGraphChart } from "./time-graph-chart"; +import { TimeGraphUnitController } from "./time-graph-unit-controller"; +import { TimeGraphNavigator } from "./time-graph-navigator"; -// const timeGraphSimple: TimeGraphModel = { -// id: 'test1', -// name: 'graph-test1', -// range: { -// start: 0, -// end: 1000 -// }, -// rows: [ -// { -// states: [ -// { -// label: 'state1', -// range: { -// start: 0, -// end: 100 -// } -// }, -// { -// label: 'state2', -// range: { -// start: 300, -// end: 400 -// } -// }, -// { -// label: 'state3', -// range: { -// start: 600, -// end: 700 -// } -// }, -// { -// label: 'state4', -// range: { -// start: 900, -// end: 1000 -// } -// } -// ] -// } -// ] -// } const timeGraph: TimeGraphModel = { id: 'test1', name: 'graph-test1', - range: { - start: 0, - end: 6000 - }, + totalRange: 160000, rows: [ { + range: { + start: 0, + end: 12000 + }, + states: [ + { + label: 'state blah', + range: { + start: 10, + end: 100 + } + }, + { + label: 'state blah', + range: { + start: 210, + end: 1100 + } + }, + { + label: 'state blah', + range: { + start: 1110, + end: 2100 + } + }, + { + label: 'state blah', + range: { + start: 2510, + end: 2600 + } + }, + { + label: 'state blah', + range: { + start: 4010, + end: 6100 + } + }, + { + label: 'state blah', + range: { + start: 7010, + end: 8100 + } + }, + { + label: 'state blah', + range: { + start: 9010, + end: 11100 + } + }, + ] + }, + { + range: { + start: 9000, + end: 63000 + }, states: [ { label: 'state1', range: { - start: 0, - end: 50 + start: 11000, + end: 15000 } }, { label: 'state2', range: { - start: 150, - end: 155 + start: 15320, + end: 15500 } }, { label: 'state3', range: { - start: 265, - end: 455 + start: 26500, + end: 34550 } }, { label: 'state4', range: { - start: 565, - end: 655 + start: 35650, + end: 46550 } }, { label: 'state5', range: { - start: 765, - end: 1455 + start: 57650, + end: 58455 + } + } + ] + }, + { + range: { + start: 21000, + end: 39000 + }, + states: [ + { + label: 'state2.1', + range: { + start: 21145, + end: 28255 + } + }, + { + label: 'state2.2', + range: { + start: 31265, + end: 35275 } }, + { + label: 'state2.3', + range: { + start: 36865, + end: 38955 + } + } + ] + }, + { + range: { + start: 35000, + end: 50000 + }, + states: [ { label: 'state6', range: { - start: 2265, - end: 2455 + start: 35265, + end: 36455 } }, { label: 'state7', range: { - start: 3265, - end: 3455 + start: 43265, + end: 46455 } }, { label: 'state8', range: { - start: 4265, - end: 4455 + start: 48265, + end: 50000 } } ] }, { range: { - start: 1000, - end: 2000 + start: 45000, + end: 90000 }, states: [ { - label: 'state2.1', + label: 'state6', range: { - start: 1145, - end: 1255 + start: 45265, + end: 46455 } }, { - label: 'state2.2', + label: 'state7', range: { - start: 1265, - end: 1275 + start: 53265, + end: 66455 } }, { - label: 'state2.3', + label: 'state8', + range: { + start: 78265, + end: 90000 + } + } + ] + }, + { + range: { + start: 75000, + end: 160000 + }, + states: [ + { + label: 'state6', + range: { + start: 75265, + end: 76455 + } + }, + { + label: 'state6', + range: { + start: 77265, + end: 86455 + } + }, + { + label: 'state6', + range: { + start: 100265, + end: 100455 + } + }, + { + label: 'state7', range: { - start: 1365, - end: 1555 + start: 120265, + end: 126455 + } + }, + { + label: 'state8', + range: { + start: 147265, + end: 160000 } } ] @@ -145,23 +242,46 @@ const timeGraph: TimeGraphModel = { ] } -const tg = new TimeGraph('main', timeGraph); +const container = document.getElementById('main'); +if (!container) { + throw (`No container available.`); +} +container.innerHTML = ''; + +const axisContainer = document.createElement('div'); +axisContainer.id = 'main_axis'; +container.appendChild(axisContainer); + +const chartContainer = document.createElement('div'); +chartContainer.id = 'main_chart'; +container.appendChild(chartContainer); + +const controller = new TimeGraphUnitController(timeGraph.totalRange, {start: 70000, end: 80000}); const timeAxis = new TimeGraphAxis({ id: 'timeGraphAxis', height: 30, width: 500 -}, timeGraph.range, tg.controller); +}, controller); +axisContainer.appendChild(timeAxis.canvas); const timeGraphChart = new TimeGraphChart({ id: timeGraph.id + '_chart', height: 300, width: 500 -}, timeGraph.range, tg.controller); +}, controller); timeGraphChart.addRows(timeGraph.rows); +chartContainer.appendChild(timeGraphChart.canvas); -tg.timeGraphAxis = timeAxis; -tg.timeGraphChart = timeGraphChart; +const naviEl = document.createElement('div'); +naviEl.id = "navi"; +document.body.appendChild(naviEl); +const navi = new TimeGraphNavigator({ + width:1200, + height:20, + id:'navi' +}, controller); +naviEl.appendChild(navi.canvas); export type TestFieldId = 'test0' | 'test1' | 'test2' | 'test3' | 'test4' | 'test5' | 'test6' | 'test7' | 'test8' | 'test9'; export function tgTest(id: TestFieldId, val: string) { diff --git a/src/time-graph-axis-scale.ts b/src/time-graph-axis-scale.ts index cf80b36..ee8282a 100644 --- a/src/time-graph-axis-scale.ts +++ b/src/time-graph-axis-scale.ts @@ -1,25 +1,95 @@ -import { TimeGraphComponent, TimeGraphRect } from "./time-graph-component"; -import { TimeGraphInteraction } from "./time-graph-interaction"; +import { TimeGraphComponent, TimeGraphRect, TimeGraphInteractionHandler } from "./time-graph-component"; +import { TimeGraphUnitController } from "./time-graph-unit-controller"; +import { TimeGraphRange } from "./time-graph-model"; +import { TimeGraphStateController } from "./time-graph-state-controller"; export class TimeGraphAxisScale extends TimeGraphComponent { - constructor(id: string, protected options: TimeGraphRect, interaction: TimeGraphInteraction) { + protected mouseStartY: number; + protected mouseStartX: number; + protected oldViewRange: TimeGraphRange; + + protected mouseIsDown: boolean = false; + + constructor(id: string, protected options: TimeGraphRect, protected unitController: TimeGraphUnitController, protected stateController: TimeGraphStateController) { super(id); - const navigationApi = interaction.dnDZoomAndPan; - interaction.addEvent('mousedown', navigationApi.start, this._displayObject); - interaction.addEvent('mousemove', navigationApi.move, this._displayObject); - interaction.addEvent('mouseup', navigationApi.end, this._displayObject); - interaction.addEvent('mouseupoutside', navigationApi.end, this._displayObject); + this.addEvent('mousedown', event => { + this.mouseStartY = event.data.global.y; + this.mouseStartX = event.data.global.x; + this.oldViewRange = this.unitController.viewRange; + this.mouseIsDown = true; + }, this._displayObject); + this.addEvent('mousemove', event => { + if (this.mouseIsDown) { + const delta = event.data.global.y - this.mouseStartY; + const zoomStep = (delta / 100); + this.zoom(zoomStep); + } + }, this._displayObject); + const moveEnd: TimeGraphInteractionHandler = event => { + this.mouseIsDown = false; + } + this.addEvent('mouseup', moveEnd, this._displayObject); + this.addEvent('mouseupoutside', moveEnd, this._displayObject); + + unitController.onViewRangeChanged(() => this.update()); + } + + update() { + this._displayObject.clear(); + this.render(); } render() { this.rect({ - color: 0xFF0000, + color: 0xededdd, height: this.options.height, width: this.options.width, position: this.options.position }); + const stepLength = 1000; + const steps = Math.trunc(this.unitController.absoluteRange / stepLength); + for (let i = 0; i < steps; i++) { + const height = i % 10 === 0 ? -10 : -5; + const xpos = (stepLength * i - this.unitController.viewRange.start) * this.stateController.zoomFactor; + if (xpos > 0 && xpos < this.stateController.canvasWidth) { + const position = { + x: xpos, + y: this.options.height + }; + this.vline({ + position, + height, + color: 0x000000 + }); + } + } + } + zoom(zoomStep: number) { + const oldViewRangeLength = this.oldViewRange.end - this.oldViewRange.start; + const newViewRangeLength = oldViewRangeLength / (1 + (zoomStep)); + const normZoomFactor = newViewRangeLength / oldViewRangeLength; + const shiftedMouseX = normZoomFactor * this.mouseStartX; + const xOffset = this.mouseStartX - shiftedMouseX; + const viewRangeOffset = xOffset / (this.stateController.canvasWidth / oldViewRangeLength); + let start = this.oldViewRange.start + viewRangeOffset; + if(start < 0) { + start = 0; + } + let end = start + newViewRangeLength; + if(end > this.unitController.absoluteRange){ + end = this.unitController.absoluteRange; + } + this.unitController.viewRange = { + start, + end + } + + + } + + } \ No newline at end of file diff --git a/src/time-graph-axis.ts b/src/time-graph-axis.ts index a35fc48..a6de09a 100644 --- a/src/time-graph-axis.ts +++ b/src/time-graph-axis.ts @@ -1,33 +1,46 @@ import { TimeGraphAxisScale } from "./time-graph-axis-scale"; import { TimeGraphContainer, TimeGraphContainerOptions } from "./time-graph-container"; -import { TimeGraphRange } from "./time-graph-model"; -import { TimeGraphStateController } from "./time-graph-state-controller"; +import { TimeGraphUnitController } from "./time-graph-unit-controller"; export class TimeGraphAxis extends TimeGraphContainer { protected scaleComponent: TimeGraphAxisScale; - constructor(protected canvasOpts: TimeGraphContainerOptions, protected range: TimeGraphRange, protected controller: TimeGraphStateController) { + constructor(protected canvasOpts: TimeGraphContainerOptions, protected unitController: TimeGraphUnitController) { super({ id: canvasOpts.id, height: canvasOpts.height, width: canvasOpts.width, backgroundColor: 0xAA30f0 - }, controller); + }, unitController); this.init(); - this.controller.timeGraphInteraction.addMousewheelZoomAndPan(this.canvas); + this._canvas.addEventListener('mousewheel', (ev: WheelEvent) => { + const shiftStep = ev.deltaY * 10; + const oldViewRange = this.unitController.viewRange; + let start = oldViewRange.start + shiftStep; + if (start < 0) { + start = 0; + } + let end = start + this.unitController.viewRangeLength; + if (end > this.unitController.absoluteRange) { + start = this.unitController.absoluteRange - this.unitController.viewRangeLength; + end = start + this.unitController.viewRangeLength; + } + this.unitController.viewRange = { start, end } + return false; + }); } init() { this.scaleComponent = new TimeGraphAxisScale(this.canvasOpts.id + '_scale', { height: 30, - width: (this.range.end - this.range.start) * this._controller.zoomFactor, + width: this.unitController.viewRangeLength * this.stateController.zoomFactor, position: { - x: this._controller.positionOffset.x, + x: this.stateController.positionOffset.x, y: 0 } - }, this._controller.timeGraphInteraction); + }, this.unitController, this.stateController); this.addChild(this.scaleComponent); } diff --git a/src/time-graph-chart.ts b/src/time-graph-chart.ts index 683cd18..63fe0ba 100644 --- a/src/time-graph-chart.ts +++ b/src/time-graph-chart.ts @@ -1,45 +1,53 @@ import { TimeGraphContainer, TimeGraphContainerOptions } from "./time-graph-container"; import { TimeGraphRowElement } from "./time-graph-row-element"; import { TimeGraphRow } from "./time-graph-row"; -import { TimeGraphRowModel, TimeGraphRowElementModel, TimeGraphRange } from "./time-graph-model"; -import { TimeGraphStateController } from "./time-graph-state-controller"; +import { TimeGraphRowModel, TimeGraphRowElementModel } from "./time-graph-model"; +import { TimeGraphUnitController } from "./time-graph-unit-controller"; export class TimeGraphChart extends TimeGraphContainer { protected rows: TimeGraphRowModel[]; - constructor(canvasOpts: TimeGraphContainerOptions, protected range: TimeGraphRange, controller: TimeGraphStateController) { + constructor(canvasOpts: TimeGraphContainerOptions, unitController: TimeGraphUnitController) { super({ id: canvasOpts.id, height: canvasOpts.height, width: canvasOpts.width, backgroundColor: 0xFFFFFF - }, controller); + }, unitController); this.rows = []; + + this.unitController.onViewRangeChanged(() => { + this.update(); + }); } addRow(row: TimeGraphRowModel) { const height = 20; const rowId = 'row_' + this._stage.children.length; + const range = row.range.end - row.range.start; + const relativeStartPosition = row.range.start - this.unitController.viewRange.start; const rowComponent = new TimeGraphRow(rowId, { position: { - x: 0, // TODO must be calculated by zoom and pan + x: relativeStartPosition * this.stateController.zoomFactor, y: (height * this.rows.length) + height / 2 }, - width: this.range.end + width: range * this.stateController.zoomFactor }); this.addChild(rowComponent); this.rows.push(row); row.states.forEach((rowElement: TimeGraphRowElementModel, idx: number) => { + const relativeElementStartPosition = rowElement.range.start - this.unitController.viewRange.start; + const relativeElementEndPosition = rowElement.range.end - this.unitController.viewRange.start const newRowElement: TimeGraphRowElementModel = { label: rowElement.label, range: { - start: (rowElement.range.start * this._controller.zoomFactor) + this._controller.positionOffset.x, - end: (rowElement.range.end * this._controller.zoomFactor) + this._controller.positionOffset.x + start: (relativeElementStartPosition * this.stateController.zoomFactor) + this.stateController.positionOffset.x, + end: (relativeElementEndPosition * this.stateController.zoomFactor) + this.stateController.positionOffset.x } } - const el = new TimeGraphRowElement(rowId + '_el_' + idx, newRowElement, rowComponent, this._controller.timeGraphInteraction); + const el = new TimeGraphRowElement(rowId + '_el_' + idx, newRowElement, rowComponent); this.addChild(el); }); } diff --git a/src/time-graph-component.ts b/src/time-graph-component.ts index 5c9a4f6..90fbe43 100644 --- a/src/time-graph-component.ts +++ b/src/time-graph-component.ts @@ -66,4 +66,13 @@ export abstract class TimeGraphComponent { this.displayObject.moveTo(position.x, position.y); this.displayObject.lineTo(position.x, position.y + height); } + + addEvent(event: TimeGraphInteractionType, handler: TimeGraphInteractionHandler, displayObject: PIXI.DisplayObject) { + displayObject.interactive = true; + displayObject.on(event, (e: PIXI.interaction.InteractionEvent) => { + if (handler) { + handler(e); + } + }); + } } \ No newline at end of file diff --git a/src/time-graph-container.ts b/src/time-graph-container.ts index e2f4021..95a093b 100644 --- a/src/time-graph-container.ts +++ b/src/time-graph-container.ts @@ -1,5 +1,6 @@ import { TimeGraphComponent } from "./time-graph-component"; import * as PIXI from "pixi.js"; +import { TimeGraphUnitController } from "./time-graph-unit-controller"; import { TimeGraphStateController } from "./time-graph-state-controller"; export interface TimeGraphContainerOptions { @@ -14,9 +15,9 @@ export abstract class TimeGraphContainer { protected _stage: PIXI.Container; protected _canvas: HTMLCanvasElement; - protected _controller: TimeGraphStateController; + protected stateController: TimeGraphStateController; - constructor(config: TimeGraphContainerOptions, controller: TimeGraphStateController) { + constructor(config: TimeGraphContainerOptions, protected unitController: TimeGraphUnitController) { const canvas: HTMLCanvasElement = document.createElement('canvas'); canvas.width = config.width; canvas.height = config.height; @@ -33,7 +34,7 @@ export abstract class TimeGraphContainer { this._stage = application.stage; this._canvas = application.view; - this._controller = controller; + this.stateController = new TimeGraphStateController(canvas, unitController); } get canvas(): HTMLCanvasElement { diff --git a/src/time-graph-interaction.ts b/src/time-graph-interaction.ts deleted file mode 100644 index 8219a83..0000000 --- a/src/time-graph-interaction.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { TimeGraphStateController } from "./time-graph-state-controller"; - -export type TimeGraphInteractionType = 'mouseover' | 'mouseout' | 'mousemove' | 'mousedown' | 'mouseup' | 'mouseupoutside' | 'click'; -export type TimeGraphInteractionHandler = (event: PIXI.interaction.InteractionEvent) => void; - -export interface DragAndDropNavigationHandler { - start: TimeGraphInteractionHandler - move: TimeGraphInteractionHandler - end: TimeGraphInteractionHandler -} - -export class TimeGraphInteraction { - protected mouseStartY: number; - protected mouseStartX: number; - protected graphWidthStart: number; - - protected mouseIsDown: boolean = false; - - constructor(protected controller: TimeGraphStateController) { } - - addMousewheelZoomAndPan(canvas: HTMLCanvasElement) { - canvas.addEventListener('mousewheel', (ev: WheelEvent) => { - this.mouseStartX = ev.x; - this.graphWidthStart = this.controller.graphWidth; - this.zoom((ev.deltaY / 100) * (-1)); - this.setXOffset(ev.deltaX * (-1)); - this.setZoomAndPosition(); - return false; - }); - } - - get dnDZoomAndPan(): DragAndDropNavigationHandler { - return { - start: event => { - this.mouseStartY = event.data.global.y; - this.mouseStartX = event.data.global.x; - this.graphWidthStart = this.controller.graphWidth; - this.mouseIsDown = true; - }, - move: event => { - if (this.mouseIsDown) { - const deltaY = event.data.global.y - this.mouseStartY; - const zoomMulti = (deltaY / 100); - this.zoom(zoomMulti); - const deltaMouseX = event.data.global.x - this.mouseStartX; - this.setXOffset(deltaMouseX); - } - }, - end: event => { - this.mouseIsDown = false; - this.setZoomAndPosition(); - } - } - } - - protected setZoomAndPosition() { - this.controller.oldZoomFactor = this.controller.zoomFactor; - this.controller.oldPositionOffset = this.controller.positionOffset; - } - - protected setXOffset(deltaMouseX: number = 0) { - const c = this.controller; - const normZoomFactor = c.graphWidth / this.graphWidthStart; - const graphMouseStartX = (c.oldPositionOffset.x * (-1)) + this.mouseStartX; - const shiftedMouseX = normZoomFactor * graphMouseStartX; - const xOffset = c.oldPositionOffset.x + graphMouseStartX - shiftedMouseX + deltaMouseX; - - const minOffset = c.canvasWidth - c.graphWidth; - let finalXOffset: number; - if (xOffset > 0) { - finalXOffset = 0; - } else if (xOffset < minOffset) { - finalXOffset = minOffset; - } else { - finalXOffset = xOffset; - } - c.positionOffset = { x: finalXOffset, y: 0 }; - } - - zoom(zoomMulti: number) { - const c = this.controller; - const newZoomFactor = c.oldZoomFactor + zoomMulti; - if (newZoomFactor > c.initialZoomFactor) { - c.zoomFactor = newZoomFactor; - } - } - - addEvent(event: TimeGraphInteractionType, handler: TimeGraphInteractionHandler, displayObject: PIXI.DisplayObject) { - displayObject.interactive = true; - displayObject.on(event, (e: PIXI.interaction.InteractionEvent) => { - if (handler) { - handler(e); - } - }); - } -} \ No newline at end of file diff --git a/src/time-graph-model.ts b/src/time-graph-model.ts index affab9a..672be17 100644 --- a/src/time-graph-model.ts +++ b/src/time-graph-model.ts @@ -6,12 +6,12 @@ export interface TimeGraphRange { export interface TimeGraphModel { id: string name: string - range: TimeGraphRange + totalRange: number rows: TimeGraphRowModel[] } export interface TimeGraphRowModel { - range?: TimeGraphRange + range: TimeGraphRange states: TimeGraphRowElementModel[] } diff --git a/src/time-graph-navigator.ts b/src/time-graph-navigator.ts new file mode 100644 index 0000000..59a8eea --- /dev/null +++ b/src/time-graph-navigator.ts @@ -0,0 +1,44 @@ +import { TimeGraphContainer, TimeGraphContainerOptions } from "./time-graph-container"; +import { TimeGraphUnitController } from "./time-graph-unit-controller"; +import { TimeGraphComponent } from "./time-graph-component"; +import { TimeGraphStateController } from "./time-graph-state-controller"; + +export class TimeGraphNavigator extends TimeGraphContainer { + + protected scaleComponent: TimeGraphNavigatorHandle; + + constructor(protected canvasOpts: TimeGraphContainerOptions, protected unitController: TimeGraphUnitController) { + super({ + id: canvasOpts.id, + height: canvasOpts.height, + width: canvasOpts.width, + backgroundColor: 0x111111 + }, unitController); + + this.unitController.onViewRangeChanged(() => this.update()); + this.scaleComponent = new TimeGraphNavigatorHandle(this.unitController, this.stateController); + this.addChild(this.scaleComponent); + } + + update() { + this.scaleComponent.clear(); + this.scaleComponent.render(); + } +} + +export class TimeGraphNavigatorHandle extends TimeGraphComponent { + constructor(protected unitController: TimeGraphUnitController, protected stateController: TimeGraphStateController) { + super('navigator_handle'); + } + + render(): void { + const position = { x: this.unitController.viewRange.start * this.stateController.absoluteResolution, y: 0 }; + const width = this.unitController.viewRangeLength * this.stateController.absoluteResolution; + this.rect({ + height: 20, + position, + width, + color: 0x11aa11 + }) + } +} \ No newline at end of file diff --git a/src/time-graph-row-element.ts b/src/time-graph-row-element.ts index 6e78fb9..1daab38 100644 --- a/src/time-graph-row-element.ts +++ b/src/time-graph-row-element.ts @@ -1,13 +1,12 @@ import { TimeGraphComponent, TimeGraphStyledRect } from "./time-graph-component"; import { TimeGraphRow } from "./time-graph-row"; import { TimeGraphRowElementModel } from "./time-graph-model"; -import { TimeGraphInteraction } from "./time-graph-interaction"; export class TimeGraphRowElement extends TimeGraphComponent { protected style: TimeGraphStyledRect; - constructor(id: string, protected options: TimeGraphRowElementModel, protected row: TimeGraphRow, interaction: TimeGraphInteraction) { + constructor(id: string, protected options: TimeGraphRowElementModel, protected row: TimeGraphRow) { super(id); const height = 20; const position = { @@ -23,10 +22,10 @@ export class TimeGraphRowElement extends TimeGraphComponent { width }; - interaction.addEvent('mouseover', this.handleMouseOver, this._displayObject); - interaction.addEvent('mouseout', this.handleMouseOut, this._displayObject); - interaction.addEvent('mousedown', this.handleMouseDown, this._displayObject); - interaction.addEvent('mouseup', this.handleMouseUp, this._displayObject); + this.addEvent('mouseover', this.handleMouseOver, this._displayObject); + this.addEvent('mouseout', this.handleMouseOut, this._displayObject); + this.addEvent('mousedown', this.handleMouseDown, this._displayObject); + this.addEvent('mouseup', this.handleMouseUp, this._displayObject); } render() { diff --git a/src/time-graph-state-controller.ts b/src/time-graph-state-controller.ts index 3e3ddbb..bd02340 100644 --- a/src/time-graph-state-controller.ts +++ b/src/time-graph-state-controller.ts @@ -1,15 +1,15 @@ -import { TimeGraphInteraction } from "./time-graph-interaction"; +import { TimeGraphUnitController } from "./time-graph-unit-controller"; export class TimeGraphStateController { - protected _originalGraphWidth: number; - protected _zoomFactor: number; - protected _initialZoomFactor: number; - protected _oldZoomFactor: number; - protected _positionOffset: { + oldPositionOffset: { x: number; y: number; }; - protected _oldPositionOffset: { + canvasWidth: number; + + protected _zoomFactor: number; + protected _initialZoomFactor: number; + protected _positionOffset: { x: number; y: number; }; @@ -17,23 +17,13 @@ export class TimeGraphStateController { protected zoomChangedHandler: (() => void)[]; protected positionChangedHandler: (() => void)[]; - protected _timeGraphInteraction: TimeGraphInteraction; - - constructor(protected _canvasWidth: number, protected _graphWidth: number) { - this._originalGraphWidth = _graphWidth; - this._initialZoomFactor = _canvasWidth / _graphWidth; - this._graphWidth = this._originalGraphWidth * this._initialZoomFactor; - this._zoomFactor = this._initialZoomFactor; - this._oldZoomFactor = this._zoomFactor; + constructor(protected canvas: HTMLCanvasElement, protected unitController: TimeGraphUnitController) { + this.canvasWidth = canvas.width; + this._initialZoomFactor = this.zoomFactor; this._positionOffset = { x: 0, y: 0 }; - this._oldPositionOffset = { x: 0, y: 0 }; + this.oldPositionOffset = { x: 0, y: 0 }; this.zoomChangedHandler = []; this.positionChangedHandler = []; - this._timeGraphInteraction = new TimeGraphInteraction(this); - } - - get timeGraphInteraction(): TimeGraphInteraction { - return this._timeGraphInteraction; } protected handleZoomChange() { @@ -50,38 +40,17 @@ export class TimeGraphStateController { this.positionChangedHandler.push(handler); } - get canvasWidth(): number { - return this._canvasWidth; - } - set canvasWidth(value: number) { - this._canvasWidth = value; - } - - get graphWidth(): number { - return this._graphWidth; - } - set graphWidth(value: number) { - this._graphWidth = value; - } - get initialZoomFactor(): number { return this._initialZoomFactor; } - get zoomFactor(): number { + get zoomFactor(): number{ + this._zoomFactor = this.canvas.width / this.unitController.viewRangeLength; return this._zoomFactor; } - set zoomFactor(value: number) { - this._zoomFactor = value; - this._graphWidth = this._zoomFactor * this._originalGraphWidth; - this.handleZoomChange(); - } - get oldZoomFactor(): number { - return this._oldZoomFactor; - } - set oldZoomFactor(value: number) { - this._oldZoomFactor = value; + get absoluteResolution(): number { + return this.canvasWidth / this.unitController.absoluteRange; } get positionOffset(): { @@ -97,17 +66,4 @@ export class TimeGraphStateController { this._positionOffset = value; this.handlePositionChange(); } - - get oldPositionOffset(): { - x: number; - y: number; - } { - return this._oldPositionOffset; - } - set oldPositionOffset(value: { - x: number; - y: number; - }) { - this._oldPositionOffset = value; - } } \ No newline at end of file diff --git a/src/time-graph-unit-controller.ts b/src/time-graph-unit-controller.ts new file mode 100644 index 0000000..b3c4282 --- /dev/null +++ b/src/time-graph-unit-controller.ts @@ -0,0 +1,38 @@ +import { TimeGraphRange } from "./time-graph-model"; + +export class TimeGraphUnitController { + protected viewRangeChangedHandler: ((newRange: TimeGraphRange) => void)[]; + + protected _viewRange: TimeGraphRange; + + constructor(public absoluteRange: number, viewRange?: TimeGraphRange) { + this.viewRangeChangedHandler = []; + + this._viewRange = viewRange || { start: 0, end: absoluteRange }; + } + + protected handleViewRangeChange() { + this.viewRangeChangedHandler.map(handler => handler(this._viewRange)); + } + + onViewRangeChanged(handler: (viewRange: TimeGraphRange) => void) { + this.viewRangeChangedHandler.push(handler); + } + + get viewRange(): TimeGraphRange { + return this._viewRange; + } + set viewRange(newRange: TimeGraphRange) { + const greaterThanAbsoluteRange = (newRange.start + this.viewRangeLength) > this.absoluteRange; + if (newRange.end > newRange.start && !greaterThanAbsoluteRange) { + this._viewRange = newRange; + } else if(greaterThanAbsoluteRange) { + this._viewRange = {start: newRange.start, end: (this.absoluteRange - newRange.start)}; + } + this.handleViewRangeChange(); + } + + get viewRangeLength(): number { + return this._viewRange.end - this._viewRange.start; + } +} diff --git a/src/time-graph.ts b/src/time-graph.ts deleted file mode 100644 index fc77ca3..0000000 --- a/src/time-graph.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { TimeGraphAxis } from "./time-graph-axis"; -import { TimeGraphChart } from "./time-graph-chart"; -import { TimeGraphStateController } from "./time-graph-state-controller"; -import { TimeGraphModel } from "./time-graph-model"; -import { TimeGraphContainer } from "./time-graph-container"; - -export class TimeGraph { - - protected container?: HTMLElement; - protected axisContainer: HTMLElement; - protected chartContainer: HTMLElement; - - protected tgContainers: TimeGraphContainer[]; - - protected _controller: TimeGraphStateController - - constructor(containerId: string, timeGraphModel: TimeGraphModel) { - this.container = document.getElementById(containerId) || undefined; - if (!this.container) { - throw (`No container with id ${containerId} available.`); - } - this.container.innerHTML = ''; - - this.axisContainer = document.createElement('div'); - this.axisContainer.id = containerId + '_axis'; - this.container.appendChild(this.axisContainer); - - this.chartContainer = document.createElement('div'); - this.chartContainer.id = containerId + '_chart'; - this.container.appendChild(this.chartContainer); - - this.tgContainers = []; - - this._controller = new TimeGraphStateController(this.container.clientWidth, timeGraphModel.range.end); - - this._controller.onZoomChanged(() => { - this.tgContainers.map(c => c.update()); - }); - this._controller.onPositionChanged(() => { - this.tgContainers.map(c => c.update()); - }); - } - - get controller(): TimeGraphStateController { - return this._controller; - } - - set timeGraphAxis(tga: TimeGraphAxis) { - this.axisContainer.innerHTML = ''; - this.tgContainers.push(tga); - this.axisContainer.appendChild(tga.canvas); - } - - set timeGraphChart(tgc: TimeGraphChart) { - this.chartContainer.innerHTML = ''; - this.tgContainers.push(tgc); - this.chartContainer.appendChild(tgc.canvas); - } -} \ No newline at end of file