ZoomScroll.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. import {
  2. event as domEvent,
  3. closest as domClosest
  4. } from 'min-dom';
  5. import {
  6. getStepSize,
  7. cap
  8. } from './ZoomUtil';
  9. import {
  10. log10
  11. } from '../../util/Math';
  12. import {
  13. bind
  14. } from 'min-dash';
  15. var sign = Math.sign || function(n) {
  16. return n >= 0 ? 1 : -1;
  17. };
  18. var RANGE = { min: 0.2, max: 4 },
  19. NUM_STEPS = 10;
  20. var DELTA_THRESHOLD = 0.1;
  21. var DEFAULT_SCALE = 0.75;
  22. /**
  23. * An implementation of zooming and scrolling within the
  24. * {@link Canvas} via the mouse wheel.
  25. *
  26. * Mouse wheel zooming / scrolling may be disabled using
  27. * the {@link toggle(enabled)} method.
  28. *
  29. * @param {Object} [config]
  30. * @param {Boolean} [config.enabled=true] default enabled state
  31. * @param {Number} [config.scale=.75] scroll sensivity
  32. * @param {EventBus} eventBus
  33. * @param {Canvas} canvas
  34. */
  35. export default function ZoomScroll(config, eventBus, canvas) {
  36. config = config || {};
  37. this._enabled = false;
  38. this._canvas = canvas;
  39. this._container = canvas._container;
  40. this._handleWheel = bind(this._handleWheel, this);
  41. this._totalDelta = 0;
  42. this._scale = config.scale || DEFAULT_SCALE;
  43. var self = this;
  44. eventBus.on('canvas.init', function(e) {
  45. self._init(config.enabled !== false);
  46. });
  47. }
  48. ZoomScroll.$inject = [
  49. 'config.zoomScroll',
  50. 'eventBus',
  51. 'canvas'
  52. ];
  53. ZoomScroll.prototype.scroll = function scroll(delta) {
  54. this._canvas.scroll(delta);
  55. };
  56. ZoomScroll.prototype.reset = function reset() {
  57. this._canvas.zoom('fit-viewport');
  58. };
  59. /**
  60. * Zoom depending on delta.
  61. *
  62. * @param {number} delta - Zoom delta.
  63. * @param {Object} position - Zoom position.
  64. */
  65. ZoomScroll.prototype.zoom = function zoom(delta, position) {
  66. // zoom with half the step size of stepZoom
  67. var stepSize = getStepSize(RANGE, NUM_STEPS * 2);
  68. // add until threshold reached
  69. this._totalDelta += delta;
  70. if (Math.abs(this._totalDelta) > DELTA_THRESHOLD) {
  71. this._zoom(delta, position, stepSize);
  72. // reset
  73. this._totalDelta = 0;
  74. }
  75. };
  76. ZoomScroll.prototype._handleWheel = function handleWheel(event) {
  77. // event is already handled by '.djs-scrollable'
  78. if (domClosest(event.target, '.djs-scrollable', true)) {
  79. return;
  80. }
  81. var element = this._container;
  82. event.preventDefault();
  83. // pinch to zoom is mapped to wheel + ctrlKey = true
  84. // in modern browsers (!)
  85. var isZoom = event.ctrlKey;
  86. var isHorizontalScroll = event.shiftKey;
  87. var factor = -1 * this._scale,
  88. delta;
  89. if (isZoom) {
  90. factor *= event.deltaMode === 0 ? 0.020 : 0.32;
  91. } else {
  92. factor *= event.deltaMode === 0 ? 1.0 : 16.0;
  93. }
  94. if (isZoom) {
  95. var elementRect = element.getBoundingClientRect();
  96. var offset = {
  97. x: event.clientX - elementRect.left,
  98. y: event.clientY - elementRect.top
  99. };
  100. delta = (
  101. Math.sqrt(
  102. Math.pow(event.deltaY, 2) +
  103. Math.pow(event.deltaX, 2)
  104. ) * sign(event.deltaY) * factor
  105. );
  106. // zoom in relative to diagram {x,y} coordinates
  107. this.zoom(delta, offset);
  108. } else {
  109. if (isHorizontalScroll) {
  110. delta = {
  111. dx: factor * event.deltaY,
  112. dy: 0
  113. };
  114. } else {
  115. delta = {
  116. dx: factor * event.deltaX,
  117. dy: factor * event.deltaY
  118. };
  119. }
  120. this.scroll(delta);
  121. }
  122. };
  123. /**
  124. * Zoom with fixed step size.
  125. *
  126. * @param {number} delta - Zoom delta (1 for zooming in, -1 for out).
  127. * @param {Object} position - Zoom position.
  128. */
  129. ZoomScroll.prototype.stepZoom = function stepZoom(delta, position) {
  130. var stepSize = getStepSize(RANGE, NUM_STEPS);
  131. this._zoom(delta, position, stepSize);
  132. };
  133. /**
  134. * Zoom in/out given a step size.
  135. *
  136. * @param {number} delta - Zoom delta. Can be positive or negative.
  137. * @param {Object} position - Zoom position.
  138. * @param {number} stepSize - Step size.
  139. */
  140. ZoomScroll.prototype._zoom = function(delta, position, stepSize) {
  141. var canvas = this._canvas;
  142. var direction = delta > 0 ? 1 : -1;
  143. var currentLinearZoomLevel = log10(canvas.zoom());
  144. // snap to a proximate zoom step
  145. var newLinearZoomLevel = Math.round(currentLinearZoomLevel / stepSize) * stepSize;
  146. // increase or decrease one zoom step in the given direction
  147. newLinearZoomLevel += stepSize * direction;
  148. // calculate the absolute logarithmic zoom level based on the linear zoom level
  149. // (e.g. 2 for an absolute x2 zoom)
  150. var newLogZoomLevel = Math.pow(10, newLinearZoomLevel);
  151. canvas.zoom(cap(RANGE, newLogZoomLevel), position);
  152. };
  153. /**
  154. * Toggle the zoom scroll ability via mouse wheel.
  155. *
  156. * @param {Boolean} [newEnabled] new enabled state
  157. */
  158. ZoomScroll.prototype.toggle = function toggle(newEnabled) {
  159. var element = this._container;
  160. var handleWheel = this._handleWheel;
  161. var oldEnabled = this._enabled;
  162. if (typeof newEnabled === 'undefined') {
  163. newEnabled = !oldEnabled;
  164. }
  165. // only react on actual changes
  166. if (oldEnabled !== newEnabled) {
  167. // add or remove wheel listener based on
  168. // changed enabled state
  169. domEvent[newEnabled ? 'bind' : 'unbind'](element, 'wheel', handleWheel, false);
  170. }
  171. this._enabled = newEnabled;
  172. return newEnabled;
  173. };
  174. ZoomScroll.prototype._init = function(newEnabled) {
  175. this.toggle(newEnabled);
  176. };