十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Chart.js 动画进度条实战:使用 animation 回调同步外部进度 UI

Chart.js 动画进度条实战:使用 animation 回调同步外部进度 UI Chart.js 动画进度条实战使用 animation 回调同步外部进度 UI【免费下载链接】Chart.jsSimple HTML5 Charts using thetag项目地址: https://gitcode.com/gh_mirrors/ch/Chart.js导读本文以 Chart.js 官方示例 progress-bar.md 为核心讲解如何利用options.animation下的onProgress与onComplete回调把图表动画的实时进度同步到页面上的原生progress进度条。你将掌握动画回调的完整参数语义currentStep、numSteps、initial、Chart.js 内部动画驱动机制core.animator.js 的 progress/complete 事件分发以及如何把官方示例改写成脱离文档工具链、可直接落地的独立实现。示例概览用两条进度条区分首次动画与后续动画Chart.js 默认所有图表创建和更新都带动画。官方 advanced 示例在页面顶部放置了两个原生progress元素progress idinitialProgress max1 value0 stylewidth: 100%/progress progress idanimationProgress max1 value0 stylewidth: 100%/progressinitialProgress反映图表首次创建时的初始动画进度animationProgress反映每次chart.update()触发的后续动画进度。由于两个进度条的max1回调中只需把已完成步数 / 总步数的比值赋给value即可。完整示例代码剖析官方示例由三个代码块构成actions页面操作按钮逻辑、setup数据与 DOM 引用、config图表配置。以下为完整代码示例中// block:...注释是文档构建工具的分块标记实际项目中可去掉。1. 操作按钮actions 块const actions [ { name: Randomize, handler(chart) { chart.data.datasets.forEach(dataset { dataset.data Utils.numbers({count: chart.data.labels.length, min: -100, max: 100}); }); chart.update(); } }, { name: Add Dataset, handler(chart) { const data chart.data; const dsColor Utils.namedColor(chart.data.datasets.length); const newDataset { label: Dataset (data.datasets.length 1), backgroundColor: Utils.transparentize(dsColor, 0.5), borderColor: dsColor, data: Utils.numbers({count: data.labels.length, min: -100, max: 100}), }; chart.data.datasets.push(newDataset); chart.update(); } }, { name: Add Data, handler(chart) { const data chart.data; if (data.datasets.length 0) { data.labels Utils.months({count: data.labels.length 1}); for (let index 0; index data.datasets.length; index) { data.datasets[index].data.push(Utils.rand(-100, 100)); } chart.update(); } } }, { name: Remove Dataset, handler(chart) { chart.data.datasets.pop(); chart.update(); } }, { name: Remove Data, handler(chart) { chart.data.labels.splice(-1, 1); // remove the label first chart.data.datasets.forEach(dataset { dataset.data.pop(); }); chart.update(); } } ];需要说明的是actions数组本身不是 Chart.js 的 API而是文档站通过插件把每个name转成按钮并绑定onClick监听器。Utils.numbers、Utils.months、Utils.rand、Utils.namedColor、Utils.transparentize等辅助函数定义在 docs/scripts/utils.js仅供文档示例生成随机数据使用例如rand采用可播种的线性同余伪随机算法numbers支持min/max/count/continuity等参数并不随库发布官方明确提示不要在生产环境依赖该文件。在自己项目里用数组字面量或自己的数据生成逻辑替换即可。2. 数据与 DOM 引用setup 块const initProgress document.getElementById(initialProgress); const progress document.getElementById(animationProgress); const DATA_COUNT 7; const NUMBER_CFG {count: DATA_COUNT, min: -100, max: 100}; const labels Utils.months({count: 7}); const data { labels: labels, datasets: [ { label: Dataset 1, data: Utils.numbers(NUMBER_CFG), borderColor: Utils.CHART_COLORS.red, backgroundColor: Utils.transparentize(Utils.CHART_COLORS.red, 0.5), }, { label: Dataset 2, data: Utils.numbers(NUMBER_CFG), borderColor: Utils.CHART_COLORS.blue, backgroundColor: Utils.transparentize(Utils.CHART_COLORS.blue, 0.5), } ] };两个数据集分别使用红蓝配色Utils.CHART_COLORS.red为rgb(255, 99, 132)blue为rgb(54, 162, 235)数据范围 -100 ~ 100x 轴为 7 个月份标签对应 docs/general/data-structures.md 中的labels结构。3. 图表配置config 块——核心所在const config { type: line, data: data, options: { animation: { duration: 2000, onProgress: function(context) { if (context.initial) { initProgress.value context.currentStep / context.numSteps; } else { progress.value context.currentStep / context.numSteps; } }, onComplete: function(context) { if (context.initial) { console.log(Initial animation finished); } else { console.log(animation finished); } } }, interaction: { mode: nearest, axis: x, intersect: false }, plugins: { title: { display: true, text: Chart.js Line Chart - Animation Progress Bar } }, }, };关键点animation.duration: 2000把动画时长设置为 2000ms默认值为 1000ms见 core.animations.defaults.js 中的duration: 1000。onProgress在动画的每一帧被调用这里根据context.initial决定更新哪条进度条。onComplete在动画全部结束时被调用示例仅用于打印日志实际可替换为任何动画结束后的联动逻辑。interaction与plugins.title与动画进度无关属于示例图表本身的交互与标题配置。动画回调核心onProgress / onComplete 与回调上下文对象onProgress与onComplete只能配置在主动画配置options.animation下见 docs/configuration/animations.md 的 Animation Callbacks 一节。两者接收同一个回调参数对象字段语义如下字段类型说明chartChart当前图表实例currentStepnumber当前动画已进行的步数毫秒计numStepsnumber本次动画开始时动画的总步数总毫秒数initialboolean是否为图表的首次初始动画进度比值即currentStep / numSteps取值在 0 ~ 1 之间正好对应progress元素的value语义。该对象结构同时记载于 docs/configuration/animations.md示例代码与文档完全一致{ // Chart object chart: Chart, // Number of animations still in progress currentStep: number, // true for the initial animation of the chart initial: boolean, // Total number of animations at the start of current animation numSteps: number, }一个最简的进度条同步实现不区分首次/后续const chart new Chart(ctx, { type: line, data: data, options: { animation: { onProgress: function(animation) { progress.value animation.currentStep / animation.numSteps; } } } });源码级原理Animator 如何驱动 progress / complete 事件要理解currentStep、numSteps、initial从何而来需要看两个核心文件。Animator 的_notify与事件分发src/core/core.animator.js 中维护了一个全局单例Animatorexport default new Animator()它通过requestAnimFrame驱动的_update循环逐帧推进所有图表的动画。每次绘制完成后会触发progress事件全部动画项结束后触发complete事件_notify(chart, anims, date, type) { const callbacks anims.listeners[type]; const numSteps anims.duration; callbacks.forEach(fn fn({ chart, initial: anims.initial, numSteps, currentStep: Math.min(date - anims.start, numSteps) })); }可见numSteps等于anims.duration即本次动画的总时长毫秒。currentStep等于当前时间戳减去动画开始时间戳并用Math.min钳制到不超过numSteps保证进度比不会超过 1。initial取自anims.initial在动画全部完成_notify(..., complete)之后会被置为falseanims.initial false所以只有图表第一次渲染时的动画会带着initial: true。在_update中每个动画帧item.tick(date)后调用chart.draw()随后this._notify(chart, anims, date, progress)——这正是onProgress在每一帧绘制后被调用的出处当items清空时触发completeif (!items.length) { anims.running false; this._notify(chart, anims, date, complete); anims.initial false; }此外Animator.start中anims.duration取所有动画项时长的最大值items.reduce((acc, cur) Math.max(acc, cur._duration), 0)因此多个属性并行动画时进度条总步数等于最长动画的时长。Controller 注册回调监听src/core/core.controller.js 在图表构造时把onComplete/onProgress包装成对chart.options.animation的读取与调用function onAnimationsComplete(context) { const chart context.chart; const animationOptions chart.options.animation; chart.notifyPlugins(afterRender); callCallback(animationOptions animationOptions.onComplete, [context], chart); } function onAnimationProgress(context) { const chart context.chart; const animationOptions chart.options.animation; callCallback(animationOptions animationOptions.onProgress, [context], chart); }并在构造函数中通过animator.listen(this, complete, onAnimationsComplete)与animator.listen(this, progress, onAnimationProgress)订阅事件core.controller.js。注意onComplete之前还会先执行chart.notifyPlugins(afterRender)即渲染完成后动画回调。测试用例的印证test/specs/core.animator.tests.js 中should fire onProgress for each draw用 250ms 动画验证了上述行为const progress (animation) { count; expect(animation.numSteps).toEqual(250); expect(animation.currentStep 250).toBeTrue(); };即numSteps严格等于配置的duration250mscurrentStep不超过numStepsonProgress每帧触发且次数与afterDraw插件调用次数一致最后以onComplete收尾。这条测试可作为回调语义的权威行为契约。从示例到实战脱离 Utils 的独立可运行实现把官方示例落地到自己的页面时需要把progress元素、图表初始化、回调三者组合起来并用字面量数据替代Utils。以下是一个可直接运行的完整版本!DOCTYPE html html body labelInitial animation/label progress idinitialProgress max1 value0 stylewidth: 100%/progress labelOther animations/label progress idanimationProgress max1 value0 stylewidth: 100%/progress canvas idchart height200/canvas script typemodule import Chart from chart.js/auto; const initProgress document.getElementById(initialProgress); const progress document.getElementById(animationProgress); const data { labels: [Jan, Feb, Mar, Apr, May, Jun, Jul], datasets: [ { label: Dataset 1, data: [65, -20, 80, 10, 56, -55, 40], borderColor: rgb(255, 99, 132), backgroundColor: rgba(255, 99, 132, 0.5), }, { label: Dataset 2, data: [30, 50, -40, 81, -26, 55, 70], borderColor: rgb(54, 162, 235), backgroundColor: rgba(54, 162, 235, 0.5), } ] }; const chart new Chart(document.getElementById(chart), { type: line, data: data, options: { animation: { duration: 2000, onProgress(context) { const value context.currentStep / context.numSteps; if (context.initial) { initProgress.value value; } else { progress.value value; } }, onComplete(context) { if (context.initial) { console.log(Initial animation finished); } else { console.log(animation finished); } } }, interaction: {mode: nearest, axis: x, intersect: false}, plugins: { title: {display: true, text: Chart.js Line Chart - Animation Progress Bar} } } }); // 模拟后续动画随机更新数据并触发 chart.update() document.getElementById(randomize).addEventListener(click, () { chart.data.datasets.forEach(ds { ds.data ds.data.map(() Math.round(Math.random() * 200 - 100)); }); chart.update(); }); /script button idrandomizeRandomize/button /body /html运行后即可看到页面加载时initialProgress随 2000ms 初始动画从 0 增长到 1点击 Randomize 后chart.update()触发的动画则驱动animationProgress两条进度条互不干扰。若希望把日志改为进度条满格的收尾动作把onComplete中的console.log替换为任何业务回调即可。动画配置扩展duration、easing、transitions 与禁用动画基于 docs/configuration/animations.md 可以进一步定制动画行为animation.duration动画时长毫秒默认1000本示例设置为2000便于观察进度条变化。animation.easing缓动函数默认easeOutQuart可选linear、easeInQuad、easeInOutExpo、easeOutBounce等 30 余种详见动画文档的 Easing 一节。animation.delay动画开始前的延迟毫秒默认undefined。animation.loop设为true时动画无限循环进度条会反复走满。transitions按更新模式覆盖动画核心模式有active、hide、reset、resize、show。例如默认resize模式duration: 0缩放不带动画、active模式duration: 400悬停动画更短这些默认值定义在 src/core/core.animations.defaults.js 的defaults.set(transitions, ...)中。禁用动画将chart.options.animation false可关闭全部动画单独关闭某属性动画用chart.options.animations.colors false或chart.options.animations.x false按模式关闭则把对应duration设为0。注意onProgress/onComplete/fn三个键被标记为不可 scriptable见 core.animations.defaults.js即回调本身不能写成按上下文动态解析的函数。另外动画的默认属性集合numbers[x, y, borderWidth, radius, tension]与colors[color, borderColor, backgroundColor]同样定义在 core.animations.defaults.js 中多数数据集控制器会覆盖这些默认值。相关文档导航动画配置与回调完整参考docs/configuration/animations.md其中 Animation Callbacks 一节即本示例的 API 依据折线图数据集属性docs/charts/line.md数据标签结构与labels用法docs/general/data-structures.md通用选项与 Scriptable 选项机制docs/general/options.mdScriptable Options示例数据辅助函数非库 APIdocs/scripts/utils.js动画引擎实现src/core/core.animator.js、回调注册 src/core/core.controller.js、默认值 src/core/core.animations.defaults.js行为验证测试test/specs/core.animator.tests.js。【免费下载链接】Chart.jsSimple HTML5 Charts using thetag项目地址: https://gitcode.com/gh_mirrors/ch/Chart.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表