mirror of
https://github.com/chartjs/Chart.js.git
synced 2025-12-08 20:36:08 +00:00
feat: remove line element from scatter controller (#10439)
* feat: remove line element from scatter controller default config * feat: move common controllers methods to helpers and add types * feat: mark methods for scatter and line conntrollers as private * fix: fix error when showline is true at root options and add tests * feat: remove else inside scatter controller update * fix: update getStartAndCountOFVisiblePoints helper code
This commit is contained in:
parent
6feb48b5ef
commit
03e9194be5
@ -1,7 +1,7 @@
|
||||
import DatasetController from '../core/core.datasetController';
|
||||
import {isNullOrUndef} from '../helpers';
|
||||
import {_limitValue, isNumber} from '../helpers/helpers.math';
|
||||
import {_lookupByKey} from '../helpers/helpers.collection';
|
||||
import {isNumber} from '../helpers/helpers.math';
|
||||
import {_getStartAndCountOfVisiblePoints, _scaleRangesChanged} from '../helpers/helpers.extras';
|
||||
|
||||
export default class LineController extends DatasetController {
|
||||
|
||||
@ -16,12 +16,12 @@ export default class LineController extends DatasetController {
|
||||
const {dataset: line, data: points = [], _dataset} = meta;
|
||||
// @ts-ignore
|
||||
const animationsDisabled = this.chart._animationsDisabled;
|
||||
let {start, count} = getStartAndCountOfVisiblePoints(meta, points, animationsDisabled);
|
||||
let {start, count} = _getStartAndCountOfVisiblePoints(meta, points, animationsDisabled);
|
||||
|
||||
this._drawStart = start;
|
||||
this._drawCount = count;
|
||||
|
||||
if (scaleRangesChanged(meta)) {
|
||||
if (_scaleRangesChanged(meta)) {
|
||||
start = 0;
|
||||
count = points.length;
|
||||
}
|
||||
@ -133,54 +133,3 @@ LineController.overrides = {
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
function getStartAndCountOfVisiblePoints(meta, points, animationsDisabled) {
|
||||
const pointCount = points.length;
|
||||
|
||||
let start = 0;
|
||||
let count = pointCount;
|
||||
|
||||
if (meta._sorted) {
|
||||
const {iScale, _parsed} = meta;
|
||||
const axis = iScale.axis;
|
||||
const {min, max, minDefined, maxDefined} = iScale.getUserBounds();
|
||||
|
||||
if (minDefined) {
|
||||
start = _limitValue(Math.min(
|
||||
_lookupByKey(_parsed, iScale.axis, min).lo,
|
||||
animationsDisabled ? pointCount : _lookupByKey(points, axis, iScale.getPixelForValue(min)).lo),
|
||||
0, pointCount - 1);
|
||||
}
|
||||
if (maxDefined) {
|
||||
count = _limitValue(Math.max(
|
||||
_lookupByKey(_parsed, iScale.axis, max, true).hi + 1,
|
||||
animationsDisabled ? 0 : _lookupByKey(points, axis, iScale.getPixelForValue(max), true).hi + 1),
|
||||
start, pointCount) - start;
|
||||
} else {
|
||||
count = pointCount - start;
|
||||
}
|
||||
}
|
||||
|
||||
return {start, count};
|
||||
}
|
||||
|
||||
function scaleRangesChanged(meta) {
|
||||
const {xScale, yScale, _scaleRanges} = meta;
|
||||
const newRanges = {
|
||||
xmin: xScale.min,
|
||||
xmax: xScale.max,
|
||||
ymin: yScale.min,
|
||||
ymax: yScale.max
|
||||
};
|
||||
if (!_scaleRanges) {
|
||||
meta._scaleRanges = newRanges;
|
||||
return true;
|
||||
}
|
||||
const changed = _scaleRanges.xmin !== xScale.min
|
||||
|| _scaleRanges.xmax !== xScale.max
|
||||
|| _scaleRanges.ymin !== yScale.min
|
||||
|| _scaleRanges.ymax !== yScale.max;
|
||||
|
||||
Object.assign(_scaleRanges, newRanges);
|
||||
return changed;
|
||||
}
|
||||
|
||||
@ -1,7 +1,125 @@
|
||||
import LineController from './controller.line';
|
||||
import DatasetController from '../core/core.datasetController';
|
||||
import {isNullOrUndef} from '../helpers';
|
||||
import {isNumber} from '../helpers/helpers.math';
|
||||
import {_getStartAndCountOfVisiblePoints, _scaleRangesChanged} from '../helpers/helpers.extras';
|
||||
import registry from '../core/core.registry';
|
||||
|
||||
export default class ScatterController extends LineController {
|
||||
export default class ScatterController extends DatasetController {
|
||||
update(mode) {
|
||||
const meta = this._cachedMeta;
|
||||
const {data: points = []} = meta;
|
||||
// @ts-ignore
|
||||
const animationsDisabled = this.chart._animationsDisabled;
|
||||
let {start, count} = _getStartAndCountOfVisiblePoints(meta, points, animationsDisabled);
|
||||
|
||||
this._drawStart = start;
|
||||
this._drawCount = count;
|
||||
|
||||
if (_scaleRangesChanged(meta)) {
|
||||
start = 0;
|
||||
count = points.length;
|
||||
}
|
||||
|
||||
if (this.options.showLine) {
|
||||
|
||||
const {dataset: line, _dataset} = meta;
|
||||
|
||||
// Update Line
|
||||
line._chart = this.chart;
|
||||
line._datasetIndex = this.index;
|
||||
line._decimated = !!_dataset._decimated;
|
||||
line.points = points;
|
||||
|
||||
const options = this.resolveDatasetElementOptions(mode);
|
||||
options.segment = this.options.segment;
|
||||
this.updateElement(line, undefined, {
|
||||
animated: !animationsDisabled,
|
||||
options
|
||||
}, mode);
|
||||
}
|
||||
|
||||
// Update Points
|
||||
this.updateElements(points, start, count, mode);
|
||||
}
|
||||
|
||||
addElements() {
|
||||
const {showLine} = this.options;
|
||||
|
||||
if (!this.datasetElementType && showLine) {
|
||||
this.datasetElementType = registry.getElement('line');
|
||||
}
|
||||
|
||||
super.addElements();
|
||||
}
|
||||
|
||||
updateElements(points, start, count, mode) {
|
||||
const reset = mode === 'reset';
|
||||
const {iScale, vScale, _stacked, _dataset} = this._cachedMeta;
|
||||
const firstOpts = this.resolveDataElementOptions(start, mode);
|
||||
const sharedOptions = this.getSharedOptions(firstOpts);
|
||||
const includeOptions = this.includeOptions(mode, sharedOptions);
|
||||
const iAxis = iScale.axis;
|
||||
const vAxis = vScale.axis;
|
||||
const {spanGaps, segment} = this.options;
|
||||
const maxGapLength = isNumber(spanGaps) ? spanGaps : Number.POSITIVE_INFINITY;
|
||||
const directUpdate = this.chart._animationsDisabled || reset || mode === 'none';
|
||||
let prevParsed = start > 0 && this.getParsed(start - 1);
|
||||
|
||||
for (let i = start; i < start + count; ++i) {
|
||||
const point = points[i];
|
||||
const parsed = this.getParsed(i);
|
||||
const properties = directUpdate ? point : {};
|
||||
const nullData = isNullOrUndef(parsed[vAxis]);
|
||||
const iPixel = properties[iAxis] = iScale.getPixelForValue(parsed[iAxis], i);
|
||||
const vPixel = properties[vAxis] = reset || nullData ? vScale.getBasePixel() : vScale.getPixelForValue(_stacked ? this.applyStack(vScale, parsed, _stacked) : parsed[vAxis], i);
|
||||
|
||||
properties.skip = isNaN(iPixel) || isNaN(vPixel) || nullData;
|
||||
properties.stop = i > 0 && (Math.abs(parsed[iAxis] - prevParsed[iAxis])) > maxGapLength;
|
||||
if (segment) {
|
||||
properties.parsed = parsed;
|
||||
properties.raw = _dataset.data[i];
|
||||
}
|
||||
|
||||
if (includeOptions) {
|
||||
properties.options = sharedOptions || this.resolveDataElementOptions(i, point.active ? 'active' : mode);
|
||||
}
|
||||
|
||||
if (!directUpdate) {
|
||||
this.updateElement(point, i, properties, mode);
|
||||
}
|
||||
|
||||
prevParsed = parsed;
|
||||
}
|
||||
|
||||
this.updateSharedOptions(sharedOptions, mode, firstOpts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @protected
|
||||
*/
|
||||
getMaxOverflow() {
|
||||
const meta = this._cachedMeta;
|
||||
const data = meta.data || [];
|
||||
|
||||
if (!this.options.showLine) {
|
||||
let max = 0;
|
||||
for (let i = data.length - 1; i >= 0; --i) {
|
||||
max = Math.max(max, data[i].size(this.resolveDataElementOptions(i)) / 2);
|
||||
}
|
||||
return max > 0 && max;
|
||||
}
|
||||
|
||||
const dataset = meta.dataset;
|
||||
const border = dataset.options && dataset.options.borderWidth || 0;
|
||||
|
||||
if (!data.length) {
|
||||
return border;
|
||||
}
|
||||
|
||||
const firstPoint = data[0].size(this.resolveDataElementOptions(0));
|
||||
const lastPoint = data[data.length - 1].size(this.resolveDataElementOptions(data.length - 1));
|
||||
return Math.max(border, firstPoint, lastPoint) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
ScatterController.id = 'scatter';
|
||||
@ -10,6 +128,8 @@ ScatterController.id = 'scatter';
|
||||
* @type {any}
|
||||
*/
|
||||
ScatterController.defaults = {
|
||||
datasetElementType: false,
|
||||
dataElementType: 'point',
|
||||
showLine: false,
|
||||
fill: false
|
||||
};
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import {_limitValue} from './helpers.math';
|
||||
import {_lookupByKey} from './helpers.collection';
|
||||
|
||||
export function fontString(pixelSize, fontStyle, fontFamily) {
|
||||
return fontStyle + ' ' + pixelSize + 'px ' + fontFamily;
|
||||
@ -87,3 +89,68 @@ export const _textX = (align, left, right, rtl) => {
|
||||
const check = rtl ? 'left' : 'right';
|
||||
return align === check ? right : align === 'center' ? (left + right) / 2 : left;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return start and count of visible points.
|
||||
* @param {object} meta - dataset meta.
|
||||
* @param {array} points - array of point elements.
|
||||
* @param {boolean} animationsDisabled - if true animation is disabled.
|
||||
* @returns {{start: number; count: number}}
|
||||
* @private
|
||||
*/
|
||||
export function _getStartAndCountOfVisiblePoints(meta, points, animationsDisabled) {
|
||||
const pointCount = points.length;
|
||||
|
||||
let start = 0;
|
||||
let count = pointCount;
|
||||
|
||||
if (meta._sorted) {
|
||||
const {iScale, _parsed} = meta;
|
||||
const axis = iScale.axis;
|
||||
const {min, max, minDefined, maxDefined} = iScale.getUserBounds();
|
||||
|
||||
if (minDefined) {
|
||||
start = _limitValue(Math.min(
|
||||
_lookupByKey(_parsed, iScale.axis, min).lo,
|
||||
animationsDisabled ? pointCount : _lookupByKey(points, axis, iScale.getPixelForValue(min)).lo),
|
||||
0, pointCount - 1);
|
||||
}
|
||||
if (maxDefined) {
|
||||
count = _limitValue(Math.max(
|
||||
_lookupByKey(_parsed, iScale.axis, max, true).hi + 1,
|
||||
animationsDisabled ? 0 : _lookupByKey(points, axis, iScale.getPixelForValue(max), true).hi + 1),
|
||||
start, pointCount) - start;
|
||||
} else {
|
||||
count = pointCount - start;
|
||||
}
|
||||
}
|
||||
|
||||
return {start, count};
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the scale ranges have changed.
|
||||
* @param {object} meta - dataset meta.
|
||||
* @returns {boolean}
|
||||
* @private
|
||||
*/
|
||||
export function _scaleRangesChanged(meta) {
|
||||
const {xScale, yScale, _scaleRanges} = meta;
|
||||
const newRanges = {
|
||||
xmin: xScale.min,
|
||||
xmax: xScale.max,
|
||||
ymin: yScale.min,
|
||||
ymax: yScale.max
|
||||
};
|
||||
if (!_scaleRanges) {
|
||||
meta._scaleRanges = newRanges;
|
||||
return true;
|
||||
}
|
||||
const changed = _scaleRanges.xmin !== xScale.min
|
||||
|| _scaleRanges.xmax !== xScale.max
|
||||
|| _scaleRanges.ymin !== yScale.min
|
||||
|| _scaleRanges.ymax !== yScale.max;
|
||||
|
||||
Object.assign(_scaleRanges, newRanges);
|
||||
return changed;
|
||||
}
|
||||
|
||||
@ -61,4 +61,107 @@ describe('Chart.controllers.scatter', function() {
|
||||
await jasmine.triggerMouseEvent(chart, 'mousemove', point);
|
||||
expect(chart.tooltip.body.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should not create line element by default', function() {
|
||||
var chart = window.acquireChart({
|
||||
type: 'scatter',
|
||||
data: {
|
||||
datasets: [{
|
||||
data: [{
|
||||
x: 10,
|
||||
y: 15
|
||||
},
|
||||
{
|
||||
x: 12,
|
||||
y: 10
|
||||
}],
|
||||
label: 'dataset1'
|
||||
},
|
||||
{
|
||||
data: [{
|
||||
x: 20,
|
||||
y: 10
|
||||
},
|
||||
{
|
||||
x: 4,
|
||||
y: 8
|
||||
}],
|
||||
label: 'dataset2'
|
||||
}]
|
||||
},
|
||||
});
|
||||
|
||||
var meta = chart.getDatasetMeta(0);
|
||||
expect(meta.dataset instanceof Chart.elements.LineElement).toBe(false);
|
||||
});
|
||||
|
||||
it('should create line element if showline is true at datasets options', function() {
|
||||
var chart = window.acquireChart({
|
||||
type: 'scatter',
|
||||
data: {
|
||||
datasets: [{
|
||||
showLine: true,
|
||||
data: [{
|
||||
x: 10,
|
||||
y: 15
|
||||
},
|
||||
{
|
||||
x: 12,
|
||||
y: 10
|
||||
}],
|
||||
label: 'dataset1'
|
||||
},
|
||||
{
|
||||
data: [{
|
||||
x: 20,
|
||||
y: 10
|
||||
},
|
||||
{
|
||||
x: 4,
|
||||
y: 8
|
||||
}],
|
||||
label: 'dataset2'
|
||||
}]
|
||||
},
|
||||
});
|
||||
|
||||
var meta = chart.getDatasetMeta(0);
|
||||
expect(meta.dataset instanceof Chart.elements.LineElement).toBe(true);
|
||||
});
|
||||
|
||||
it('should create line element if showline is true at root options', function() {
|
||||
var chart = window.acquireChart({
|
||||
type: 'scatter',
|
||||
data: {
|
||||
datasets: [{
|
||||
data: [{
|
||||
x: 10,
|
||||
y: 15
|
||||
},
|
||||
{
|
||||
x: 12,
|
||||
y: 10
|
||||
}],
|
||||
label: 'dataset1'
|
||||
},
|
||||
{
|
||||
data: [{
|
||||
x: 20,
|
||||
y: 10
|
||||
},
|
||||
{
|
||||
x: 4,
|
||||
y: 8
|
||||
}],
|
||||
label: 'dataset2'
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
showLine: true
|
||||
}
|
||||
});
|
||||
|
||||
var meta = chart.getDatasetMeta(0);
|
||||
expect(meta.dataset instanceof Chart.elements.LineElement).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user