You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 20:57:23