Skip to content

Commit

Permalink
fix(zoom): Maintain zoommed scale on .zoom()
Browse files Browse the repository at this point in the history
- Make zoom scale consistent
- Reset transform value for .unzoom()

Fix #654
Close #702
  • Loading branch information
netil authored Dec 13, 2018
1 parent 2edb82a commit 7b7a274
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 28 deletions.
66 changes: 61 additions & 5 deletions spec/interactions/zoom-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,6 @@ describe("ZOOM", function() {
const main = chart.internal.main;
const eventRect = main.select(`.${CLASS.eventRect}-2`).node();

util.fireEvent(eventRect, "mousedown", {
clientX: 100,
clientY: 150
}, chart);

new Promise((resolve, reject) => {
util.fireEvent(eventRect, "mousedown", {
clientX: 100,
Expand Down Expand Up @@ -384,4 +379,65 @@ describe("ZOOM", function() {
}, 500);
});
});

describe ("zoom scale consistency for dragging", () => {
before(() => {
args = {
data: {
columns: [
["data1", 30, 200, 100, 400, 3150, 250],
["data2", 50, 20, 10, 40, 15, 6025]
]
},
zoom: {
enabled: true
}
};
});

it("zoom scale should maintained on dragging interaction", done => {
const internal = chart.internal;
const main = internal.main;
const zoomDomain = [0,2];

// when
chart.zoom(zoomDomain);

const eventRect = main.select(`.${CLASS.eventRect}-2`).node();
const zoomedDomain = internal.x.domain();

expect(zoomedDomain).to.be.deep.equal(zoomDomain);

new Promise((resolve, reject) => {
util.fireEvent(eventRect, "mousedown", {
clientX: 100,
clientY: 150
}, chart);

resolve();
}).then(() => {
return new Promise((resolve, reject) => {
setTimeout(() => {
util.fireEvent(eventRect, "mousemove", {
clientX: 130,
clientY: 150
}, chart);

resolve();
}, 500);
});
}).then(() => {
setTimeout(() => {
util.fireEvent(eventRect, "mouseup", {
clientX: 150,
clientY: 150
}, chart);

expect(internal.x.domain()).to.be.deep.equal(zoomedDomain);

done();
}, 500);
});
});
});
});
28 changes: 19 additions & 9 deletions src/api/api.zoom.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
/**
* Copyright (c) 2017 NAVER Corp.
* Copyright (c) 2017 ~ present NAVER Corp.
* billboard.js project is licensed under the MIT license
*/
import {
min as d3Min,
max as d3Max
} from "d3-array";
import {zoomIdentity as d3ZoomIdentity} from "d3-zoom";
import {zoomIdentity as d3ZoomIdentity, zoomTransform as d3ZoomTransform} from "d3-zoom";
import Chart from "../internals/Chart";
import CLASS from "../config/classes";
import {callFn, isDefined, isObject, isString, extend} from "../internals/util";

