找出内插值as they are calculated by chart.js是
这不是小事,因为chart.js以图形模式执行内插
所以我们必须得到现有点的图形值
对于相关的datasets meta个,
执行内插,然后使用返回真实空间
Y轴scaling.
然而,这比试图在真实空间中以数学方式模拟插值法更安全.
查找中间值的第一件事是标识
由chart.js用来对曲线的所有值进行内插的函数;
有三个函数:_steppedInterpolation
用于
stepped个
折线图,_bezierInterpolation
如果存在tension
选项集,
默认线性插值法为_pointInLine
.
const _bezierInterpolation = Chart.helpers._bezierInterpolation,
_steppedInterpolation = Chart.helpers._steppedInterpolation,
_pointInLine = Chart.helpers._pointInLine;
请注意,如果使用模块,则需要导入helpers
模块
分开的.
重要的一点是在动画完成后执行计算
完成,因为例如贝塞尔系数(如果贝塞尔插值
被使用)在动画过程中不断重新计算,并且它们的最终
值只能在动画被
最后敲定.因此,我们在动画的
onComplete
个
处理程序:
onComplete: function({chart}){
const datasetMetas = chart.getSortedVisibleDatasetMetas();
for(const datasetMeta of datasetMetas){
if(datasetMeta.type === 'line'){
const controller = datasetMeta.controller,
spanGaps = controller.options.spanGaps,
dataRaw = controller._data;
if(spanGaps && dataRaw.includes(null)){
const gData = datasetMeta.data,
yScale = datasetMeta.yScale,
yValues = []; // the final result
const tension = controller.options.tension || controller.options.elements.line.tension;
interpolation = controller.options.stepped ? _steppedInterpolation :
tension ? _bezierInterpolation : _pointInLine;
for(let i = 0; i < gData.length; i++){
if(dataRaw[i] !== null){
yValues.push(dataRaw[i]);
}
else if(i === 0 || i ===gData.length-1){
yValues.push(null); // no interpolation for extreme points
}
else{
const pLeft = gData[i-1],
pThis = gData[i],
pRight = gData[i+1];
const xgLeft = pLeft.x, xg = pThis.x, xgRight = pRight.x,
frac = (xg - xgLeft) / (xgRight - xgLeft);
let {y: yg} = interpolation(pLeft, pRight, frac);
yValues.push(yScale.getValueForPixel(yg));
}
}
console.log(`For dataset ${controller.index}:`, yValues);
}
}
}
}
此解假定索引轴为x
,值轴为y
;
它应该工作而不管x
轴的type
(category
,
linear
、time
等).
下面是将该解决方案应用于OP示例的完整代码片段.
const _bezierInterpolation = Chart.helpers._bezierInterpolation,
_steppedInterpolation = Chart.helpers._steppedInterpolation,
_pointInLine = Chart.helpers._pointInLine;
// note: different access with modules
const config = {
type: 'line',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange", "Blue", "Yellow", "Green", "Purple", "Green"],
datasets: [{
label: '# of Votes',
data: [7, null, 11, null, 5 , null, 8, null, 3, null, 7],
spanGaps: true,
fill: true,
borderWidth: 1,
pointHitRadius: 25,
tension: 0.4
}]
},
options: {
animation:{
onComplete: function({chart}){
const datesetMetas = chart.getSortedVisibleDatasetMetas();
for(const datasetMeta of datesetMetas){
if(datasetMeta.type === 'line'){
const controller = datasetMeta.controller,
spanGaps = controller.options.spanGaps,
dataRaw = controller._data;
if(spanGaps && dataRaw.includes(null)){
const gData = datasetMeta.data,
yScale = datasetMeta.yScale,
yValues = []; // the final result
const tension = controller.options.tension || controller.options.elements.line.tension;
interpolation = controller.options.stepped ? _steppedInterpolation :
tension ? _bezierInterpolation : _pointInLine;
for(let i = 0; i < gData.length; i++){
if(dataRaw[i] !== null){
yValues.push(dataRaw[i]);
}
else if(i === 0 || i ===gData.length-1){
yValues.push(null); // no interpolation for extreme points
}
else{
const pLeft = gData[i-1],
pThis = gData[i],
pRight = gData[i+1];
const xgLeft = pLeft.x, xg = pThis.x, xgRight = pRight.x,
frac = (xg - xgLeft) / (xgRight - xgLeft);
let {y: yg} = interpolation(pLeft, pRight, frac);
yValues.push(yScale.getValueForPixel(yg));
}
}
console.log(`For dataset ${controller.index}:`, yValues);
}
}
}
}
},
scales: {
y: {
min: 0,
max: 20
}
}
}
}
const chart = new Chart('chartJSContainer', config);
<div style="min-height: 60vh">
<canvas id="chartJSContainer" style="background-color: #eee;">
</canvas>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.js" integrity="sha512-ZwR1/gSZM3ai6vCdI+LVF1zSq/5HznD3ZSTk7kajkaj4D292NLuduDCO1c/NT8Id+jE58KYLKT7hXnbtryGmMg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>