ScrollablePlotArea.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. /**
  2. * (c) 2010-2019 Torstein Honsi
  3. *
  4. * License: www.highcharts.com/license
  5. *
  6. * Highcharts feature to make the Y axis stay fixed when scrolling the chart
  7. * horizontally on mobile devices. Supports left and right side axes.
  8. */
  9. 'use strict';
  10. import H from './Globals.js';
  11. var addEvent = H.addEvent,
  12. Chart = H.Chart;
  13. /**
  14. * Options for a scrollable plot area. This feature provides a minimum width for
  15. * the plot area of the chart. If the width gets smaller than this, typically
  16. * on mobile devices, a native browser scrollbar is presented below the chart.
  17. * This scrollbar provides smooth scrolling for the contents of the plot area,
  18. * whereas the title, legend and axes are fixed.
  19. *
  20. * @sample {highcharts} highcharts/chart/scrollable-plotarea
  21. * Scrollable plot area
  22. *
  23. * @since 6.1.0
  24. * @product highcharts gantt
  25. * @apioption chart.scrollablePlotArea
  26. */
  27. /**
  28. * The minimum width for the plot area. If it gets smaller than this, the plot
  29. * area will become scrollable.
  30. *
  31. * @type {number}
  32. * @apioption chart.scrollablePlotArea.minWidth
  33. */
  34. /**
  35. * The initial scrolling position of the scrollable plot area. Ranges from 0 to
  36. * 1, where 0 aligns the plot area to the left and 1 aligns it to the right.
  37. * Typically we would use 1 if the chart has right aligned Y axes.
  38. *
  39. * @type {number}
  40. * @apioption chart.scrollablePlotArea.scrollPositionX
  41. */
  42. addEvent(Chart, 'afterSetChartSize', function (e) {
  43. var scrollablePlotArea = this.options.chart.scrollablePlotArea,
  44. scrollableMinWidth =
  45. scrollablePlotArea && scrollablePlotArea.minWidth,
  46. scrollablePixels;
  47. if (scrollableMinWidth && !this.renderer.forExport) {
  48. // The amount of pixels to scroll, the difference between chart
  49. // width and scrollable width
  50. this.scrollablePixels = scrollablePixels = Math.max(
  51. 0,
  52. scrollableMinWidth - this.chartWidth
  53. );
  54. if (scrollablePixels) {
  55. this.plotWidth += scrollablePixels;
  56. this.clipBox.width += scrollablePixels;
  57. if (!e.skipAxes) {
  58. this.axes.forEach(function (axis) {
  59. if (axis.side === 1) {
  60. // Get the plot lines right in getPlotLinePath,
  61. // temporarily set it to the adjusted plot width.
  62. axis.getPlotLinePath = function () {
  63. var right = this.right,
  64. path;
  65. this.right = right - axis.chart.scrollablePixels;
  66. path = H.Axis.prototype.getPlotLinePath.apply(
  67. this,
  68. arguments
  69. );
  70. this.right = right;
  71. return path;
  72. };
  73. } else {
  74. // Apply the corrected plotWidth
  75. axis.setAxisSize();
  76. axis.setAxisTranslation();
  77. }
  78. });
  79. }
  80. }
  81. }
  82. });
  83. addEvent(Chart, 'render', function () {
  84. if (this.scrollablePixels) {
  85. if (this.setUpScrolling) {
  86. this.setUpScrolling();
  87. }
  88. this.applyFixed();
  89. } else if (this.fixedDiv) { // Has been in scrollable mode
  90. this.applyFixed();
  91. }
  92. });
  93. /**
  94. * @private
  95. * @function Highcharts.Chart#setUpScrolling
  96. */
  97. Chart.prototype.setUpScrolling = function () {
  98. // Add the necessary divs to provide scrolling
  99. this.scrollingContainer = H.createElement('div', {
  100. 'className': 'highcharts-scrolling'
  101. }, {
  102. overflowX: 'auto',
  103. WebkitOverflowScrolling: 'touch'
  104. }, this.renderTo);
  105. this.innerContainer = H.createElement('div', {
  106. 'className': 'highcharts-inner-container'
  107. }, null, this.scrollingContainer);
  108. // Now move the container inside
  109. this.innerContainer.appendChild(this.container);
  110. // Don't run again
  111. this.setUpScrolling = null;
  112. };
  113. /**
  114. * @private
  115. * @function Highcharts.Chart#applyFixed
  116. */
  117. Chart.prototype.applyFixed = function () {
  118. var container = this.container,
  119. fixedRenderer,
  120. scrollableWidth,
  121. firstTime = !this.fixedDiv;
  122. // First render
  123. if (firstTime) {
  124. this.fixedDiv = H.createElement(
  125. 'div',
  126. {
  127. className: 'highcharts-fixed'
  128. },
  129. {
  130. position: 'absolute',
  131. overflow: 'hidden',
  132. pointerEvents: 'none',
  133. zIndex: 2
  134. },
  135. null,
  136. true
  137. );
  138. this.renderTo.insertBefore(
  139. this.fixedDiv,
  140. this.renderTo.firstChild
  141. );
  142. this.renderTo.style.overflow = 'visible';
  143. this.fixedRenderer = fixedRenderer = new H.Renderer(
  144. this.fixedDiv,
  145. 0,
  146. 0
  147. );
  148. // Mask
  149. this.scrollableMask = fixedRenderer.path()
  150. .attr({
  151. fill: H.color(
  152. this.options.chart.backgroundColor || '#fff'
  153. ).setOpacity(0.85).get(),
  154. zIndex: -1
  155. })
  156. .addClass('highcharts-scrollable-mask')
  157. .add();
  158. // These elements are moved over to the fixed renderer and stay fixed
  159. // when the user scrolls the chart.
  160. ([
  161. this.inverted ?
  162. '.highcharts-xaxis' :
  163. '.highcharts-yaxis',
  164. this.inverted ?
  165. '.highcharts-xaxis-labels' :
  166. '.highcharts-yaxis-labels',
  167. '.highcharts-contextbutton',
  168. '.highcharts-credits',
  169. '.highcharts-legend',
  170. '.highcharts-subtitle',
  171. '.highcharts-title',
  172. '.highcharts-legend-checkbox'
  173. ]).forEach(function (className) {
  174. [].forEach.call(
  175. container.querySelectorAll(className),
  176. function (elem) {
  177. (
  178. elem.namespaceURI === fixedRenderer.SVG_NS ?
  179. fixedRenderer.box :
  180. fixedRenderer.box.parentNode
  181. ).appendChild(elem);
  182. elem.style.pointerEvents = 'auto';
  183. }
  184. );
  185. });
  186. }
  187. // Set the size of the fixed renderer to the visible width
  188. this.fixedRenderer.setSize(
  189. this.chartWidth,
  190. this.chartHeight
  191. );
  192. // Increase the size of the scrollable renderer and background
  193. scrollableWidth = this.chartWidth + this.scrollablePixels;
  194. H.stop(this.container);
  195. this.container.style.width = scrollableWidth + 'px';
  196. this.renderer.boxWrapper.attr({
  197. width: scrollableWidth,
  198. height: this.chartHeight,
  199. viewBox: [0, 0, scrollableWidth, this.chartHeight].join(' ')
  200. });
  201. this.chartBackground.attr({ width: scrollableWidth });
  202. // Set scroll position
  203. if (firstTime) {
  204. var options = this.options.chart.scrollablePlotArea;
  205. if (options.scrollPositionX) {
  206. this.scrollingContainer.scrollLeft =
  207. this.scrollablePixels * options.scrollPositionX;
  208. }
  209. }
  210. // Mask behind the left and right side
  211. var axisOffset = this.axisOffset,
  212. maskTop = this.plotTop - axisOffset[0] - 1,
  213. maskBottom = this.plotTop + this.plotHeight + axisOffset[2],
  214. maskPlotRight = this.plotLeft + this.plotWidth -
  215. this.scrollablePixels;
  216. this.scrollableMask.attr({
  217. d: this.scrollablePixels ? [
  218. // Left side
  219. 'M', 0, maskTop,
  220. 'L', this.plotLeft - 1, maskTop,
  221. 'L', this.plotLeft - 1, maskBottom,
  222. 'L', 0, maskBottom,
  223. 'Z',
  224. // Right side
  225. 'M', maskPlotRight, maskTop,
  226. 'L', this.chartWidth, maskTop,
  227. 'L', this.chartWidth, maskBottom,
  228. 'L', maskPlotRight, maskBottom,
  229. 'Z'
  230. ] : ['M', 0, 0]
  231. });
  232. };