恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
Chart.js 数据降采样(Data Decimation)插件完全指南:LTTB 与 Min/Max 算法配置与源码原理
首页
资讯中心
/
Chart.js 数据降采样(Data Decimation)插件完全指南:LTTB 与 Min/Max 算法配置与源码原理
Chart.js 数据降采样(Data Decimation)插件完全指南:LTTB 与 Min/Max 算法配置与源码原理
发布时间:2026/9/18 9:56:31
Chart.js 数据降采样Data Decimation插件完全指南LTTB 与 Min/Max 算法配置与源码原理【免费下载链接】Chart.jsSimple HTML5 Charts using thetag项目地址: https://gitcode.com/gh_mirrors/ch/Chart.js导读本指南深入解析 Chart.js 内置的 decimation数据降采样插件——它专为**大数据量折线图line chart**设计可在图表生命周期起始阶段自动削减渲染点数从而显著提升绘制性能与交互流畅度。文章将完整覆盖插件的全部配置项、两种内置算法LTTB 与 Min/Max的适用场景、六项启用前置要求并结合仓库源码src/plugins/plugin.decimation.js与测试用例讲透降采样在何时触发、如何降、如何还原三环节的底层实现。读完你将能直接复用一个基于 10 万数据点的完整示例配置并理解在何种数据特征下应选择哪种算法。为什么需要数据降采样折线图的数据点一旦达到数万甚至十万级别Canvas 渲染和事件命中检测都会成为明显的性能瓶颈。decimation 插件的思路很直接在图表生命周期早期元素更新之前就把原始数据替换为一份精简后的数据子集让后续的解析、布局、绘制与交互都只面对少量点。该插件定义于 src/plugins/plugin.decimation.js插件 id 为decimation可通过 官方示例 直观感受默认不降采样与启用降采样后同一份 10 万点数据的渲染差异。需要强调的是decimation 与 decimation 之外的其他性能手段 不同它是有损压缩——通过牺牲部分数据细节换取渲染性能因此必须按数据类型与业务诉求权衡算法。该插件默认关闭enabled: false需要显式开启。配置选项插件的配置命名空间为options.plugins.decimation全局默认值定义于Chart.defaults.plugins.decimation。源码中插件对象的defaults字段plugin.decimation.js即承载了这些默认值。名称类型默认值描述enabledbooleanfalse是否启用降采样。注意插件默认关闭需显式开启algorithmstringmin-max使用的降采样算法可选lttb或min-max详见下文算法章节samplesnumber无仅当使用lttb算法时生效表示输出数据集中的采样点数量。默认取 canvas 宽度即每像素 1 个样本点thresholdnumber无当当前轴可见范围内的样本数大于该值时触发降采样。默认取 4 倍 canvas 宽度。注意降采样后的点数可能仍高于threshold值源码中与默认值/阈值直接相关的逻辑如下plugin.decimation.jsconst threshold options.threshold || 4 * availableWidth; if (count threshold) { // No decimation is required until we are above this threshold cleanDecimatedDataset(dataset); return; }其中availableWidth即chart.widthplugin.decimation.js。也就是说只有当可见数据点数超过 4 倍画布宽度时降采样才会被真正触发低于阈值时插件会静默跳过并顺带清理可能残留的旧降采样状态。动态切换算法与启用状态插件的三个核心配置都可以在运行时通过chart.options.plugins.decimation.xxx修改并调用chart.update()生效。官方示例data-decimation.md提供了四种可交互切换的状态// 关闭默认 chart.options.plugins.decimation.enabled false; chart.update(); // min-max 算法 chart.options.plugins.decimation.algorithm min-max; chart.options.plugins.decimation.enabled true; chart.update(); // LTTB 算法50 个采样点 chart.options.plugins.decimation.algorithm lttb; chart.options.plugins.decimation.enabled true; chart.options.plugins.decimation.samples 50; chart.update(); // LTTB 算法500 个采样点 chart.options.plugins.decimation.algorithm lttb; chart.options.plugins.decimation.enabled true; chart.options.plugins.decimation.samples 500; chart.update();enabled从true切回false时插件会通过cleanDecimatedData(chart)遍历所有数据集、移除_decimated与_data并恢复原始data属性详见下文数据还原机制因此来回切换是安全的。降采样算法原理与适用场景插件支持两种算法通过algorithm选项选择。若传入不支持的算法名插件会直接抛出Unsupported decimation algorithm xxx错误plugin.decimation.js这也意味着算法名必须严格匹配lttb或min-max。Largest Triangle Three BucketsLTTB降采样LTTB 算法能够在保留数据整体趋势的前提下将数据点数量压缩到非常少特别适合用少量点看趋势的场景例如长期监控曲线的宏观形态。其实现位于 plugin.decimation.js注释明确指出该实现基于 Sveinn Steinarsson 的 flot-downsample 项目MIT 许可。核心思路先把数据在 x 轴上大致均分为若干个桶bucket再从每个桶中挑选出能与相邻两点构成最大三角形面积的点作为代表点从而保证被选中的点是最能凸显形状的拐点。算法始终保留首点与末点const samples options.samples || availableWidth; // 如果采样数不小于数据量直接返回原始切片 if (samples count) { return data.slice(start, start count); } ... decimated[sampledIndex] data[a]; // 首点 for (i 0; i samples - 2; i) { // 计算每个桶的平均点再在与相邻点构成的三角形中找面积最大者 ... decimated[sampledIndex] maxAreaPoint; ... } decimated[sampledIndex] data[endIndex]; // 末点其中samples的取值逻辑印证了文档说法未显式指定samples时默认取availableWidthcanvas 宽度即每像素 1 个样本点。文档中的samples描述、默认行为与源码完全一致。一个值得注意的实现细节原版算法将maxArea初始化为 1而本仓库改为初始化为-1。源码注释解释了原因——三角形面积恒为非负在数据为水平直线面积恒为 0时若初始值为 1 会导致nextA永不被赋值下一轮循环中a变成undefined而崩溃。这一修复也由测试用例should not crash with uneven pointstest/specs/plugin.decimation.tests.js覆盖该用例用 15552 个不均匀点验证了不抛异常。Min/Max 降采样Min/Max 算法保留数据的峰值与谷值是极值保持型算法非常适合噪声大、必须看到尖峰信号的时序数据如振动、脉冲类监测数据。其实现位于 plugin.decimation.js。核心思路将可见数据按 x 像素坐标映射并分组同一像素列内的点只保留 y 值最小和最大的两个点连同组首、组尾点一起输出从而保证每个像素列上极值不丢失。源码注释明确说明每个区间最多输出 4 个点组首、min、max、组尾if (truncX prevX) { // 同一像素列内维护 minY / maxY 及对应下标 ... } else { // Push up to 4 points, 3 for the last interval and the first point for this interval const intermediateIndex1 Math.min(minIndex, maxIndex); const intermediateIndex2 Math.max(minIndex, maxIndex); ... decimated.push(point); // 新区间的起点 }这也是文档所述Min/Max 每个像素最多需要 4 个点的由来。对比而言LTTB输出点数由samples严格控制压缩率最高适合看趋势Min/Max输出点数随像素宽度浮动最多每像素 4 点保真度更高适合看极值/噪声信号。六项启用前置要求逐一对应源码验证文档明确指出启用该插件前必须满足以下全部要求。这些要求并非文档空谈每一条都能在插件源码的beforeElementsUpdate钩子中找到对应的守卫判断plugin.decimation.js数据集的indexAxis必须为x源码中resolve([indexAxis, chart.options.indexAxis]) y时直接跳过L220-L223。即不支持横向y 轴索引折线图文档对应链接见 line.md 的 General 章节。数据集必须是折线line数据集源码检查meta.controller.supportsDecimationL225。supportsDecimation默认在基类 core.datasetController.js 中为false仅在 controller.line.js以及 scatter 控制器中被置为true。因此 bar、doughnut 等图表类型天然不适用。X 轴必须为linear或time类型源码检查xAxis.type ! linear xAxis.type ! timeL230-L234。category等离散轴不支持相关轴文档见 linear 轴 与 time 轴。数据必须无需解析即parsing必须为false源码检查chart.options.parsing为真则跳过L236-L239。原因在于降采样算法直接以{x, y}对象形式读写数据如data[j].x、data[j].y需要数据已是解析后的内部格式。相关说明见>dataset._data data; // 原始数据存入 _data delete dataset.data; Object.defineProperty(dataset, data, { configurable: true, enumerable: true, get: function() { return this._decimated; }, // 读操作返回降采样结果 set: function(d) { this._data d; } // 写操作仍写回原始数据 });之后 Chart.js 内部及用户读取dataset.data时拿到的都是降采样后的_decimated而用户若给dataset.data赋新值会被 setter 存入_data下次更新时重新降采样。需要还原时cleanDecimatedDatasetL156-L168会删除_decimated、_data并将data重新定义为普通可写属性、恢复原始数据。该清理逻辑在插件destroy钩子L284-L286中也会执行避免图表销毁后留下悬挂引用。此外_decimated标记还会传递给折线元素折线控制器会把line._decimated !!_dataset._decimated写入元素controller.line.js而 element.line.js 的绘制快速路径useFastPath会跳过降采样数据上的复杂插值计算进一步提升渲染性能。完整可运行示例10 万数据点的降采样配置官方示例docs/samples/advanced/data-decimation.md给出了一个完整的、可直接运行的 10 万点折线图配置。它以 30 秒为间隔生成从2021-04-01T00:00:00Z开始的 10 万个{x, y}时间序列点其中绝大多数数据落在[0, 20)约 0.1% 的罕见数据落在[0, 100)——这种罕见尖峰分布恰好能体现两种算法的差异const NUM_POINTS 100000; Utils.srand(10); const start Utils.parseISODate(2021-04-01T00:00:00Z).toMillis(); const pointData []; for (let i 0; i NUM_POINTS; i) { const max Math.random() 0.001 ? 100 : 20; pointData.push({x: start (i * 30000), y: Utils.rand(0, max)}); } const decimation { enabled: false, // 先关闭示例中通过 actions 动态切换 algorithm: min-max, }; const config { type: line, data: { datasets: [{ borderColor: Utils.CHART_COLORS.red, borderWidth: 1, data: pointData, label: Large Dataset, radius: 0, }] }, options: { // 关闭动画与数据解析以获得最佳性能 animation: false, parsing: false, // 必须为 false否则插件跳过 interaction: { mode: nearest, axis: x, intersect: false }, plugins: { decimation: decimation, }, scales: { x: { type: time, // 必须为 time 或 linear ticks: { source: auto, maxRotation: 0, // 关闭刻度旋转提升性能 autoSkip: true, } } } } };该配置满足全部前置要求type: line、x 轴为time、parsing: false、数据为预解析的{x, y}对象数组且按 x 升序。示例内置的 actions 面板支持在不降采样 / min-max / LTTB(50) / LTTB(500)四种状态间切换用于对比压缩率与形态保真度。运行时动态切换的代码见上文动态切换算法与启用状态一节。测试验证算法行为的关键断言仓库测试test/specs/plugin.decimation.tests.js从多个角度锁定了插件行为可作为配置预期的重要参考采样数上限保护samples: 100大于 10 个数据点时输出仍为全部 10 个点should draw all element if sample is greater than data based on canvas width采样数精确控制samples: 7时输出恰好 7 个点should draw the specified number of elements based on canvas width阈值门槛samples: 5, threshold: 7时输出 5 个点证明阈值只决定是否触发而非输出多少should draw the specified number of elements based on threshold可见范围裁剪x 轴范围限定为 3–6 时输出仅覆盖该范围含范围前一点与范围内各点的 5 个点should draw all element only in range佐证降采样只在可见区间内进行边界健壮性15552 个不均匀点在devicePixelRatio: 1.25下不抛异常should not crash with uneven points对应 LTTB 实现中maxArea初始化的修复。这些断言与文档参数表、源码逻辑互相印证samples决定 LTTB 输出规模threshold决定是否触发可见范围决定处理的数据窗口。实践建议与注意事项数据必须预排序由于parsing: false时数据需为内部格式且按 x 升序见>【免费下载链接】Chart.jsSimple HTML5 Charts using thetag项目地址: https://gitcode.com/gh_mirrors/ch/Chart.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考