Files
T-DAS/Epost.TestToolsWeb/Content/code/es-modules/parts/TouchPointer.js
2023-01-13 15:30:20 +08:00

405 lines
12 KiB
JavaScript

/**
* (c) 2010-2018 Torstein Honsi
*
* License: www.highcharts.com/license
*/
'use strict';
import H from './Globals.js';
import './Utilities.js';
var charts = H.charts,
extend = H.extend,
noop = H.noop,
pick = H.pick,
Pointer = H.Pointer;
// Support for touch devices
extend(Pointer.prototype, /** @lends Pointer.prototype */ {
/**
* Run translation operations
*
* @private
* @function Highcharts.Pointer#pinchTranslate
*
* @param {Array<*>} pinchDown
*
* @param {Array<*>} touches
*
* @param {*} transform
*
* @param {*} selectionMarker
*
* @param {*} clip
*
* @param {*} lastValidTouch
*/
pinchTranslate: function (
pinchDown,
touches,
transform,
selectionMarker,
clip,
lastValidTouch
) {
if (this.zoomHor) {
this.pinchTranslateDirection(
true,
pinchDown,
touches,
transform,
selectionMarker,
clip,
lastValidTouch
);
}
if (this.zoomVert) {
this.pinchTranslateDirection(
false,
pinchDown,
touches,
transform,
selectionMarker,
clip,
lastValidTouch
);
}
},
/**
* Run translation operations for each direction (horizontal and vertical)
* independently.
*
* @private
* @function Highcharts.Pointer#pinchTranslateDirection
*
* @param {boolean} horiz
*
* @param {Array<*>} pinchDown
*
* @param {Array<*>} touches
*
* @param {*} transform
*
* @param {*} selectionMarker
*
* @param {*} clip
*
* @param {*} lastValidTouch
*
* @param {number|undefined} [forcedScale=1]
*/
pinchTranslateDirection: function (
horiz,
pinchDown,
touches,
transform,
selectionMarker,
clip,
lastValidTouch,
forcedScale
) {
var chart = this.chart,
xy = horiz ? 'x' : 'y',
XY = horiz ? 'X' : 'Y',
sChartXY = 'chart' + XY,
wh = horiz ? 'width' : 'height',
plotLeftTop = chart['plot' + (horiz ? 'Left' : 'Top')],
selectionWH,
selectionXY,
clipXY,
scale = forcedScale || 1,
inverted = chart.inverted,
bounds = chart.bounds[horiz ? 'h' : 'v'],
singleTouch = pinchDown.length === 1,
touch0Start = pinchDown[0][sChartXY],
touch0Now = touches[0][sChartXY],
touch1Start = !singleTouch && pinchDown[1][sChartXY],
touch1Now = !singleTouch && touches[1][sChartXY],
outOfBounds,
transformScale,
scaleKey,
setScale = function () {
// Don't zoom if fingers are too close on this axis
if (!singleTouch && Math.abs(touch0Start - touch1Start) > 20) {
scale = forcedScale ||
Math.abs(touch0Now - touch1Now) /
Math.abs(touch0Start - touch1Start);
}
clipXY = ((plotLeftTop - touch0Now) / scale) + touch0Start;
selectionWH = chart['plot' + (horiz ? 'Width' : 'Height')] /
scale;
};
// Set the scale, first pass
setScale();
// The clip position (x or y) is altered if out of bounds, the selection
// position is not
selectionXY = clipXY;
// Out of bounds
if (selectionXY < bounds.min) {
selectionXY = bounds.min;
outOfBounds = true;
} else if (selectionXY + selectionWH > bounds.max) {
selectionXY = bounds.max - selectionWH;
outOfBounds = true;
}
// Is the chart dragged off its bounds, determined by dataMin and
// dataMax?
if (outOfBounds) {
// Modify the touchNow position in order to create an elastic drag
// movement. This indicates to the user that the chart is responsive
// but can't be dragged further.
touch0Now -= 0.8 * (touch0Now - lastValidTouch[xy][0]);
if (!singleTouch) {
touch1Now -= 0.8 * (touch1Now - lastValidTouch[xy][1]);
}
// Set the scale, second pass to adapt to the modified touchNow
// positions
setScale();
} else {
lastValidTouch[xy] = [touch0Now, touch1Now];
}
// Set geometry for clipping, selection and transformation
if (!inverted) {
clip[xy] = clipXY - plotLeftTop;
clip[wh] = selectionWH;
}
scaleKey = inverted ? (horiz ? 'scaleY' : 'scaleX') : 'scale' + XY;
transformScale = inverted ? 1 / scale : scale;
selectionMarker[wh] = selectionWH;
selectionMarker[xy] = selectionXY;
transform[scaleKey] = scale;
transform['translate' + XY] = (transformScale * plotLeftTop) +
(touch0Now - (transformScale * touch0Start));
},
/**
* Handle touch events with two touches
*
* @private
* @function Highcharts.Pointer#pinch
*
* @param {Highcharts.PointerEvent} e
*/
pinch: function (e) {
var self = this,
chart = self.chart,
pinchDown = self.pinchDown,
touches = e.touches,
touchesLength = touches.length,
lastValidTouch = self.lastValidTouch,
hasZoom = self.hasZoom,
selectionMarker = self.selectionMarker,
transform = {},
fireClickEvent = touchesLength === 1 && (
(
self.inClass(e.target, 'highcharts-tracker') &&
chart.runTrackerClick
) ||
self.runChartClick
),
clip = {};
// Don't initiate panning until the user has pinched. This prevents us
// from blocking page scrolling as users scroll down a long page
// (#4210).
if (touchesLength > 1) {
self.initiated = true;
}
// On touch devices, only proceed to trigger click if a handler is
// defined
if (hasZoom && self.initiated && !fireClickEvent) {
e.preventDefault();
}
// Normalize each touch
[].map.call(touches, function (e) {
return self.normalize(e);
});
// Register the touch start position
if (e.type === 'touchstart') {
[].forEach.call(touches, function (e, i) {
pinchDown[i] = { chartX: e.chartX, chartY: e.chartY };
});
lastValidTouch.x = [pinchDown[0].chartX, pinchDown[1] &&
pinchDown[1].chartX];
lastValidTouch.y = [pinchDown[0].chartY, pinchDown[1] &&
pinchDown[1].chartY];
// Identify the data bounds in pixels
chart.axes.forEach(function (axis) {
if (axis.zoomEnabled) {
var bounds = chart.bounds[axis.horiz ? 'h' : 'v'],
minPixelPadding = axis.minPixelPadding,
min = axis.toPixels(
pick(axis.options.min, axis.dataMin)
),
max = axis.toPixels(
pick(axis.options.max, axis.dataMax)
),
absMin = Math.min(min, max),
absMax = Math.max(min, max);
// Store the bounds for use in the touchmove handler
bounds.min = Math.min(axis.pos, absMin - minPixelPadding);
bounds.max = Math.max(
axis.pos + axis.len,
absMax + minPixelPadding
);
}
});
self.res = true; // reset on next move
// Optionally move the tooltip on touchmove
} else if (self.followTouchMove && touchesLength === 1) {
this.runPointActions(self.normalize(e));
// Event type is touchmove, handle panning and pinching
} else if (pinchDown.length) { // can be 0 when releasing, if touchend
// fires first
// Set the marker
if (!selectionMarker) {
self.selectionMarker = selectionMarker = extend({
destroy: noop,
touch: true
}, chart.plotBox);
}
self.pinchTranslate(
pinchDown,
touches,
transform,
selectionMarker,
clip,
lastValidTouch
);
self.hasPinched = hasZoom;
// Scale and translate the groups to provide visual feedback during
// pinching
self.scaleGroups(transform, clip);
if (self.res) {
self.res = false;
this.reset(false, 0);
}
}
},
/**
* General touch handler shared by touchstart and touchmove.
*
* @private
* @function Highcharts.Pointer#touch
*
* @param {Highcharts.PointerEvent} e
*
* @param {boolean} start
*/
touch: function (e, start) {
var chart = this.chart,
hasMoved,
pinchDown,
isInside;
if (chart.index !== H.hoverChartIndex) {
this.onContainerMouseLeave({ relatedTarget: true });
}
H.hoverChartIndex = chart.index;
if (e.touches.length === 1) {
e = this.normalize(e);
isInside = chart.isInsidePlot(
e.chartX - chart.plotLeft,
e.chartY - chart.plotTop
);
if (isInside && !chart.openMenu) {
// Run mouse events and display tooltip etc
if (start) {
this.runPointActions(e);
}
// Android fires touchmove events after the touchstart even if
// the finger hasn't moved, or moved only a pixel or two. In iOS
// however, the touchmove doesn't fire unless the finger moves
// more than ~4px. So we emulate this behaviour in Android by
// checking how much it moved, and cancelling on small
// distances. #3450.
if (e.type === 'touchmove') {
pinchDown = this.pinchDown;
hasMoved = pinchDown[0] ? Math.sqrt( // #5266
Math.pow(pinchDown[0].chartX - e.chartX, 2) +
Math.pow(pinchDown[0].chartY - e.chartY, 2)
) >= 4 : false;
}
if (pick(hasMoved, true)) {
this.pinch(e);
}
} else if (start) {
// Hide the tooltip on touching outside the plot area (#1203)
this.reset();
}
} else if (e.touches.length === 2) {
this.pinch(e);
}
},
/**
* @private
* @function Highcharts.Pointer#onContainerTouchStart
*
* @param {Highcharts.PointerEvent} e
*/
onContainerTouchStart: function (e) {
this.zoomOption(e);
this.touch(e, true);
},
/**
* @private
* @function Highcharts.Pointer#onContainerTouchMove
*
* @param {Highcharts.PointerEvent} e
*/
onContainerTouchMove: function (e) {
this.touch(e);
},
/**
* @private
* @function Highcharts.Pointer#onDocumentTouchEnd
*
* @param {Highcharts.PointerEvent} e
*/
onDocumentTouchEnd: function (e) {
if (charts[H.hoverChartIndex]) {
charts[H.hoverChartIndex].pointer.drop(e);
}
}
});