123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387 |
- /* *
- *
- * (c) 2010-2021 Torstein Honsi
- *
- * License: www.highcharts.com/license
- *
- * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
- *
- * Highcharts feature to make the Y axis stay fixed when scrolling the chart
- * horizontally on mobile devices. Supports left and right side axes.
- */
- /*
- WIP on vertical scrollable plot area (#9378). To do:
- - Bottom axis positioning
- - Test with Gantt
- - Look for size optimizing the code
- - API and demos
- */
- 'use strict';
- import A from '../Core/Animation/AnimationUtilities.js';
- var stop = A.stop;
- import Axis from '../Core/Axis/Axis.js';
- import Chart from '../Core/Chart/Chart.js';
- import Series from '../Core/Series/Series.js';
- import RendererRegistry from '../Core/Renderer/RendererRegistry.js';
- import U from '../Core/Utilities.js';
- var addEvent = U.addEvent, createElement = U.createElement, merge = U.merge, pick = U.pick;
- /* eslint-disable no-invalid-this, valid-jsdoc */
- addEvent(Chart, 'afterSetChartSize', function (e) {
- var scrollablePlotArea = this.options.chart.scrollablePlotArea, scrollableMinWidth = scrollablePlotArea && scrollablePlotArea.minWidth, scrollableMinHeight = scrollablePlotArea && scrollablePlotArea.minHeight, scrollablePixelsX, scrollablePixelsY, corrections;
- if (!this.renderer.forExport) {
- // The amount of pixels to scroll, the difference between chart
- // width and scrollable width
- if (scrollableMinWidth) {
- this.scrollablePixelsX = scrollablePixelsX = Math.max(0, scrollableMinWidth - this.chartWidth);
- if (scrollablePixelsX) {
- this.scrollablePlotBox = this.renderer.scrollablePlotBox = merge(this.plotBox);
- this.plotBox.width = this.plotWidth += scrollablePixelsX;
- if (this.inverted) {
- this.clipBox.height += scrollablePixelsX;
- }
- else {
- this.clipBox.width += scrollablePixelsX;
- }
- corrections = {
- // Corrections for right side
- 1: { name: 'right', value: scrollablePixelsX }
- };
- }
- // Currently we can only do either X or Y
- }
- else if (scrollableMinHeight) {
- this.scrollablePixelsY = scrollablePixelsY = Math.max(0, scrollableMinHeight - this.chartHeight);
- if (scrollablePixelsY) {
- this.scrollablePlotBox = this.renderer.scrollablePlotBox = merge(this.plotBox);
- this.plotBox.height = this.plotHeight += scrollablePixelsY;
- if (this.inverted) {
- this.clipBox.width += scrollablePixelsY;
- }
- else {
- this.clipBox.height += scrollablePixelsY;
- }
- corrections = {
- 2: { name: 'bottom', value: scrollablePixelsY }
- };
- }
- }
- if (corrections && !e.skipAxes) {
- this.axes.forEach(function (axis) {
- // For right and bottom axes, only fix the plot line length
- if (corrections[axis.side]) {
- // Get the plot lines right in getPlotLinePath,
- // temporarily set it to the adjusted plot width.
- axis.getPlotLinePath = function () {
- var marginName = corrections[axis.side].name, correctionValue = corrections[axis.side].value,
- // axis.right or axis.bottom
- margin = this[marginName], path;
- // Temporarily adjust
- this[marginName] = margin - correctionValue;
- path = Axis.prototype.getPlotLinePath.apply(this, arguments);
- // Reset
- this[marginName] = margin;
- return path;
- };
- }
- else {
- // Apply the corrected plotWidth
- axis.setAxisSize();
- axis.setAxisTranslation();
- }
- });
- }
- }
- });
- addEvent(Chart, 'render', function () {
- if (this.scrollablePixelsX || this.scrollablePixelsY) {
- if (this.setUpScrolling) {
- this.setUpScrolling();
- }
- this.applyFixed();
- }
- else if (this.fixedDiv) { // Has been in scrollable mode
- this.applyFixed();
- }
- });
- /**
- * @private
- * @function Highcharts.Chart#setUpScrolling
- * @return {void}
- */
- Chart.prototype.setUpScrolling = function () {
- var _this = this;
- var css = {
- WebkitOverflowScrolling: 'touch',
- overflowX: 'hidden',
- overflowY: 'hidden'
- };
- if (this.scrollablePixelsX) {
- css.overflowX = 'auto';
- }
- if (this.scrollablePixelsY) {
- css.overflowY = 'auto';
- }
- // Insert a container with position relative
- // that scrolling and fixed container renders to (#10555)
- this.scrollingParent = createElement('div', {
- className: 'highcharts-scrolling-parent'
- }, {
- position: 'relative'
- }, this.renderTo);
- // Add the necessary divs to provide scrolling
- this.scrollingContainer = createElement('div', {
- 'className': 'highcharts-scrolling'
- }, css, this.scrollingParent);
- // On scroll, reset the chart position because it applies to the scrolled
- // container
- addEvent(this.scrollingContainer, 'scroll', function () {
- if (_this.pointer) {
- delete _this.pointer.chartPosition;
- }
- });
- this.innerContainer = createElement('div', {
- 'className': 'highcharts-inner-container'
- }, null, this.scrollingContainer);
- // Now move the container inside
- this.innerContainer.appendChild(this.container);
- // Don't run again
- this.setUpScrolling = null;
- };
- /**
- * These elements are moved over to the fixed renderer and stay fixed when the
- * user scrolls the chart
- * @private
- */
- Chart.prototype.moveFixedElements = function () {
- var container = this.container, fixedRenderer = this.fixedRenderer, fixedSelectors = [
- '.highcharts-contextbutton',
- '.highcharts-credits',
- '.highcharts-legend',
- '.highcharts-legend-checkbox',
- '.highcharts-navigator-series',
- '.highcharts-navigator-xaxis',
- '.highcharts-navigator-yaxis',
- '.highcharts-navigator',
- '.highcharts-reset-zoom',
- '.highcharts-drillup-button',
- '.highcharts-scrollbar',
- '.highcharts-subtitle',
- '.highcharts-title'
- ], axisClass;
- if (this.scrollablePixelsX && !this.inverted) {
- axisClass = '.highcharts-yaxis';
- }
- else if (this.scrollablePixelsX && this.inverted) {
- axisClass = '.highcharts-xaxis';
- }
- else if (this.scrollablePixelsY && !this.inverted) {
- axisClass = '.highcharts-xaxis';
- }
- else if (this.scrollablePixelsY && this.inverted) {
- axisClass = '.highcharts-yaxis';
- }
- if (axisClass) {
- fixedSelectors.push(axisClass + ":not(.highcharts-radial-axis)", axisClass + "-labels:not(.highcharts-radial-axis-labels)");
- }
- fixedSelectors.forEach(function (className) {
- [].forEach.call(container.querySelectorAll(className), function (elem) {
- (elem.namespaceURI === fixedRenderer.SVG_NS ?
- fixedRenderer.box :
- fixedRenderer.box.parentNode).appendChild(elem);
- elem.style.pointerEvents = 'auto';
- });
- });
- };
- /**
- * @private
- * @function Highcharts.Chart#applyFixed
- * @return {void}
- */
- Chart.prototype.applyFixed = function () {
- var firstTime = !this.fixedDiv, chartOptions = this.options.chart, scrollableOptions = chartOptions.scrollablePlotArea, Renderer = RendererRegistry.getRendererType();
- var fixedRenderer, scrollableWidth, scrollableHeight;
- // First render
- if (firstTime) {
- this.fixedDiv = createElement('div', {
- className: 'highcharts-fixed'
- }, {
- position: 'absolute',
- overflow: 'hidden',
- pointerEvents: 'none',
- zIndex: (chartOptions.style && chartOptions.style.zIndex || 0) + 2,
- top: 0
- }, null, true);
- if (this.scrollingContainer) {
- this.scrollingContainer.parentNode.insertBefore(this.fixedDiv, this.scrollingContainer);
- }
- this.renderTo.style.overflow = 'visible';
- this.fixedRenderer = fixedRenderer = new Renderer(this.fixedDiv, this.chartWidth, this.chartHeight, this.options.chart.style);
- // Mask
- this.scrollableMask = fixedRenderer
- .path()
- .attr({
- fill: this.options.chart.backgroundColor || '#fff',
- 'fill-opacity': pick(scrollableOptions.opacity, 0.85),
- zIndex: -1
- })
- .addClass('highcharts-scrollable-mask')
- .add();
- addEvent(this, 'afterShowResetZoom', this.moveFixedElements);
- addEvent(this, 'afterDrilldown', this.moveFixedElements);
- addEvent(this, 'afterLayOutTitles', this.moveFixedElements);
- }
- else {
- // Set the size of the fixed renderer to the visible width
- this.fixedRenderer.setSize(this.chartWidth, this.chartHeight);
- }
- if (this.scrollableDirty || firstTime) {
- this.scrollableDirty = false;
- this.moveFixedElements();
- }
- // Increase the size of the scrollable renderer and background
- scrollableWidth = this.chartWidth + (this.scrollablePixelsX || 0);
- scrollableHeight = this.chartHeight + (this.scrollablePixelsY || 0);
- stop(this.container);
- this.container.style.width = scrollableWidth + 'px';
- this.container.style.height = scrollableHeight + 'px';
- this.renderer.boxWrapper.attr({
- width: scrollableWidth,
- height: scrollableHeight,
- viewBox: [0, 0, scrollableWidth, scrollableHeight].join(' ')
- });
- this.chartBackground.attr({
- width: scrollableWidth,
- height: scrollableHeight
- });
- this.scrollingContainer.style.height = this.chartHeight + 'px';
- // Set scroll position
- if (firstTime) {
- if (scrollableOptions.scrollPositionX) {
- this.scrollingContainer.scrollLeft =
- this.scrollablePixelsX *
- scrollableOptions.scrollPositionX;
- }
- if (scrollableOptions.scrollPositionY) {
- this.scrollingContainer.scrollTop =
- this.scrollablePixelsY *
- scrollableOptions.scrollPositionY;
- }
- }
- // Mask behind the left and right side
- var axisOffset = this.axisOffset, maskTop = this.plotTop - axisOffset[0] - 1, maskLeft = this.plotLeft - axisOffset[3] - 1, maskBottom = this.plotTop + this.plotHeight + axisOffset[2] + 1, maskRight = this.plotLeft + this.plotWidth + axisOffset[1] + 1, maskPlotRight = this.plotLeft + this.plotWidth -
- (this.scrollablePixelsX || 0), maskPlotBottom = this.plotTop + this.plotHeight -
- (this.scrollablePixelsY || 0), d;
- if (this.scrollablePixelsX) {
- d = [
- // Left side
- ['M', 0, maskTop],
- ['L', this.plotLeft - 1, maskTop],
- ['L', this.plotLeft - 1, maskBottom],
- ['L', 0, maskBottom],
- ['Z'],
- // Right side
- ['M', maskPlotRight, maskTop],
- ['L', this.chartWidth, maskTop],
- ['L', this.chartWidth, maskBottom],
- ['L', maskPlotRight, maskBottom],
- ['Z']
- ];
- }
- else if (this.scrollablePixelsY) {
- d = [
- // Top side
- ['M', maskLeft, 0],
- ['L', maskLeft, this.plotTop - 1],
- ['L', maskRight, this.plotTop - 1],
- ['L', maskRight, 0],
- ['Z'],
- // Bottom side
- ['M', maskLeft, maskPlotBottom],
- ['L', maskLeft, this.chartHeight],
- ['L', maskRight, this.chartHeight],
- ['L', maskRight, maskPlotBottom],
- ['Z']
- ];
- }
- else {
- d = [['M', 0, 0]];
- }
- if (this.redrawTrigger !== 'adjustHeight') {
- this.scrollableMask.attr({ d: d });
- }
- };
- addEvent(Axis, 'afterInit', function () {
- this.chart.scrollableDirty = true;
- });
- addEvent(Series, 'show', function () {
- this.chart.scrollableDirty = true;
- });
- /* *
- *
- * API Declarations
- *
- * */
- /**
- * Options for a scrollable plot area. This feature provides a minimum size for
- * the plot area of the chart. If the size gets smaller than this, typically
- * on mobile devices, a native browser scrollbar is presented. This scrollbar
- * provides smooth scrolling for the contents of the plot area, whereas the
- * title, legend and unaffected axes are fixed.
- *
- * Since v7.1.2, a scrollable plot area can be defined for either horizontal or
- * vertical scrolling, depending on whether the `minWidth` or `minHeight`
- * option is set.
- *
- * @sample highcharts/chart/scrollable-plotarea
- * Scrollable plot area
- * @sample highcharts/chart/scrollable-plotarea-vertical
- * Vertically scrollable plot area
- * @sample {gantt} highcharts/chart/scrollable-plotarea-vertical
- * Gantt chart with vertically scrollable plot area
- *
- * @since 6.1.0
- * @product highcharts gantt
- * @apioption chart.scrollablePlotArea
- */
- /**
- * The minimum height for the plot area. If it gets smaller than this, the plot
- * area will become scrollable.
- *
- * @type {number}
- * @apioption chart.scrollablePlotArea.minHeight
- */
- /**
- * The minimum width for the plot area. If it gets smaller than this, the plot
- * area will become scrollable.
- *
- * @type {number}
- * @apioption chart.scrollablePlotArea.minWidth
- */
- /**
- * The initial scrolling position of the scrollable plot area. Ranges from 0 to
- * 1, where 0 aligns the plot area to the left and 1 aligns it to the right.
- * Typically we would use 1 if the chart has right aligned Y axes.
- *
- * @type {number}
- * @apioption chart.scrollablePlotArea.scrollPositionX
- */
- /**
- * The initial scrolling position of the scrollable plot area. Ranges from 0 to
- * 1, where 0 aligns the plot area to the top and 1 aligns it to the bottom.
- *
- * @type {number}
- * @apioption chart.scrollablePlotArea.scrollPositionY
- */
- /**
- * The opacity of mask applied on one of the sides of the plot
- * area.
- *
- * @sample {highcharts} highcharts/chart/scrollable-plotarea-opacity
- * Disabled opacity for the mask
- *
- * @type {number}
- * @default 0.85
- * @since 7.1.1
- * @apioption chart.scrollablePlotArea.opacity
- */
- (''); // keep doclets above in transpiled file
|