ChartJS - 折线图中重叠点的处理

问题描述 投票:0回答:3

在我的一个折线图中,我遇到了某些点重叠的情况(即具有完全相同的

x,y
坐标)。这是预期的行为,但我有兴趣找到一种方法来潜在地抵消/抖动任何重叠的点,以便不隐藏任何点。有没有人遇到过折线图上重叠点的类似情况?注意:我不能使用
scatter
类型,因为我的 x 轴是类别。

这是一个演示重叠的小提琴(带有我数据集的缩小样本):https://jsfiddle.net/quanda412/407dcoL9/19/

在深入研究文档后,我的第一直觉是利用其中一个 ChartJS 插件挂钩 并在该函数中迭代我的数据集以搜索任何重叠的实例,然后更改 x 或 y 定位。我在其他地方看到这被称为“抖动”,而且似乎一些图表库支持开箱即用的抖动功能。

无论如何,我的数据集非常大,当我对其进行迭代时,例如在

beforeDatasetUpdate
挂钩中,图表不仅会受到巨大的性能影响,而且点数也不会按预期进行调整。

我尝试在

beforeDatasetDraw
钩子中抖动的代码示例:

beforeDatasetDraw: chart => {
    const { datasets } = chart.config.data;
    const coordinateMap = []; // holds array of unique coord objects
    datasets.forEach(d => {
        let elements = d._meta[5].data;
        elements.forEach((el, i) => {
            let { x, y } = el._model;
            const overlap = coordinateMap.find(coord => coord.x === x && coord.y === y);
            if (overlap) { // Overlap detected!
                // Update coordinate map
                x += 1;
                y += 1;
                // Jitter the x,y positioning - not working!
                d._meta[5].data[i]._model.x = x;
                d._meta[5].data[i]._model.y = y;
            }
            coordinateMap.push({ x, y });
       });
   });
}

在这里你可以看到重叠,灰点中的灰点:

chart.js overlap
3个回答
2
投票

我能够使用这个自定义

beforeDatasetDraw
插件实现有效的x轴抖动:

 beforeDatasetDraw(chart, args) {
  if (chart.animating || chart.$deferred.loaded) {
    const { index: dataIndex, meta } = args;
    const points = meta.data.map(el => ({ x: el._model.x, y: el._model.y }));
    const { length: dsLength } = chart.data.datasets;
    const adjustedMap = []; // keeps track of adjustments to prevent double offsets

    for (let datasetIndex = 0; datasetIndex < dsLength; datasetIndex += 1) {
      if (dataIndex !== datasetIndex) {
        const datasetMeta = chart.getDatasetMeta(datasetIndex);
        datasetMeta.data.forEach(el => {
          const overlap = points.find(point => point.x === el._model.x && point.y === el._model.y);
          if (overlap) {
            const adjusted = adjustedMap.find(item => item.datasetIndex === datasetIndex && item.dataIndex === dataIndex);
            if (!adjusted && datasetIndex % 2) { el._model.x += 7; } else { el._model.x -= 7; }
              adjustedMap.push({ datasetIndex, dataIndex });
          }
        });
       }
     }
   }
 }

这会产生抖动效果,如下所示:


1
投票

感谢全达。我修改了上面的代码以支持 IE 浏览器。

var jitterEffectPlugin = {
                        beforeDatasetDraw: function (ctx, args) {
                            if (ctx.animating) {
                                var _args = args,
                                dataIndex = _args.index,
                                meta = _args.meta;
                                var points = meta.data.map(function (el) {
                                  return {
                                    x: el._model.x,
                                    y: el._model.y
                                  };
                                });
                                var dsLength = ctx.data.datasets.length;
                                var adjustedMap = []; // keeps track of adjustments to prevent double offsets
        
                                for (var datasetIndex = 0; datasetIndex < dsLength; datasetIndex += 1) {
                                  if (dataIndex !== datasetIndex) {
                                    var datasetMeta = ctx.getDatasetMeta(datasetIndex);
                                    datasetMeta.data.forEach(function (el) {
                                      var overlapFilter = points.filter(function (point) {
                                        return point.x === el._model.x && point.y === el._model.y;
                                      });

                                      var overlap = false;
                                      var overObj = JSON.parse(JSON.stringify(overlapFilter));
                                      for (var i = 0; i < overObj.length; i++) {
                                          if(overObj[i]['x'] === el._model.x && overObj[i]['y'] === el._model.y){
                                            overlap = true;
                                            break;
                                          }
                                      }
                                      if (overlap) {
                                        var adjusted = false;
                                        var adjustedFilter = adjustedMap.filter(function (item) {
                                            return item.datasetIndex === datasetIndex && item.dataIndex === dataIndex;
                                        });
                                        var adjObj = JSON.parse(JSON.stringify(adjustedFilter));
                                          for (var i = 0; i < adjObj.length; i++) {
                                              if(adjObj[i]['datasetIndex'] === datasetIndex && adjObj[i]['dataIndex'] === dataIndex){
                                                adjusted = true;
                                                break;
                                              }
                                          }

                                        if (!adjusted && datasetIndex % 2) {
                                          el._model.x += 7;
                                        } else {
                                          el._model.x -= 7;
                                        }
        
                                        adjustedMap.push({
                                          datasetIndex: datasetIndex,
                                          dataIndex: dataIndex
                                        });
                                      }
                                    });
                                  }
                                }
                            }
                        }
                    }

                    Chart.pluginService.register(jitterEffectPlugin);

0
投票

一定是 Chartjs 的变化,我不得不为当前版本修改这个插件。基本上我只是从 el._model.x 和 el.model.y 中删除了 _model 并添加了 id:

var jitterEffectsPlugin = {
    id: 'jitterEffects',
    beforeDatasetDraw: function (ctx, args) {
        var _args = args,
            dataIndex = _args.index,
            meta = _args.meta;
        var points = meta.data.map(function (el) {
            return {
                x: el.x,
                y: el.y
            };
        });
        var dsLength = ctx.data.datasets.length;
        var adjustedMap = []; // keeps track of adjustments to prevent double offsets

        for (var datasetIndex = 0; datasetIndex < dsLength; datasetIndex += 1) {
            if (dataIndex !== datasetIndex) {
                var datasetMeta = ctx.getDatasetMeta(datasetIndex);
                datasetMeta.data.forEach(function (el) {
                    var overlapFilter = points.filter(function (point) {
                        return point.x === el.x && point.y === el.y;
                    });

                    var overlap = false;
                    var overObj = JSON.parse(JSON.stringify(overlapFilter));
                    for (var i = 0; i < overObj.length; i++) {
                        if(overObj[i]['x'] === el.x && overObj[i]['y'] === el.y){
                            overlap = true;
                            break;
                        }
                    }
                    if (overlap) {
                        var adjusted = false;
                        var adjustedFilter = adjustedMap.filter(function (item) {
                            return item.datasetIndex === datasetIndex && item.dataIndex === dataIndex;
                        });
                        var adjObj = JSON.parse(JSON.stringify(adjustedFilter));
                        for (var i = 0; i < adjObj.length; i++) {
                            if(adjObj[i]['datasetIndex'] === datasetIndex && adjObj[i]['dataIndex'] === dataIndex){
                                adjusted = true;
                                break;
                            }
                        }

                        if (!adjusted && datasetIndex % 2) {
                            el.x += 7;
                        } else {
                            el.x -= 7;
                        }

                        adjustedMap.push({
                            datasetIndex: datasetIndex,
                            dataIndex: dataIndex
                        });
                    }
                });
            }
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.