能否将Chart.js Annotation用作基于规则和数据的动态目标线?
如何用Chart.js的Annotation插件实现动态目标线
需求:让Chart.js的annotation插件作为动态目标线,根据过去3周的跑步里程数据自动调整数值:
- 连续3周跑步里程不低于10km时,目标线提升至16km
- 连续3周未达到10km时,目标线降至8km
实现方法
1. 计算动态目标值
先获取最近3周的里程数据,通过规则判断计算出当前的目标线数值:
// 获取最近3周的里程数据,转换为数字类型 const recent3Weeks = kilometer.slice(-3).map(num => parseFloat(num)); let targetValue = 10; // 默认初始目标值 // 判断连续3周是否都≥10km const allAbove10 = recent3Weeks.every(km => km >= 10); // 判断连续3周是否都<10km const allBelow10 = recent3Weeks.every(km => km < 10); if (allAbove10) { targetValue = 16; } else if (allBelow10) { targetValue = 8; }
2. 配置动态Annotation目标线
将计算出的targetValue赋值给annotation的yMin和yMax,替换原有的固定值:
annotation: { annotations: { line1: { type: 'line', yMin: targetValue, yMax: targetValue, borderColor: 'rgb(255, 99, 132)', borderWidth: 2, } } }
3. 修正插件依赖路径
原代码中annotation插件的本地路径替换为CDN链接,确保插件正常加载:
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-annotation@2.2.0/dist/chartjs-plugin-annotation.min.js"></script>
修改后的完整代码
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Chart.js, the Missing Manual</title> <style> * { margin: 0; padding: 0; font-family: sans-serif; } .chartMenu { width: 100vw; height: 40px; background: #1A1A1A; color: rgba(75, 192, 192, 1); } .chartMenu p { padding: 10px; font-size: 20px; } .chartCard { width: 100vw; height: calc(100vh - 40px); background: rgba(75, 192, 192, 0.2); display: flex; align-items: center; justify-content: center; } .chartBox { width: 800px; padding: 10px; border-radius: 20px; border: solid 3px rgba(75, 192, 192, 1); background: white; } </style> </head> <body> <div class="chartMenu"> <p>Hi there</p> </div> <div class="chartCard"> <div class="chartBox"> <canvas id="myChart"></canvas> </div> </div> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-datalabels/2.1.0/chartjs-plugin-datalabels.min.js" integrity="sha512-Tfw6etYMUhL4RTki37niav99C6OHwMDB2iBT5S5piyHO+ltK2YX8Hjy9TXxhE1Gm/TmAV0uaykSpnHKFIAif/A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-annotation@2.2.0/dist/chartjs-plugin-annotation.min.js"></script> <script> // data block var week = [], kilometer = [], pushups = [], situps = [] async function dummyChart () { await getDummyData() // 计算动态目标线数值 const recent3Weeks = kilometer.slice(-3).map(num => parseFloat(num)); let targetValue = 10; // 默认初始目标值 // 判断连续3周是否都≥10km const allAbove10 = recent3Weeks.every(km => km >= 10); // 判断连续3周是否都<10km const allBelow10 = recent3Weeks.every(km => km < 10); if (allAbove10) { targetValue = 16; } else if (allBelow10) { targetValue = 8; } const data = { labels: week.slice(-8), datasets: [{ label: 'Kilometer', data: kilometer.slice(-8), datalabels: { anchor: 'end', align: 'end', offset: 5, font: { weight: 'bold', }, clamp: true, }, backgroundColor: [ 'rgba(255, 99, 132, 0.2)', 'rgba(54, 162, 235, 0.2)', 'rgba(255, 206, 86, 0.2)', 'rgba(75, 192, 192, 0.2)', 'rgba(153, 102, 255, 0.2)', 'rgba(255, 159, 64, 0.2)' ], borderColor: [ 'rgba(255, 99, 132, 1)', 'rgba(54, 162, 235, 1)', 'rgba(255, 206, 86, 1)', 'rgba(75, 192, 192, 1)', 'rgba(153, 102, 255, 1)', 'rgba(255, 159, 64, 1)' ], borderWidth: 1 }] }; // config block const config = { type: 'line', data: data, options: { scales: { y: { beginAtZero: true, }, }, layout: { padding: 20, }, plugins: { legend: { display: false, }, autocolors: false, annotation: { annotations: { line1: { type: 'line', yMin: targetValue, yMax: targetValue, borderColor: 'rgb(255, 99, 132)', borderWidth: 2, } } } } }, plugins: [ChartDataLabels] }; // init render block const myChart = new Chart( document.getElementById('myChart'), config ); } dummyChart() // Fetch data from API async function getDummyData() { const apiUrl = "https://sheetdb.io/api/v1/an675a1pxghf6?sheet=Strava:weekly" const response = await fetch(apiUrl) const lineChartData = await response.json() const weekdata = lineChartData.map( (x) => x.week) const kilometerdata = lineChartData.map( (x) => x.kilometer) const pushupsdata = lineChartData.map( (x) => x.pushups) const situpsdata = lineChartData.map( (x) => x.situps) console.log(kilometerdata.slice(-4), pushupsdata, situpsdata) kilometer = kilometerdata pushups = pushupsdata situps = situpsdata week = weekdata } </script> </body> </html>
内容的提问来源于stack exchange,提问作者Alexander Adolfsson
相关产品推荐
相关产品推荐

