diff --git a/src/api/options/time-scale-options-defaults.ts b/src/api/options/time-scale-options-defaults.ts index d28982915e..ef38b8c30a 100644 --- a/src/api/options/time-scale-options-defaults.ts +++ b/src/api/options/time-scale-options-defaults.ts @@ -4,6 +4,7 @@ export const timeScaleOptionsDefaults: HorzScaleOptions = { rightOffset: 0, barSpacing: 6, minBarSpacing: 0.5, + maxBarSpacing: 0, fixLeftEdge: false, fixRightEdge: false, lockVisibleTimeRangeOnResize: false, diff --git a/src/model/time-scale.ts b/src/model/time-scale.ts index a02d237dbd..631951625c 100644 --- a/src/model/time-scale.ts +++ b/src/model/time-scale.ts @@ -81,6 +81,15 @@ export interface HorzScaleOptions { */ minBarSpacing: number; + /** + * The maximum space between bars in pixels. + * + * Has no effect if value is set to `0`. + * + * @defaultValue `0` + */ + maxBarSpacing: number; + /** * Prevent scrolling to the left of the first bar. * @@ -300,8 +309,8 @@ export class TimeScale implements ITimeScale { this._model.setRightOffset(options.rightOffset); } - if (options.minBarSpacing !== undefined) { - // yes, if we apply min bar spacing then we need to correct bar spacing + if (options.minBarSpacing !== undefined || options.maxBarSpacing !== undefined) { + // yes, if we apply bar spacing constrains then we need to correct bar spacing // the easiest way is to apply it once again this._model.setBarSpacing(options.barSpacing ?? this._barSpacing); } @@ -844,20 +853,20 @@ export class TimeScale implements ITimeScale { } private _correctBarSpacing(): void { - const minBarSpacing = this._minBarSpacing(); - - if (this._barSpacing < minBarSpacing) { - this._barSpacing = minBarSpacing; + const barSpacing = clamp(this._barSpacing, this._minBarSpacing(), this._maxBarSpacing()); + if (this._barSpacing !== barSpacing) { + this._barSpacing = barSpacing; this._visibleRangeInvalidated = true; } + } - if (this._width !== 0) { - // make sure that this (1 / Constants.MinVisibleBarsCount) >= coeff in max bar spacing (it's 0.5 here) - const maxBarSpacing = this._width * 0.5; - if (this._barSpacing > maxBarSpacing) { - this._barSpacing = maxBarSpacing; - this._visibleRangeInvalidated = true; - } + private _maxBarSpacing(): number { + if (this._options.maxBarSpacing > 0) { + // option takes precedance + return this._options.maxBarSpacing; + } else { + // half of the width is default value for maximum bar spacing + return this._width * 0.5; } } diff --git a/tests/e2e/graphics/test-cases/time-scale/max-bar-spacing.js b/tests/e2e/graphics/test-cases/time-scale/max-bar-spacing.js new file mode 100644 index 0000000000..069bdbbe55 --- /dev/null +++ b/tests/e2e/graphics/test-cases/time-scale/max-bar-spacing.js @@ -0,0 +1,41 @@ +function generateBar(i, startValue, target) { + const step = (i % 20) / 1000; + const base = i + startValue; + target.open = base * (1 - step); + target.high = base * (1 + 2 * step); + target.low = base * (1 - 2 * step); + target.close = base * (1 + step); +} + +function generateData(startValue) { + const res = []; + const time = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 0)); + for (let i = 0; i < 25; ++i) { + const item = { + time: time.getTime() / 1000, + }; + time.setUTCDate(time.getUTCDate() + 1); + + generateBar(i, startValue, item); + res.push(item); + } + return res; +} + +function runTestCase(container) { + const chart = window.chart = LightweightCharts.createChart(container, { + timeScale: { + minBarSpacing: 10, + maxBarSpacing: 10, + }, + layout: { attributionLogo: false }, + width: 500, + }); + + const mainSeries = chart.addSeries(LightweightCharts.BarSeries); + + const data = generateData(0); + mainSeries.setData(data); + + chart.timeScale().setVisibleLogicalRange({ from: 0, to: data.length }); +}