我想知道ChartJS是否公开了一个插件API,允许我们获取已被ChartJS"跨越"的null点的坐标?

For example as illustrated in this question当设置spanGaps为‘TRUE’时,ChartJS可使曲线平滑通过null个点.

所以我们可以有这样的数据点.

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],

有什么 idea 吗?

推荐答案

找出内插值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(categorylineartime等).

下面是将该解决方案应用于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>

Javascript相关问答推荐

为什么getRecord()会因为与_logger相关的错误而失败?(使用Hedera SDK)

使用续集和下拉栏显示模型属性

从mat—country—select获取整个Country数组

使用复选框在d3.js多折线图中添加或删除线条

将2D数组转换为图形

如何从隐藏/显示中删除其中一个点击?

无法使用单击按钮时的useState将数据从一个页面传递到另一个页面

以Angular 实现ng-Circle-Progress时出错:模块没有导出的成员

Angular 形式,从DOM中删除不会删除指定索引处的内容,但会删除最后一项

如何在HTMX提示符中设置默认值?

删除加载页面时不存在的元素(JavaScript)

在D3条形图中对具有相同X值的多条记录进行分组

处理app.param()中的多个参数

按特定顺序将4个数组组合在一起,按ID分组

TypeORM QueryBuilder限制联接到一条记录

bootstrap S JS赢得了REACT中的函数/加载

Chrome上的印度时区名S有问题吗?

每隔一行文本段落进行镜像(Boustrophedon)

Google OAuth 2.0库和跨域开放程序的问题-策略错误

如何动态呈现适合未知屏幕大小的最大数量的表行?苗条的