264 lines
7.9 KiB
JavaScript
264 lines
7.9 KiB
JavaScript
![]() |
/**
|
||
|
* (c) 2010-2018 Torstein Honsi
|
||
|
*
|
||
|
* License: www.highcharts.com/license
|
||
|
*
|
||
|
* Highcharts feature to make the Y axis stay fixed when scrolling the chart
|
||
|
* horizontally on mobile devices. Supports left and right side axes.
|
||
|
*/
|
||
|
|
||
|
'use strict';
|
||
|
|
||
|
import H from './Globals.js';
|
||
|
|
||
|
var addEvent = H.addEvent,
|
||
|
Chart = H.Chart;
|
||
|
|
||
|
/**
|
||
|
* Options for a scrollable plot area. This feature provides a minimum width for
|
||
|
* the plot area of the chart. If the width gets smaller than this, typically
|
||
|
* on mobile devices, a native browser scrollbar is presented below the chart.
|
||
|
* This scrollbar provides smooth scrolling for the contents of the plot area,
|
||
|
* whereas the title, legend and axes are fixed.
|
||
|
*
|
||
|
* @sample {highcharts} highcharts/chart/scrollable-plotarea
|
||
|
* Scrollable plot area
|
||
|
*
|
||
|
* @since 6.1.0
|
||
|
* @product highcharts gantt
|
||
|
* @apioption chart.scrollablePlotArea
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* 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
|
||
|
*/
|
||
|
|
||
|
addEvent(Chart, 'afterSetChartSize', function (e) {
|
||
|
|
||
|
var scrollablePlotArea = this.options.chart.scrollablePlotArea,
|
||
|
scrollableMinWidth =
|
||
|
scrollablePlotArea && scrollablePlotArea.minWidth,
|
||
|
scrollablePixels;
|
||
|
|
||
|
if (scrollableMinWidth && !this.renderer.forExport) {
|
||
|
|
||
|
// The amount of pixels to scroll, the difference between chart
|
||
|
// width and scrollable width
|
||
|
this.scrollablePixels = scrollablePixels = Math.max(
|
||
|
0,
|
||
|
scrollableMinWidth - this.chartWidth
|
||
|
);
|
||
|
|
||
|
if (scrollablePixels) {
|
||
|
this.plotWidth += scrollablePixels;
|
||
|
this.clipBox.width += scrollablePixels;
|
||
|
|
||
|
if (!e.skipAxes) {
|
||
|
this.axes.forEach(function (axis) {
|
||
|
if (axis.side === 1) {
|
||
|
// Get the plot lines right in getPlotLinePath,
|
||
|
// temporarily set it to the adjusted plot width.
|
||
|
axis.getPlotLinePath = function () {
|
||
|
var right = this.right,
|
||
|
path;
|
||
|
this.right = right - axis.chart.scrollablePixels;
|
||
|
path = H.Axis.prototype.getPlotLinePath.apply(
|
||
|
this,
|
||
|
arguments
|
||
|
);
|
||
|
this.right = right;
|
||
|
return path;
|
||
|
};
|
||
|
|
||
|
} else {
|
||
|
// Apply the corrected plotWidth
|
||
|
axis.setAxisSize();
|
||
|
axis.setAxisTranslation();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
addEvent(Chart, 'render', function () {
|
||
|
if (this.scrollablePixels) {
|
||
|
if (this.setUpScrolling) {
|
||
|
this.setUpScrolling();
|
||
|
}
|
||
|
this.applyFixed();
|
||
|
|
||
|
} else if (this.fixedDiv) { // Has been in scrollable mode
|
||
|
this.applyFixed();
|
||
|
}
|
||
|
});
|
||
|
|
||
|
/**
|
||
|
* @private
|
||
|
* @function Highcharts.Chart#setUpScrolling
|
||
|
*/
|
||
|
Chart.prototype.setUpScrolling = function () {
|
||
|
|
||
|
// Add the necessary divs to provide scrolling
|
||
|
this.scrollingContainer = H.createElement('div', {
|
||
|
'className': 'highcharts-scrolling'
|
||
|
}, {
|
||
|
overflowX: 'auto',
|
||
|
WebkitOverflowScrolling: 'touch'
|
||
|
}, this.renderTo);
|
||
|
|
||
|
this.innerContainer = H.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;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* @private
|
||
|
* @function Highcharts.Chart#applyFixed
|
||
|
*/
|
||
|
Chart.prototype.applyFixed = function () {
|
||
|
var container = this.container,
|
||
|
fixedRenderer,
|
||
|
scrollableWidth,
|
||
|
firstTime = !this.fixedDiv;
|
||
|
|
||
|
// First render
|
||
|
if (firstTime) {
|
||
|
|
||
|
this.fixedDiv = H.createElement(
|
||
|
'div',
|
||
|
{
|
||
|
className: 'highcharts-fixed'
|
||
|
},
|
||
|
{
|
||
|
position: 'absolute',
|
||
|
overflow: 'hidden',
|
||
|
pointerEvents: 'none',
|
||
|
zIndex: 2
|
||
|
},
|
||
|
null,
|
||
|
true
|
||
|
);
|
||
|
this.renderTo.insertBefore(
|
||
|
this.fixedDiv,
|
||
|
this.renderTo.firstChild
|
||
|
);
|
||
|
|
||
|
this.fixedRenderer = fixedRenderer = new H.Renderer(
|
||
|
this.fixedDiv,
|
||
|
0,
|
||
|
0
|
||
|
);
|
||
|
|
||
|
// Mask
|
||
|
this.scrollableMask = fixedRenderer.path()
|
||
|
.attr({
|
||
|
fill: H.color(
|
||
|
this.options.chart.backgroundColor || '#fff'
|
||
|
).setOpacity(0.85).get(),
|
||
|
zIndex: -1
|
||
|
})
|
||
|
.addClass('highcharts-scrollable-mask')
|
||
|
.add();
|
||
|
|
||
|
// These elements are moved over to the fixed renderer and stay fixed
|
||
|
// when the user scrolls the chart.
|
||
|
([
|
||
|
this.inverted ?
|
||
|
'.highcharts-xaxis' :
|
||
|
'.highcharts-yaxis',
|
||
|
this.inverted ?
|
||
|
'.highcharts-xaxis-labels' :
|
||
|
'.highcharts-yaxis-labels',
|
||
|
'.highcharts-contextbutton',
|
||
|
'.highcharts-credits',
|
||
|
'.highcharts-legend',
|
||
|
'.highcharts-subtitle',
|
||
|
'.highcharts-title',
|
||
|
'.highcharts-legend-checkbox'
|
||
|
]).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';
|
||
|
}
|
||
|
);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// Set the size of the fixed renderer to the visible width
|
||
|
this.fixedRenderer.setSize(
|
||
|
this.chartWidth,
|
||
|
this.chartHeight
|
||
|
);
|
||
|
|
||
|
// Increase the size of the scrollable renderer and background
|
||
|
scrollableWidth = this.chartWidth + this.scrollablePixels;
|
||
|
H.stop(this.container);
|
||
|
this.container.style.width = scrollableWidth + 'px';
|
||
|
this.renderer.boxWrapper.attr({
|
||
|
width: scrollableWidth,
|
||
|
height: this.chartHeight,
|
||
|
viewBox: [0, 0, scrollableWidth, this.chartHeight].join(' ')
|
||
|
});
|
||
|
this.chartBackground.attr({ width: scrollableWidth });
|
||
|
|
||
|
// Set scroll position
|
||
|
if (firstTime) {
|
||
|
var options = this.options.chart.scrollablePlotArea;
|
||
|
if (options.scrollPositionX) {
|
||
|
this.scrollingContainer.scrollLeft =
|
||
|
this.scrollablePixels * options.scrollPositionX;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Mask behind the left and right side
|
||
|
var axisOffset = this.axisOffset,
|
||
|
maskTop = this.plotTop - axisOffset[0] - 1,
|
||
|
maskBottom = this.plotTop + this.plotHeight + axisOffset[2],
|
||
|
maskPlotRight = this.plotLeft + this.plotWidth -
|
||
|
this.scrollablePixels;
|
||
|
|
||
|
this.scrollableMask.attr({
|
||
|
d: this.scrollablePixels ? [
|
||
|
// 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'
|
||
|
] : ['M', 0, 0]
|
||
|
});
|
||
|
};
|