/**
* Zoom by giving x domain.
* - **NOTE:** For `wheel` type zoom, the minimum zoom range will be set as the given domain.<br>
* To get the initial state, [.unzoom()](#unzoom) should be called.
* @method zoom
* @instance
* @memberOf Chart
* @memberof Chart
* @param {Array} domainValue If domain is given, the chart will be zoomed to the given domain. If no argument is given, the current zoomed domain will be returned.
* @return {Array} domain value in array
* @example
Expand Down Expand Up @@ -70,7 +73,7 @@ extend(zoom, {
* Enable and disable zooming.
* @method zoom․enable
* @instance
* @memberOf Chart
* @memberof Chart
* @param {String|Boolean} enabled Possible string values are "wheel" or "drag". If enabled is true, "wheel" will be used. If false is given, zooming will be disabled.<br>When set to false, the current zooming status will be reset.
* @example
* // Enable zooming using the mouse wheel
Expand Down Expand Up @@ -111,7 +114,7 @@ extend(zoom, {
* Set or get x Axis maximum zoom range value
* @method zoom․max
* @instance
* @memberOf Chart
* @memberof Chart
* @param {Number} [max] maximum value to set for zoom
* @return {Number} zoom max value
* @example
Expand All @@ -133,7 +136,7 @@ extend(zoom, {
* Set or get x Axis minimum zoom range value
* @method zoom․min
* @instance
* @memberOf Chart
* @memberof Chart
* @param {Number} [min] minimum value tp set for zoom
* @return {Number} zoom min value
* @example
Expand All @@ -155,7 +158,7 @@ extend(zoom, {
* Set zoom range
* @method zoom․range
* @instance
* @memberOf Chart
* @memberof Chart
* @param {Object} [range]
* @return {Object} zoom range value
* {
Expand Down Expand Up @@ -190,7 +193,7 @@ extend(Chart.prototype, {
* Unzoom zoomed area
* @method unzoom
* @instance
* @memberOf Chart
* @memberof Chart
* @example
* chart.unzoom();
*/
Expand All @@ -203,9 +206,16 @@ extend(Chart.prototype, {
$$.brush.getSelection().call($$.brush.move, null) :
$$.zoom.updateTransformScale(d3ZoomIdentity);

$$.updateZoom();
$$.updateZoom(true);
$$.zoom.resetBtn && $$.zoom.resetBtn.style("display", "none");

// reset transform
const eventRects = $$.main.select(`.${CLASS.eventRects}`);

if (d3ZoomTransform(eventRects.node()) !== d3ZoomIdentity) {
$$.zoom.transform(eventRects, d3ZoomIdentity);
}

$$.redraw({
withTransition: true,
withY: config.zoom_rescale
Expand Down
37 changes: 23 additions & 14 deletions src/interactions/zoom.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2017 NAVER Corp.
* Copyright (c) 2017 ~ present NAVER Corp.
* billboard.js project is licensed under the MIT license
*/
import {
Expand Down Expand Up @@ -40,12 +40,14 @@ extend(ChartInternal.prototype, {

$$.redrawEventRect();

const eventRects = $$.main.select(`.${CLASS.eventRects}`);

if (zoomEnabled && bind) {
$$.bindZoomOnEventRect(zoomEnabled.type);
$$.bindZoomOnEventRect(eventRects, zoomEnabled.type);
} else if (bind === false) {
$$.api.unzoom();

$$.main.select(`.${CLASS.eventRects}`)
eventRects
.on(".zoom", null)
.on(".drag", null);
}
Expand Down Expand Up @@ -87,7 +89,7 @@ extend(ChartInternal.prototype, {
*/
zoom.updateTransformScale = transform => {
// rescale from the original scale
const newScale = transform.rescaleX($$.subX.orgScale());
const newScale = transform.rescaleX($$.x);
const domain = $$.trimXDomain(newScale.domain());
const rescale = config.zoom_rescale;

Expand All @@ -110,6 +112,10 @@ extend(ChartInternal.prototype, {
const $$ = this;
const event = d3Event.sourceEvent;

if (!event) {
return;
}

$$.zoom.altDomain = event.altKey ?
$$.x.orgDomain() : null;

Expand All @@ -126,7 +132,7 @@ extend(ChartInternal.prototype, {
const config = $$.config;
const event = d3Event;

if (!config.zoom_enabled) {
if (!config.zoom_enabled || !event.sourceEvent) {
return;
}

Expand Down Expand Up @@ -170,7 +176,9 @@ extend(ChartInternal.prototype, {
const startEvent = $$.zoom.startEvent;

// if click, do nothing. otherwise, click interaction will be canceled.
if (event && startEvent.clientX === event.clientX && startEvent.clientY === event.clientY) {
if (!startEvent ||
(event && startEvent.clientX === event.clientX && startEvent.clientY === event.clientY)
) {
return;
}

Expand All @@ -182,8 +190,8 @@ extend(ChartInternal.prototype, {

/**
* Get zoom domain
* @private
* @returns {Array} zoom domain
* @private
*/
getZoomDomain() {
const $$ = this;
Expand All @@ -196,21 +204,22 @@ extend(ChartInternal.prototype, {

/**
* Update zoom
* @param {Boolean} force Force unzoom
* @private
*/
updateZoom() {
updateZoom(force) {
const $$ = this;

if ($$.zoomScale) {
const zoomDomain = $$.zoomScale.domain();
const xDomain = $$.subX.domain();
const delta = 0.015; // arbitrary value

const isfullyShown = (zoomDomain[0] <= xDomain[0] || (zoomDomain[0] - delta) <= xDomain[0]) &&
(xDomain[1] <= zoomDomain[1] || xDomain[1] <= (zoomDomain[1] - delta));

// check if the zoomed chart is fully shown, then reset scale when zoom is out as initial
if (
(zoomDomain[0] <= xDomain[0] || (zoomDomain[0] - delta) <= xDomain[0]) &&
(xDomain[1] <= zoomDomain[1] || xDomain[1] <= (zoomDomain[1] - delta))
) {
if (force || isfullyShown) {
$$.xAxis.scale($$.subX);
$$.x.domain($$.subX.orgDomain());
$$.zoomScale = null;
Expand All @@ -222,11 +231,11 @@ extend(ChartInternal.prototype, {
* Attach zoom event on <rect>
* @private
*/
bindZoomOnEventRect(type) {
bindZoomOnEventRect(eventRects, type) {
const $$ = this;
const behaviour = type === "drag" ? $$.zoomBehaviour : $$.zoom;

$$.main.select(`.${CLASS.eventRects}`)
eventRects
.call(behaviour)
.on("dblclick.zoom", null);
},
Expand Down

0 comments on commit 7b7a274

Please sign in to comment.