highcharts
Version:
JavaScript charting framework
367 lines (366 loc) • 11.1 kB
JavaScript
/* *
*
* (c) 2019-2025 Highsoft AS
*
* Boost module: stripped-down renderer for higher performance
*
* License: highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
;
import BoostChart from './BoostChart.js';
import BoostSeries from './BoostSeries.js';
import H from '../../Core/Globals.js';
const { doc, win } = H;
import NamedColors from './NamedColors.js';
import U from '../../Core/Utilities.js';
const { addEvent, error } = U;
/* *
*
* Constants
*
* */
const contexts = [
'webgl',
'experimental-webgl',
'moz-webgl',
'webkit-3d'
];
/* *
*
* Functions
*
* */
/**
* @private
*/
function compose(ChartClass, AxisClass, SeriesClass, seriesTypes, PointClass, ColorClass) {
const wglMode = hasWebGLSupport();
if (!wglMode) {
if (typeof H.initCanvasBoost !== 'undefined') {
// Fallback to canvas boost
H.initCanvasBoost();
}
else {
error(26);
}
}
if (ColorClass && !ColorClass.names.lightgoldenrodyellow) {
ColorClass.names = {
...ColorClass.names,
...NamedColors.defaultHTMLColorMap
};
}
// WebGL support is alright, and we're good to go.
BoostChart.compose(ChartClass, wglMode);
BoostSeries.compose(SeriesClass, seriesTypes, PointClass, wglMode);
// Handle zooming by touch/pinch or mouse wheel. Assume that boosted charts
// are too slow for a live preview while dragging. Instead, just scale the
// div while `isPanning`.
addEvent(AxisClass, 'setExtremes', function (e) {
// Render targets can be either chart-wide or series-specific
const renderTargets = [this.chart, ...this.series]
.map((item) => item.renderTarget)
.filter(Boolean);
for (const renderTarget of renderTargets) {
const { horiz, pos } = this, scaleKey = horiz ? 'scaleX' : 'scaleY', translateKey = horiz ? 'translateX' : 'translateY', lastScale = renderTarget?.[scaleKey] ?? 1;
let scale = 1, translate = 0, opacity = 1, filter = 'none';
if (this.isPanning) {
scale = (e.scale ?? 1) * lastScale;
translate = (renderTarget?.[translateKey] || 0) -
scale * (e.move || 0) +
lastScale * pos -
scale * pos;
opacity = 0.7;
filter = 'blur(3px)';
}
renderTarget
?.attr({
[scaleKey]: scale,
[translateKey]: translate
})
.css({
transition: '250ms filter, 250ms opacity',
filter,
opacity
});
}
});
}
/**
* Returns true if the current browser supports WebGL.
*
* @requires modules/boost
*
* @function Highcharts.hasWebGLSupport
*
* @return {boolean}
* `true` if the browser supports WebGL.
*/
function hasWebGLSupport() {
let canvas, gl = false;
if (typeof win.WebGLRenderingContext !== 'undefined') {
canvas = doc.createElement('canvas');
for (let i = 0; i < contexts.length; ++i) {
try {
gl = canvas.getContext(contexts[i]);
if (typeof gl !== 'undefined' && gl !== null) {
return true;
}
}
catch (e) {
// Silent error
}
}
}
return false;
}
/* *
*
* Default Export
*
* */
const Boost = {
compose,
hasWebGLSupport
};
export default Boost;
/* *
*
* API Options
*
* */
/**
* Options for the Boost module. The Boost module allows certain series types
* to be rendered by WebGL instead of the default SVG. This allows hundreds of
* thousands of data points to be rendered in milliseconds. In addition to the
* WebGL rendering it saves time by skipping processing and inspection of the
* data wherever possible. This introduces some limitations to what features are
* available in boost mode. See [the docs](
* https://www.highcharts.com/docs/advanced-chart-features/boost-module) for
* details.
*
* In addition to the global `boost` option, each series has a
* [boostThreshold](#plotOptions.series.boostThreshold) that defines when the
* boost should kick in.
*
* Requires the `modules/boost.js` module.
*
* @sample {highstock} highcharts/boost/line-series-heavy-stock
* Stock chart
* @sample {highstock} highcharts/boost/line-series-heavy-dynamic
* Dynamic stock chart
* @sample highcharts/boost/line
* Line chart
* @sample highcharts/boost/line-series-heavy
* Line chart with hundreds of series
* @sample highcharts/boost/scatter
* Scatter chart
* @sample highcharts/boost/area
* Area chart
* @sample highcharts/boost/arearange
* Area range chart
* @sample highcharts/boost/column
* Column chart
* @sample highcharts/boost/columnrange
* Column range chart
* @sample highcharts/boost/bubble
* Bubble chart
* @sample highcharts/boost/heatmap
* Heat map
* @sample highcharts/boost/treemap
* Tree map
*
* @product highcharts highstock
* @requires modules/boost
* @apioption boost
*/
/**
* The chart will be boosted, if one of the series crosses its threshold and all
* the series in the chart can be boosted.
*
* @type {boolean}
* @default true
* @apioption boost.allowForce
*/
/**
* Enable or disable boost on a chart.
*
* @type {boolean}
* @default true
* @apioption boost.enabled
*/
/**
* Debugging options for boost.
* Useful for benchmarking, and general timing.
*
* @apioption boost.debug
*/
/**
* Time the series rendering.
*
* This outputs the time spent on actual rendering in the console when
* set to true.
*
* @type {boolean}
* @default false
* @apioption boost.debug.timeRendering
*/
/**
* Time the series processing.
*
* This outputs the time spent on transforming the series data to
* vertex buffers when set to true.
*
* @type {boolean}
* @default false
* @apioption boost.debug.timeSeriesProcessing
*/
/**
* Time the WebGL setup.
*
* This outputs the time spent on setting up the WebGL context,
* creating shaders, and textures.
*
* @type {boolean}
* @default false
* @apioption boost.debug.timeSetup
*/
/**
* Time the building of the k-d tree.
*
* This outputs the time spent building the k-d tree used for
* markers etc.
*
* Note that the k-d tree is built async, and runs post-rendering.
* Following, it does not affect the performance of the rendering itself.
*
* @type {boolean}
* @default false
* @apioption boost.debug.timeKDTree
*/
/**
* Show the number of points skipped through culling.
*
* When set to true, the number of points skipped in series processing
* is outputted. Points are skipped if they are closer than 1 pixel from
* each other.
*
* @type {boolean}
* @default false
* @apioption boost.debug.showSkipSummary
*/
/**
* Time the WebGL to SVG buffer copy
*
* After rendering, the result is copied to an image which is injected
* into the SVG.
*
* If this property is set to true, the time it takes for the buffer copy
* to complete is outputted.
*
* @type {boolean}
* @default false
* @apioption boost.debug.timeBufferCopy
*/
/**
* The pixel ratio for the WebGL content. If 0, the `window.devicePixelRatio` is
* used. This ensures sharp graphics on high DPI displays like Apple's Retina,
* as well as when a page is zoomed.
*
* The default is left at 1 for now, as this is a new feature that has the
* potential to break existing setups. Over time, when it has been battle
* tested, the intention is to set it to 0 by default.
*
* Another use case for this option is to set it to 2 in order to make exported
* and upscaled charts render sharp.
*
* One limitation when using the `pixelRatio` is that the line width of graphs
* is scaled down. Since the Boost module currently can only render 1px line
* widths, it is scaled down to a thin 0.5 pixels on a Retina display.
*
* @sample highcharts/boost/line-devicepixelratio
* Enable the `devicePixelRatio`
* @sample highcharts/boost/line-export-pixelratio
* Sharper graphics in export
*
* @type {number}
* @since 10.0.0
* @default 1
* @apioption boost.pixelRatio
*/
/**
* Set the series threshold for when the boost should kick in globally.
*
* Setting to e.g. 20 will cause the whole chart to enter boost mode
* if there are 20 or more series active. When the chart is in boost mode,
* every series in it will be rendered to a common canvas. This offers
* a significant speed improvement in charts with a very high
* amount of series.
*
* @type {number}
* @default 50
* @apioption boost.seriesThreshold
*/
/**
* Enable or disable GPU translations. GPU translations are faster than doing
* the translation in JavaScript.
*
* This option may cause rendering issues with certain datasets.
* Namely, if your dataset has large numbers with small increments (such as
* timestamps), it won't work correctly. This is due to floating point
* precision.
*
* @type {boolean}
* @default false
* @apioption boost.useGPUTranslations
*/
/**
* Enable or disable pre-allocation of vertex buffers.
*
* Enabling this will make it so that the binary data arrays required for
* storing the series data will be allocated prior to transforming the data
* to a WebGL-compatible format.
*
* This saves a copy operation on the order of O(n) and so is significantly more
* performant. However, this is currently an experimental option, and may cause
* visual artifacts with some datasets.
*
* As such, care should be taken when using this setting to make sure that
* it doesn't cause any rendering glitches with the given use-case.
*
* @type {boolean}
* @default false
* @apioption boost.usePreallocated
*/
/**
* Set the point threshold for when a series should enter boost mode.
*
* Setting it to e.g. 2000 will cause the series to enter boost mode when there
* are 2000 or more points in the series.
*
* To disable boosting on the series, set the `boostThreshold` to 0. Setting it
* to 1 will force boosting.
*
* Note that the [cropThreshold](plotOptions.series.cropThreshold) also affects
* this setting. When zooming in on a series that has fewer points than the
* `cropThreshold`, all points are rendered although outside the visible plot
* area, and the `boostThreshold` won't take effect.
*
* @type {number}
* @default 5000
* @requires modules/boost
* @apioption plotOptions.series.boostThreshold
*/
/**
* Sets the color blending in the boost module.
*
* @type {string}
* @default undefined
* @validvalue ["add", "multiply", "darken"]
* @requires modules/boost
* @apioption plotOptions.series.boostBlending
*/
''; // Adds doclets above to transpiled file