diff --git a/client/app/scripts/charts/nodes-chart.js b/client/app/scripts/charts/nodes-chart.js
index e200efe1f..59ffb2839 100644
--- a/client/app/scripts/charts/nodes-chart.js
+++ b/client/app/scripts/charts/nodes-chart.js
@@ -3,12 +3,15 @@ import { connect } from 'react-redux';
import NodesChartElements from './nodes-chart-elements';
import ZoomableCanvas from '../components/zoomable-canvas';
+import { transformToString } from '../utils/transform-utils';
import { clickBackground } from '../actions/app-actions';
import {
- graphZoomLimitsSelector,
+ graphLimitsSelector,
graphZoomStateSelector,
} from '../selectors/graph-view/zoom';
+import { CONTENT_INCLUDED } from '../constants/naming';
+
const EdgeMarkerDefinition = ({ selectedNodeId }) => {
const markerOffset = selectedNodeId ? '35' : '40';
@@ -43,17 +46,25 @@ class NodesChart extends React.Component {
}
}
+ renderContent(transform) {
+ return (
+
+
+
+
+ );
+ }
+
render() {
- const { selectedNodeId } = this.props;
return (
{transform => this.renderLayers(transform)}
diff --git a/client/app/scripts/components/time-travel-timeline.js b/client/app/scripts/components/time-travel-timeline.js
index 71fd32229..fc427fbb4 100644
--- a/client/app/scripts/components/time-travel-timeline.js
+++ b/client/app/scripts/components/time-travel-timeline.js
@@ -168,7 +168,7 @@ class TimeTravelTimeline extends React.Component {
}
handleZoom(ev) {
- let durationPerPixel = scaleDuration(this.state.durationPerPixel, zoomFactor(ev));
+ let durationPerPixel = scaleDuration(this.state.durationPerPixel, 1 / zoomFactor(ev));
if (durationPerPixel > MAX_DURATION_PER_PX) durationPerPixel = MAX_DURATION_PER_PX;
if (durationPerPixel < MIN_DURATION_PER_PX) durationPerPixel = MIN_DURATION_PER_PX;
diff --git a/client/app/scripts/components/zoomable-canvas.js b/client/app/scripts/components/zoomable-canvas.js
index 205b4f2c1..2616c966f 100644
--- a/client/app/scripts/components/zoomable-canvas.js
+++ b/client/app/scripts/components/zoomable-canvas.js
@@ -1,15 +1,17 @@
import React from 'react';
+import classNames from 'classnames';
import { connect } from 'react-redux';
-import { debounce, pick } from 'lodash';
+import { clamp, debounce, pick } from 'lodash';
import { fromJS } from 'immutable';
+import { drag } from 'd3-drag';
import { event as d3Event, select } from 'd3-selection';
-import { zoom, zoomIdentity } from 'd3-zoom';
import Logo from '../components/logo';
import ZoomControl from '../components/zoom-control';
import { cacheZoomState } from '../actions/app-actions';
-import { transformToString } from '../utils/transform-utils';
+import { zoomFactor } from '../utils/zoom-utils';
+import { applyTransform, inverseTransform } from '../utils/transform-utils';
import { activeTopologyZoomCacheKeyPathSelector } from '../selectors/zooming';
import {
canvasMarginsSelector,
@@ -18,6 +20,7 @@ import {
} from '../selectors/canvas';
import { ZOOM_CACHE_DEBOUNCE_INTERVAL } from '../constants/timer';
+import { CONTENT_INCLUDED, CONTENT_COVERING } from '../constants/naming';
class ZoomableCanvas extends React.Component {
@@ -25,10 +28,11 @@ class ZoomableCanvas extends React.Component {
super(props, context);
this.state = {
- minTranslateX: 0,
- maxTranslateX: 0,
- minTranslateY: 0,
- maxTranslateY: 0,
+ isPanning: false,
+ contentMinX: 0,
+ contentMaxX: 0,
+ contentMinY: 0,
+ contentMaxY: 0,
translateX: 0,
translateY: 0,
minScale: 1,
@@ -40,27 +44,33 @@ class ZoomableCanvas extends React.Component {
this.debouncedCacheZoom = debounce(this.cacheZoom.bind(this), ZOOM_CACHE_DEBOUNCE_INTERVAL);
this.handleZoomControlAction = this.handleZoomControlAction.bind(this);
this.canChangeZoom = this.canChangeZoom.bind(this);
- this.zoomed = this.zoomed.bind(this);
+
+ this.handleZoom = this.handleZoom.bind(this);
+ this.handlePanStart = this.handlePanStart.bind(this);
+ this.handlePanEnd = this.handlePanEnd.bind(this);
+ this.handlePan = this.handlePan.bind(this);
}
componentDidMount() {
+ this.svg = select('.zoomable-canvas svg');
+ this.drag = drag()
+ .on('start', this.handlePanStart)
+ .on('end', this.handlePanEnd)
+ .on('drag', this.handlePan);
+ this.svg.call(this.drag);
+
this.zoomRestored = false;
- this.zoom = zoom().on('zoom', this.zoomed);
- this.svg = select('svg#canvas');
- this.setZoomTriggers(!this.props.disabled);
this.updateZoomLimits(this.props);
this.restoreZoomState(this.props);
}
componentWillUnmount() {
- this.setZoomTriggers(false);
this.debouncedCacheZoom.cancel();
}
componentWillReceiveProps(nextProps) {
const layoutChanged = nextProps.layoutId !== this.props.layoutId;
- const disabledChanged = nextProps.disabled !== this.props.disabled;
// If the layout has changed (either active topology or its options) or
// relayouting has been requested, stop pending zoom caching event and
@@ -70,11 +80,6 @@ class ZoomableCanvas extends React.Component {
this.zoomRestored = false;
}
- // If the zooming has been enabled/disabled, update its triggers.
- if (disabledChanged) {
- this.setZoomTriggers(!nextProps.disabled);
- }
-
this.updateZoomLimits(nextProps);
if (!this.zoomRestored) {
this.restoreZoomState(nextProps);
@@ -82,31 +87,25 @@ class ZoomableCanvas extends React.Component {
}
handleZoomControlAction(scale) {
- // Update the canvas scale (not touching the translation).
- this.svg.call(this.zoom.scaleTo, scale);
-
- // Update the scale state and propagate to the global cache.
- this.setState(this.cachableState({
- scaleX: scale,
- scaleY: scale,
- }));
- this.debouncedCacheZoom();
+ // Get the center of the SVG and zoom around it.
+ const { top, bottom, left, right } = this.svg.node().getBoundingClientRect();
+ const centerOfCanvas = {
+ x: (left + right) / 2,
+ y: (top + bottom) / 2,
+ };
+ // Zoom factor diff is obtained by dividing the new zoom scale with the old one.
+ this.zoomAtPositionByFactor(centerOfCanvas, scale / this.state.scaleX);
}
render() {
- // `forwardTransform` says whether the zoom transform is forwarded to the child
- // component. The advantage of that is more control rendering control in the
- // children, while the disadvantage is that it's slower, as all the children
- // get updated on every zoom/pan action.
- const { children, forwardTransform } = this.props;
- const transform = forwardTransform ? '' : transformToString(this.state);
+ const className = classNames({ panning: this.state.isPanning });
return (
-