我想知道 ChartJS 是否公开了一个插件 API,允许我们获取已被 ChartJS“跨越”的
null
点的坐标?
例如,如本问题所示 当将
null
设置为“true”时,ChartJS 可以通过 spanGaps
点实现平滑曲线。
所以我们可以有这样的数据点。
data: [7, null, 11, null, 5 , null, 8, null, 3, null, 7],
对应这些标签。
["Red", "Blue", "Yellow", "Green", "Purple", "Orange", "Blue", "Yellow", "Green", "Purple", "Green"],
有没有办法(也许通过插件API)来获取空值?
因此,对于图表 JS 绘制的给定二次曲线:
data: [7, null, 11, null, 5 , null, 8, null, 3, null, 7],
我们可以调用例如:
const arr = ChartJSPluginAPI.deriveNulls(data);
并得到类似的东西:
data: [7, 8, 11, 6, 5 , 6, 8, 4, 3, 5, 7],
想法?
找到由图表.js 计算的插值是 这并不简单,因为 Chart.js 以图形模式执行插值 只是,所以我们必须获得现有点的图形值 对于相关的数据集元, 执行插值,然后回到真实空间,使用 y 轴缩放。 然而,这比尝试在真实空间中以数学方式模拟插值更安全。
寻找中间值的第一件事是确定 Chart.js 使用函数对曲线的所有值进行插值; 共有三个功能:_steppedInterpolation
_bezierInterpolation
如果设置了
tension
选项, 默认线性插值为
_pointInLine
。
const _bezierInterpolation = Chart.helpers._bezierInterpolation,
_steppedInterpolation = Chart.helpers._steppedInterpolation,
_pointInLine = Chart.helpers._pointInLine;
注意,如果使用模块,需要导入helpers
模块 分别。很重要的一点是在动画结束后进行计算 完成,因为例如贝塞尔系数(如果贝塞尔插值 被使用)在动画过程中不断重新计算,并且它们的最终 动画结束后才能获取值 最终确定。因此,我们在动画中实现计算 处理者:
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
; 无论
type
轴的
x
如何(
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>