initial commit
This commit is contained in:
@@ -0,0 +1,404 @@
|
||||
/**
|
||||
* (c) 2010-2019 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);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
Reference in New Issue
Block a user