From f4bab88cf34380c952e0a3d74644cc0a0df12585 Mon Sep 17 00:00:00 2001 From: Anton Vorobev Date: Mon, 28 Oct 2024 16:04:50 +0000 Subject: [PATCH] Pull request #5315: Feature/DXCF-5652 adaptive x axis time axis pinch gesture improvement Merge in DXCHARTS/dxchart5 from feature/DXCF-5652-adaptive-x-axis-time-axis-pinch-gesture-improvement to master * commit '8e0df5df2c99a80041e65b994b7635710b48e769': [DXCF-5652] [Adaptive] X-axis (time axis) pinch gesture improvement // init [DXCF-5652] [Adaptive] X-axis (time axis) pinch gesture improvement // init [DXCF-5652] [Adaptive] X-axis (time axis) pinch gesture improvement // init [DXCF-5652] [Adaptive] X-axis (time axis) pinch gesture improvement // init [DXCF-5652] [Adaptive] X-axis (time axis) pinch gesture improvement // init GitOrigin-RevId: 533ffd644902c4e3a8cfc7763e891cbbe2b2ff96 --- src/chart/canvas/canvas-bounds-container.ts | 6 ++- .../types/cross-and-labels.drawer.ts | 6 +-- .../x_axis/x-axis-draw.functions.ts | 4 +- .../components/x_axis/x-axis-scale.handler.ts | 49 ++++++++++++++++--- .../x_axis/x-axis-time-labels.drawer.ts | 4 +- 5 files changed, 53 insertions(+), 16 deletions(-) diff --git a/src/chart/canvas/canvas-bounds-container.ts b/src/chart/canvas/canvas-bounds-container.ts index f68d13f..7ab2269 100644 --- a/src/chart/canvas/canvas-bounds-container.ts +++ b/src/chart/canvas/canvas-bounds-container.ts @@ -60,6 +60,9 @@ const N_MAP_BUTTON_W = 15; const KNOTS_W_MOBILE_MULTIPLIER = 1.5; const N_MAP_KNOT_W = isMobile() ? 8 * KNOTS_W_MOBILE_MULTIPLIER : 8; +// additional x axis height padding for mobiles, used for better usability on mobile devices +export const X_AXIS_MOBILE_PADDING = isMobile() ? 5 : 0; + /** * we need to check that: heightRatios - 1 < 0.000001 after calculations between decimals */ @@ -466,7 +469,8 @@ export class CanvasBoundsContainer { this.xAxisHeight = fontHeight + (this.config.components.xAxis.padding.top ?? 0) + - (this.config.components.xAxis.padding.bottom ?? 0); + (this.config.components.xAxis.padding.bottom ?? 0) + + X_AXIS_MOBILE_PADDING; } return this.xAxisHeight; } diff --git a/src/chart/components/cross_tool/types/cross-and-labels.drawer.ts b/src/chart/components/cross_tool/types/cross-and-labels.drawer.ts index dc3639c..e756f30 100644 --- a/src/chart/components/cross_tool/types/cross-and-labels.drawer.ts +++ b/src/chart/components/cross_tool/types/cross-and-labels.drawer.ts @@ -3,7 +3,7 @@ * 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 { CanvasBoundsContainer, CanvasElement } from '../../../canvas/canvas-bounds-container'; +import { CanvasBoundsContainer, CanvasElement, X_AXIS_MOBILE_PADDING } from '../../../canvas/canvas-bounds-container'; import { FullChartConfig } from '../../../chart.config'; import { avoidAntialiasing, drawRoundedRect } from '../../../utils/canvas/canvas-drawing-functions.utils'; import { PaneManager } from '../../pane/pane-manager.component'; @@ -112,12 +112,12 @@ export class CrossAndLabelsDrawerType implements CrossToolTypeDrawer { const boxWidth = width + xLabelPaddingLeft + xLabelPaddingRight; const boxHeight = fontHeight + xLabelPaddingTop + xLabelPaddingBottom; const xBoxPos = Math.max(x - boxWidth / 2, 0); - const yBoxPos = xAxis.y + xLabelMarginTop; + const yBoxPos = xAxis.y + xLabelMarginTop + X_AXIS_MOBILE_PADDING / 2; drawRoundedRect(ctx, xBoxPos, yBoxPos, boxWidth, boxHeight); // label ctx.fillStyle = crossToolColors.labelTextColor; const xTextPos = Math.max(x - width / 2, xLabelPaddingLeft); - const yTextPos = xAxis.y + xLabelMarginTop + fontHeight + xLabelPaddingTop - 1; // -1 for vertical adjustment + const yTextPos = xAxis.y + xLabelMarginTop + fontHeight + X_AXIS_MOBILE_PADDING / 2 + xLabelPaddingTop - 1; // -1 for vertical adjustment ctx.fillText(labelText, xTextPos, yTextPos); ctx.restore(); } diff --git a/src/chart/components/x_axis/x-axis-draw.functions.ts b/src/chart/components/x_axis/x-axis-draw.functions.ts index 623c824..ff996d8 100644 --- a/src/chart/components/x_axis/x-axis-draw.functions.ts +++ b/src/chart/components/x_axis/x-axis-draw.functions.ts @@ -3,7 +3,7 @@ * 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 { CanvasBoundsContainer, CanvasElement } from '../../canvas/canvas-bounds-container'; +import { CanvasBoundsContainer, CanvasElement, X_AXIS_MOBILE_PADDING } from '../../canvas/canvas-bounds-container'; import { FullChartConfig } from '../../chart.config'; import { XAxisLabel } from './x-axis-labels.model'; @@ -63,7 +63,7 @@ export function drawXAxisLabel( ctx.fillStyle = label.color; const xTextPos = boxStart + DEFAULT_X_LABEL_PADDING.x; - const yTextPos = xAxisBounds.y + offsetTop + fontSize; // -2 for vertical adjustment + const yTextPos = xAxisBounds.y + offsetTop + fontSize + X_AXIS_MOBILE_PADDING / 2; ctx.fillText(label.text, xTextPos, yTextPos); ctx.restore(); } diff --git a/src/chart/components/x_axis/x-axis-scale.handler.ts b/src/chart/components/x_axis/x-axis-scale.handler.ts index 90f8c74..d70943f 100644 --- a/src/chart/components/x_axis/x-axis-scale.handler.ts +++ b/src/chart/components/x_axis/x-axis-scale.handler.ts @@ -14,12 +14,16 @@ import { DragNDropXComponent } from '../dran-n-drop_helper/drag-n-drop-x.compone import { ChartPanComponent } from '../pan/chart-pan.component'; import { HitTestCanvasModel } from '../../model/hit-test-canvas.model'; +// 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 { lastXStart: Unit = 0; + lastXEnd: Unit = 0; lastXWidth: Unit = 0; lastXPxWidth: Pixel = 0; @@ -97,6 +101,7 @@ export class XAxisScaleHandler extends ChartBaseElement { private 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; @@ -105,16 +110,44 @@ export class XAxisScaleHandler extends ChartBaseElement { }; private onXDragTick = (dragInfo: DragInfo) => { - const { delta: absoluteXDelta } = dragInfo; - const newPxWidth = this.lastXPxWidth - absoluteXDelta; - // cursor goes beyond x-axis - maximum scale is reached - if (newPxWidth < 0) { + 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; } - const xZoomMult = this.lastXPxWidth / newPxWidth; - const newWidth = this.lastXWidth * xZoomMult; - const newXStart = this.lastXStart + (this.lastXWidth - newWidth); - this.scale.setXScale(newXStart, this.scale.xEnd); + + // 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); + } + } }; private onXDragEnd = () => { diff --git a/src/chart/components/x_axis/x-axis-time-labels.drawer.ts b/src/chart/components/x_axis/x-axis-time-labels.drawer.ts index 37ac945..4084ed6 100644 --- a/src/chart/components/x_axis/x-axis-time-labels.drawer.ts +++ b/src/chart/components/x_axis/x-axis-time-labels.drawer.ts @@ -6,7 +6,7 @@ import { NumericAxisLabel } from '../labels_generator/numeric-axis-labels.generator'; import { Bounds } from '../../model/bounds.model'; import { FullChartConfig } from '../../chart.config'; -import { CanvasBoundsContainer, CanvasElement } from '../../canvas/canvas-bounds-container'; +import { CanvasBoundsContainer, CanvasElement, X_AXIS_MOBILE_PADDING } from '../../canvas/canvas-bounds-container'; import { CanvasModel } from '../../model/canvas.model'; import { Drawer } from '../../drawers/drawing-manager'; import { ViewportModel } from '../../model/scaling/viewport.model'; @@ -46,7 +46,7 @@ export class XAxisTimeLabelsDrawer implements Drawer { const color = this.config.colors.xAxis.labelTextColor; const labels = this.labelsProvider(); - this.drawLabels(ctx, labels, bounds, color, fontHeight, fontFamily, offsetTop); + this.drawLabels(ctx, labels, bounds, color, fontHeight, fontFamily, offsetTop + X_AXIS_MOBILE_PADDING / 2); ctx.restore(); } }