diff --git a/src/controllers/controller.bar.js b/src/controllers/controller.bar.js index cadae3fdb6c..6040c5ddf9e 100644 --- a/src/controllers/controller.bar.js +++ b/src/controllers/controller.bar.js @@ -164,7 +164,7 @@ function computeFlexCategoryTraits(index, ruler, options) { if (prev === null) { // first data: its size is double based on the next point or, // if it's also the last data, we use the scale end extremity. - prev = curr - (next === null ? ruler.end - curr : next - curr); + prev = curr - (next === null ? Math.max(curr - ruler.start, ruler.end - curr) * 2 : next - curr); } if (next === null) { diff --git a/src/scales/scale.time.js b/src/scales/scale.time.js index dfd708b2345..6d64060befd 100644 --- a/src/scales/scale.time.js +++ b/src/scales/scale.time.js @@ -361,27 +361,55 @@ function generate(min, max, capacity, options) { * Returns the right and left offsets from edges in the form of {left, right}. * Offsets are added when the `offset` option is true. */ -function computeOffsets(table, ticks, min, max, options) { +function computeOffsets(table, ticks, data, min, max, options) { + var minInterval = 1; var left = 0; var right = 0; - var upper, lower; - - if (options.offset && ticks.length) { - if (!options.time.min) { - upper = ticks.length > 1 ? ticks[1] : max; - lower = ticks[0]; - left = ( - interpolate(table, 'time', upper, 'pos') - - interpolate(table, 'time', lower, 'pos') - ) / 2; + var timestamps = []; + var timeOpts = options.time; + var i, ilen, curr, prev, length, first, last, width; + + if (options.offset) { + data.forEach(function(timestamp) { + if (timestamp >= min && timestamp <= max) { + timestamps.push(timestamp); + } + }); + + if (!options.barThickness) { + [data, ticks].forEach(function(arr) { + for (i = 0, ilen = arr.length; i < ilen; ++i) { + curr = interpolate(table, 'time', arr[i], 'pos'); + minInterval = i > 0 ? Math.min(minInterval, curr - prev) : minInterval; + prev = curr; + } + }); } - if (!options.time.max) { - upper = ticks[ticks.length - 1]; - lower = ticks.length > 1 ? ticks[ticks.length - 2] : min; - right = ( - interpolate(table, 'time', upper, 'pos') - - interpolate(table, 'time', lower, 'pos') - ) / 2; + + length = timestamps.length; + if (length) { + if (!timeOpts.min) { + first = interpolate(table, 'time', timestamps[0], 'pos'); + if (length === 1) { + width = (1 - first) * 2; + } else if (options.barThickness) { + width = interpolate(table, 'time', timestamps[1], 'pos') - first; + } else { + width = minInterval; + } + left = Math.max(width / 2 - first, 0); + } + if (!timeOpts.max) { + last = interpolate(table, 'time', timestamps[length - 1], 'pos'); + if (length === 1) { + width = last * 2; + } else if (options.barThickness) { + width = last - interpolate(table, 'time', timestamps[length - 2], 'pos'); + } else { + width = minInterval; + } + right = Math.max(width / 2 - (1 - last), 0); + } } } @@ -643,7 +671,7 @@ module.exports = function() { me._unit = timeOpts.unit || determineUnitForFormatting(ticks, timeOpts.minUnit, me.min, me.max); me._majorUnit = determineMajorUnit(me._unit); me._table = buildLookupTable(me._timestamps.data, min, max, options.distribution); - me._offsets = computeOffsets(me._table, ticks, min, max, options); + me._offsets = computeOffsets(me._table, ticks, me._timestamps.data, min, max, options); me._labelFormat = determineLabelFormat(me._timestamps.data, timeOpts); return ticksFromTimestamps(ticks, me._majorUnit); diff --git a/test/fixtures/controller.bar/bar-thickness-offset.png b/test/fixtures/controller.bar/bar-thickness-offset.png index 8dcecac88a4..74b2b33161d 100644 Binary files a/test/fixtures/controller.bar/bar-thickness-offset.png and b/test/fixtures/controller.bar/bar-thickness-offset.png differ diff --git a/test/specs/scale.time.tests.js b/test/specs/scale.time.tests.js index 964e25e080a..cefe60737c2 100755 --- a/test/specs/scale.time.tests.js +++ b/test/specs/scale.time.tests.js @@ -1276,16 +1276,35 @@ describe('Time scale tests', function() { var chart = this.chart; var scale = chart.scales.x; var options = chart.options.scales.xAxes[0]; + var minInterval; options.offset = true; chart.update(); - var numTicks = scale.ticks.length; - var firstTickInterval = scale.getPixelForTick(1) - scale.getPixelForTick(0); - var lastTickInterval = scale.getPixelForTick(numTicks - 1) - scale.getPixelForTick(numTicks - 2); + if (source === 'auto' && distribution === 'series') { + minInterval = scale.getPixelForTick(5) - scale.getPixelForTick(4); + } else { + minInterval = scale.getPixelForValue('2020') - scale.getPixelForValue('2019'); + } + + expect(scale.getPixelForValue('2017')).toBeCloseToPixel(scale.left + minInterval / 2); + expect(scale.getPixelForValue('2042')).toBeCloseToPixel(scale.left + scale.width - minInterval / 2); + }); + + it ('should add offset from the edges if offset is true and barThickness is "flex"', function() { + var chart = this.chart; + var scale = chart.scales.x; + var options = chart.options.scales.xAxes[0]; + + options.offset = true; + options.barThickness = 'flex'; + chart.update(); + + var firstInterval = scale.getPixelForValue('2019') - scale.getPixelForValue('2017'); + var lastInterval = scale.getPixelForValue('2042') - scale.getPixelForValue('2025'); - expect(scale.getPixelForValue('2017')).toBeCloseToPixel(scale.left + firstTickInterval / 2); - expect(scale.getPixelForValue('2042')).toBeCloseToPixel(scale.left + scale.width - lastTickInterval / 2); + expect(scale.getPixelForValue('2017')).toBeCloseToPixel(scale.left + firstInterval / 2); + expect(scale.getPixelForValue('2042')).toBeCloseToPixel(scale.left + scale.width - lastInterval / 2); }); it ('should not add offset if min and max extend the labels range', function() {