pinch.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. import { mix, each, directionEnabled } from '../util/common';
  2. import { getLimitRange, getFieldRange } from './helper';
  3. import Interaction from './base';
  4. import Chart from '../chart/chart';
  5. import * as FilterPlugin from '../plugin/filter';
  6. import UpdateScaleMixin from './mixin/update-scale';
  7. class Pinch extends Interaction {
  8. getDefaultCfg() {
  9. var defaultCfg = super.getDefaultCfg();
  10. return mix({}, defaultCfg, {
  11. startEvent: 'pinchstart',
  12. processEvent: 'pinch',
  13. endEvent: 'pinchend',
  14. resetEvent: 'touchend',
  15. pressThreshold: 9,
  16. // Minimal movement that is allowed while pressing
  17. pressTime: 251,
  18. // Minimal press time in ms
  19. mode: 'x',
  20. currentPinchScaling: null,
  21. originValues: null,
  22. minScale: null,
  23. maxScale: null,
  24. limitRange: {},
  25. sensitivity: 1,
  26. _pinchCumulativeDelta: 0,
  27. _timestamp: 0
  28. });
  29. }
  30. constructor(cfg, chart) {
  31. super(cfg, chart);
  32. var self = this;
  33. var {
  34. hammer
  35. } = self;
  36. hammer.get('pinch').set({
  37. // open pinch recognizer
  38. enable: true
  39. });
  40. chart.registerPlugins([FilterPlugin, {
  41. changeData() {
  42. self.limitRange = {};
  43. self.originTicks = null;
  44. },
  45. clear() {
  46. self.limitRange = {};
  47. self.originTicks = null;
  48. }
  49. }]);
  50. mix(self, UpdateScaleMixin);
  51. }
  52. start() {
  53. if (this.pressed) return;
  54. this.currentPinchScaling = 1;
  55. }
  56. process(e) {
  57. if (this.pressed) return;
  58. this._handlePinch(e);
  59. }
  60. end(e) {
  61. if (this.pressed) return;
  62. this._handlePinch(e);
  63. this.currentPinchScaling = null; // reset
  64. this.pinchCumulativeDelta = 0;
  65. }
  66. _handlePinch(e) {
  67. var currentPinchScaling = this.currentPinchScaling;
  68. var diff = 1 / currentPinchScaling * e.scale;
  69. var rect = e.target.getBoundingClientRect();
  70. var offsetX = e.center.x - rect.left;
  71. var offsetY = e.center.y - rect.top;
  72. var center = {
  73. x: offsetX,
  74. y: offsetY
  75. }; // fingers position difference
  76. var x = Math.abs(e.pointers[0].clientX - e.pointers[1].clientX);
  77. var y = Math.abs(e.pointers[0].clientY - e.pointers[1].clientY); // diagonal fingers will change both (xy) axes
  78. var p = x / y;
  79. var xy;
  80. if (p > 0.3 && p < 1.7) {
  81. xy = 'xy';
  82. } else if (x > y) {
  83. xy = 'x';
  84. } else {
  85. xy = 'y';
  86. }
  87. var lastTimestamp = this._timestamp;
  88. var now = +new Date();
  89. if (now - lastTimestamp > 16) {
  90. this._doZoom(diff, center, xy);
  91. this._timestamp = now;
  92. } // Keep track of overall scale
  93. this.currentPinchScaling = e.scale;
  94. }
  95. _doZoom(diff, center, whichAxes) {
  96. var self = this;
  97. var {
  98. mode,
  99. chart,
  100. limitRange
  101. } = self; // Which axe should be modified when figers were used.
  102. var _whichAxes;
  103. if (mode === 'xy' && whichAxes !== undefined) {
  104. // based on fingers positions
  105. _whichAxes = whichAxes;
  106. } else {
  107. _whichAxes = 'xy';
  108. }
  109. var data = chart.get('data');
  110. if (directionEnabled(mode, 'x') && directionEnabled(_whichAxes, 'x')) {
  111. // x
  112. var xScale = chart.getXScale();
  113. var xField = xScale.field;
  114. if (!limitRange[xField]) {
  115. limitRange[xField] = getLimitRange(data, xScale);
  116. }
  117. if (xScale.isCategory) {
  118. // 横轴为分类类型
  119. self._zoomCatScale(xScale, diff, center);
  120. } else if (xScale.isLinear) {
  121. self._zoomLinearScale(xScale, diff, center, 'x');
  122. }
  123. this.xRange = getFieldRange(xScale, limitRange[xField], xScale.type);
  124. }
  125. if (directionEnabled(mode, 'y') && directionEnabled(_whichAxes, 'y')) {
  126. // y
  127. var yScales = chart.getYScales();
  128. each(yScales, function (yScale) {
  129. var yField = yScale.field;
  130. if (!limitRange[yField]) {
  131. limitRange[yField] = getLimitRange(data, yScale);
  132. }
  133. yScale.isLinear && self._zoomLinearScale(yScale, diff, center, 'y');
  134. });
  135. var scale = yScales[0];
  136. this.yRange = getFieldRange(scale, limitRange[scale.field], scale.type);
  137. }
  138. chart.repaint();
  139. }
  140. _zoomLinearScale(scale, zoom, center, flag) {
  141. var chart = this.chart;
  142. var {
  143. min,
  144. max,
  145. field
  146. } = scale;
  147. var valueRange = max - min;
  148. var limitRange = this.limitRange;
  149. var originRange = limitRange[field].max - limitRange[field].min;
  150. var coord = chart.get('coord');
  151. var newDiff = valueRange * (zoom - 1);
  152. if (this.minScale && zoom < 1) {
  153. // zoom in
  154. var maxRange = originRange / this.minScale;
  155. newDiff = Math.max(valueRange - maxRange, newDiff);
  156. }
  157. if (this.maxScale && zoom >= 1) {
  158. // zoom out
  159. var minRange = originRange / this.maxScale;
  160. newDiff = Math.min(valueRange - minRange, newDiff);
  161. }
  162. var offsetPoint = coord.invertPoint(center);
  163. var percent = flag === 'x' ? offsetPoint.x : offsetPoint.y;
  164. var minDelta = newDiff * percent;
  165. var maxDelta = newDiff * (1 - percent);
  166. var newMax = max - maxDelta;
  167. var newMin = min + minDelta;
  168. this.updateLinearScale(field, newMin, newMax);
  169. } // 针对分类类型
  170. _zoomCatScale(scale, zoom, center) {
  171. var pinchCumulativeDelta = this._pinchCumulativeDelta;
  172. var sensitivity = this.sensitivity;
  173. pinchCumulativeDelta = zoom > 1 ? pinchCumulativeDelta + 1 : pinchCumulativeDelta - 1;
  174. this._pinchCumulativeDelta = pinchCumulativeDelta;
  175. var {
  176. field,
  177. values
  178. } = scale;
  179. var chart = this.chart;
  180. var coord = chart.get('coord');
  181. if (!this.originTicks) {
  182. this.originTicks = scale.ticks;
  183. }
  184. var originValues = this.limitRange[field];
  185. var originValuesLen = originValues.length;
  186. var minScale = this.minScale || 1;
  187. var maxScale = this.maxScale || 5;
  188. var minCount = parseInt(originValuesLen / maxScale);
  189. var maxCount = parseInt(originValuesLen / minScale);
  190. var currentLen = values.length;
  191. if (pinchCumulativeDelta > 0 && currentLen <= minCount) {
  192. return null;
  193. }
  194. if (pinchCumulativeDelta < 0 && currentLen >= maxCount) {
  195. return null;
  196. }
  197. var lastLabelIndex = originValuesLen - 1;
  198. var firstValue = values[0];
  199. var lastValue = values[currentLen - 1];
  200. var minIndex = originValues.indexOf(firstValue);
  201. var maxIndex = originValues.indexOf(lastValue);
  202. var chartCenter = (coord.start.x + coord.end.x) / 2;
  203. var centerPointer = center.x;
  204. if (Math.abs(pinchCumulativeDelta) > sensitivity) {
  205. var deltaCount = Math.max(1, parseInt(currentLen * Math.abs(zoom - 1)));
  206. if (pinchCumulativeDelta < 0) {
  207. if (centerPointer >= chartCenter) {
  208. if (minIndex <= 0) {
  209. maxIndex = Math.min(lastLabelIndex, maxIndex + deltaCount);
  210. } else {
  211. minIndex = Math.max(0, minIndex - deltaCount);
  212. }
  213. } else if (centerPointer < chartCenter) {
  214. if (maxIndex >= lastLabelIndex) {
  215. minIndex = Math.max(0, minIndex - deltaCount);
  216. } else {
  217. maxIndex = Math.min(lastLabelIndex, maxIndex + deltaCount);
  218. }
  219. }
  220. this._pinchCumulativeDelta = 0;
  221. } else if (pinchCumulativeDelta > 0) {
  222. if (centerPointer >= chartCenter) {
  223. minIndex = minIndex < maxIndex ? minIndex = Math.min(maxIndex, minIndex + deltaCount) : minIndex;
  224. } else if (centerPointer < chartCenter) {
  225. maxIndex = maxIndex > minIndex ? maxIndex = Math.max(minIndex, maxIndex - deltaCount) : maxIndex;
  226. }
  227. this._pinchCumulativeDelta = 0;
  228. }
  229. var newValues = originValues.slice(minIndex, maxIndex + 1);
  230. this.updateCatScale(field, newValues, this.originTicks, originValues, minIndex, maxIndex);
  231. }
  232. }
  233. }
  234. Chart.registerInteraction('pinch', Pinch);
  235. export default Pinch;