forked from chartjs/Chart.js
/
helpers.extras.ts
145 lines (129 loc) · 4.1 KB
/
helpers.extras.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import type {ChartMeta, PointElement} from '../../types/index.js';
import {_limitValue} from './helpers.math.js';
import {_lookupByKey} from './helpers.collection.js';
export function fontString(pixelSize: number, fontStyle: string, fontFamily: string) {
return fontStyle + ' ' + pixelSize + 'px ' + fontFamily;
}
/**
* Request animation polyfill
*/
export const requestAnimFrame = (function() {
if (typeof window === 'undefined') {
return function(callback) {
return callback();
};
}
return window.requestAnimationFrame;
}());
/**
* Throttles calling `fn` once per animation frame
* Latest arguments are used on the actual call
*/
export function throttled<TArgs extends Array<any>>(
fn: (...args: TArgs) => void,
thisArg: any,
) {
let ticking = false;
return function(...args: TArgs) {
// Save the args for use later
const argsToUse = Array.from(args) as TArgs;
if (!ticking) {
ticking = true;
requestAnimFrame.call(window, () => {
ticking = false;
fn.apply(thisArg, argsToUse);
});
}
};
}
/**
* Debounces calling `fn` for `delay` ms
*/
export function debounce<TArgs extends Array<any>>(fn: (...args: TArgs) => void, delay: number) {
let timeout;
return function(...args: TArgs) {
if (delay) {
clearTimeout(timeout);
timeout = setTimeout(fn, delay, args);
} else {
fn.apply(this, args);
}
return delay;
};
}
/**
* Converts 'start' to 'left', 'end' to 'right' and others to 'center'
* @private
*/
export const _toLeftRightCenter = (align: 'start' | 'end' | 'center') => align === 'start' ? 'left' : align === 'end' ? 'right' : 'center';
/**
* Returns `start`, `end` or `(start + end) / 2` depending on `align`. Defaults to `center`
* @private
*/
export const _alignStartEnd = (align: 'start' | 'end' | 'center', start: number, end: number) => align === 'start' ? start : align === 'end' ? end : (start + end) / 2;
/**
* Returns `left`, `right` or `(left + right) / 2` depending on `align`. Defaults to `left`
* @private
*/
export const _textX = (align: 'left' | 'right' | 'center', left: number, right: number, rtl: boolean) => {
const check = rtl ? 'left' : 'right';
return align === check ? right : align === 'center' ? (left + right) / 2 : left;
};
/**
* Return start and count of visible points.
* @private
*/
export function _getStartAndCountOfVisiblePoints(meta: ChartMeta<'line' | 'scatter'>, points: PointElement[], animationsDisabled: boolean) {
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(
// @ts-expect-error Need to type _parsed
_lookupByKey(_parsed, iScale.axis, min).lo,
// @ts-expect-error Need to fix types on _lookupByKey
animationsDisabled ? pointCount : _lookupByKey(points, axis, iScale.getPixelForValue(min)).lo),
0, pointCount - 1);
}
if (maxDefined) {
count = _limitValue(Math.max(
// @ts-expect-error Need to type _parsed
_lookupByKey(_parsed, iScale.axis, max, true).hi + 1,
// @ts-expect-error Need to fix types on _lookupByKey
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;
}