@devexperts/dxcharts-lite
Version:
118 lines (117 loc) • 6.12 kB
JavaScript
/*
* Copyright (C) 2019 - 2025 Devexperts Solutions IE Limited
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { ChartBaseElement } from '../../model/chart-base-element';
import { CanvasElement } from '../../canvas/canvas-bounds-container';
import { DragNDropXComponent } from '../dran-n-drop_helper/drag-n-drop-x.component';
// if you drag full X width from left to right - you will have x3 zoom, and vice-versa
const FULL_X_WIDTH_ZOOM_FACTOR = 3;
/**
* Handles the mouse drag on X axis - to zoom the viewport horizontally.
* @doc-tags scaling,zoom,viewport
*/
export class XAxisScaleHandler extends ChartBaseElement {
constructor(scale, canvasInputListener, canvasBoundsContainer, chartPanComponent, chartModel, hitTest, hitTestCanvasModel) {
super();
this.scale = scale;
this.canvasInputListener = canvasInputListener;
this.canvasBoundsContainer = canvasBoundsContainer;
this.chartPanComponent = chartPanComponent;
this.chartModel = chartModel;
this.hitTest = hitTest;
this.hitTestCanvasModel = hitTestCanvasModel;
this.lastXStart = 0;
this.lastXEnd = 0;
this.lastXWidth = 0;
this.lastXPxWidth = 0;
this.onXDragStart = () => {
this.lastXStart = this.scale.xStart;
this.lastXEnd = this.scale.xEnd;
this.lastXWidth = this.scale.xEnd - this.scale.xStart;
const bounds = this.canvasBoundsContainer.getBounds(CanvasElement.X_AXIS);
this.lastXPxWidth = bounds.width - this.canvasInputListener.currentPoint.x;
// Stop redrawing hit test
this.hitTestCanvasModel.hitTestDrawersPredicateSubject.next(false);
};
this.onXDragTick = (dragInfo) => {
let { delta: absoluteXDelta } = dragInfo;
// mouse click or single tap drag
if (!this.touches || this.touches.length === 1) {
const newPxWidth = this.lastXPxWidth - absoluteXDelta;
// cursor goes beyond x-axis - maximum scale is reached
if (newPxWidth < 0) {
return;
}
const xZoomMult = this.lastXPxWidth / newPxWidth;
const newWidth = this.lastXWidth * xZoomMult;
const newXStart = this.lastXStart + (this.lastXWidth - newWidth);
this.scale.setXScale(newXStart, this.scale.xEnd);
return;
}
// if multitouch - take the first two touch events and apply delta the following way:
// the touch on the left keeps the initial delta because left => right gesture
// the touch on the right has the reversed delta because the gesture is right => left
if (this.touches.length > 1) {
const left = Math.min(this.touches[0].clientX, this.touches[1].clientX);
absoluteXDelta = left === this.touches[0].clientX ? absoluteXDelta : -absoluteXDelta;
let xZoomMult;
if (absoluteXDelta < 0) {
xZoomMult = 1 / (1 + (-absoluteXDelta / this.lastXPxWidth) * (FULL_X_WIDTH_ZOOM_FACTOR - 1));
}
else {
xZoomMult = 1 + (absoluteXDelta / this.lastXPxWidth) * (FULL_X_WIDTH_ZOOM_FACTOR - 1);
}
const newXWidth = this.lastXWidth * xZoomMult;
const delta = (newXWidth - this.lastXWidth) / 2;
const newXStart = this.lastXStart - delta;
const newXEnd = this.lastXEnd + delta;
if (this.lastXStart !== newXStart || this.lastXEnd !== newXEnd) {
this.scale.setXScale(newXStart, newXEnd);
}
}
};
this.onXDragEnd = () => {
// Continue redrawing hit test
this.hitTestCanvasModel.hitTestDrawersPredicateSubject.next(true);
};
this.setDblTapCallback = (cb) => (this.dblTapCallback = cb);
this.setDblClickCallback = (cb) => (this.dblClickCallback = cb);
this.dblClickCallback = () => chartModel.doBasicScale();
this.dblTapCallback = () => chartModel.doBasicScale();
const dragNDropXComponent = new DragNDropXComponent(hitTest, {
onDragStart: this.onXDragStart,
onDragTick: this.onXDragTick,
onDragEnd: this.onXDragEnd,
}, this.canvasInputListener, this.chartPanComponent, {
dragPredicate: () => chartPanComponent.chartAreaPanHandler.chartPanningOptions.horizontal,
});
this.addChildEntity(dragNDropXComponent);
}
/**
* This method is used to activate the canvas input listener and add a subscription to it.
* It calls the parent class's doActivate method and then subscribes to the canvasInputListener's observeDbClick method.
* The subscription is added to the RxJS subscription list.
* When the subscription is triggered, the doBasicScale method is called.
* @protected
* @returns {void}
*/
doActivate() {
super.doActivate();
this.addRxSubscription(this.canvasInputListener.observeDbClick(this.hitTest).subscribe(() => this.dblClickCallback()));
this.addRxSubscription(this.canvasInputListener.observeDbTap(this.hitTest).subscribe(() => {
var _a;
// apply dbl tap only if single finger taps are made
if (this.touches && ((_a = this.touches) === null || _a === void 0 ? void 0 : _a.length) > 1) {
this.touches = undefined;
return;
}
this.dblTapCallback();
}));
this.addRxSubscription(this.canvasInputListener.observeTouchStart(this.hitTest).subscribe(e => {
this.touches = e.touches;
}));
this.addRxSubscription(this.chartModel.candlesPrependSubject.subscribe(({ prependedCandlesWidth }) => (this.lastXStart += prependedCandlesWidth)));
}
}