如何用Chart.js基于历史价格数据生成近12个月价格曲线图表?
如何用Chart.js生成近12个月的价格走势折线图
我来一步步帮你搞定这个需求,核心是把你的历史价格数据和生成的12个月列表做匹配,再配置Chart.js的坐标轴和数据集。下面是完整的实现方案:
第一步:准备工作
首先确保你已经引入了两个必要的库:
moment.js(用来处理日期格式转换)Chart.js(用来生成图表)
你可以用CDN引入,或者本地导入:
<script src="https://cdn.jsdelivr.net/npm/moment@2.29.4/moment.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.8/dist/chart.umd.min.js"></script>
同时在HTML中添加一个canvas容器,用来渲染图表:
<canvas id="priceChart" width="400" height="200"></canvas>
第二步:处理数据匹配
你已经有了生成过去12个月的函数,但需要把原始价格数据和这些月份对应起来。这里写一个处理函数,把零散的历史数据映射到每个月份上:
function getPreviousMonths() { var months = []; for (i = 0; i < 12; i++) { var month = moment().subtract(i, 'months').format('MMMM Y'); months.push(month); } return months.reverse(); } // 处理原始价格数据,匹配到12个月的列表中 function processPriceData(priceHistory, targetMonths) { // 先创建一个映射表,初始化所有月份的价格为null(无数据时Chart.js会跳过该点) const monthPriceMap = {}; targetMonths.forEach(month => { monthPriceMap[month] = null; }); // 遍历你的历史价格数据,转换日期格式并填充价格 priceHistory.forEach(item => { // 解析原始的DD/MM/YYYY格式日期,转换成和目标月份一致的MMMM Y格式 const itemMonth = moment(item.date, 'DD/MM/YYYY').format('MMMM Y'); // 如果这个月份在我们的12个月列表里,就填充价格(注意转成数字) if (monthPriceMap.hasOwnProperty(itemMonth)) { // 如果你有同一个月份的多个价格,可以改成取平均值/最大值,这里我用最后一条数据覆盖 monthPriceMap[itemMonth] = parseFloat(item.price); } }); // 按照目标月份的顺序,提取价格数组 return targetMonths.map(month => monthPriceMap[month]); }
第三步:初始化Chart.js图表
现在可以把处理好的数据传入Chart.js,配置坐标轴和样式:
// 获取12个月的标签列表 const months = getPreviousMonths(); // 你的原始价格历史数据 const priceHistory = [ { "price":"49", "date":"21\/01\/2018" }, { "price":"30", "date":"01\/01\/2018" }, { "price":"32", "date":"15\/11\/2017" } ]; // 处理得到对应每个月份的价格数组 const priceDataset = processPriceData(priceHistory, months); // 初始化图表 const ctx = document.getElementById('priceChart').getContext('2d'); new Chart(ctx, { type: 'line', data: { labels: months, // X轴直接用我们生成的月份标签 datasets: [{ label: '月度价格', data: priceDataset, borderColor: '#2196F3', // 折线颜色 backgroundColor: 'rgba(33, 150, 243, 0.1)', // 填充颜色(可选) tension: 0.2, // 折线平滑度,0是纯折线 fill: true, // 是否填充折线下方区域 spanGaps: false // 无数据时是否跳过断点,设为true会连接前后点 }] }, options: { scales: { x: { title: { display: true, text: '月份' }, ticks: { maxRotation: 45, // 旋转X轴标签避免重叠 minRotation: 30 } }, y: { title: { display: true, text: '价格' }, beginAtZero: true, // Y轴从0开始,如果你不想从0开始可以改成false ticks: { // 给价格加上货币符号,比如$ callback: function(value) { return `$${value}`; } } } }, plugins: { tooltip: { // 自定义提示框内容 callbacks: { label: function(context) { return `价格: $${context.parsed.y || 'N/A'}`; } } } } } });
关键细节说明
- 重复月份数据处理:如果你的历史数据中有同一个月份的多条价格(比如例子里2018年1月有两个价格),可以修改
processPriceData函数来处理,比如取平均值:// 修改初始化部分为数组 targetMonths.forEach(month => { monthPriceMap[month] = []; }); // 填充时push价格 priceHistory.forEach(item => { const itemMonth = moment(item.date, 'DD/MM/YYYY').format('MMMM Y'); if (monthPriceMap.hasOwnProperty(itemMonth)) { monthPriceMap[itemMonth].push(parseFloat(item.price)); } }); // 最后计算平均值 return targetMonths.map(month => { const prices = monthPriceMap[month]; if (prices.length === 0) return null; return prices.reduce((sum, price) => sum + price, 0) / prices.length; }); - 坐标轴配置:因为我们已经提前处理了月份标签,不需要用Chart.js的时间坐标轴,直接用文本标签更简单。如果需要用时间轴,可以参考Chart.js的时间刻度配置,但这里没必要。
- 无数据处理:把无数据的月份设为
null,Chart.js会自动跳过这些点,如果你希望折线连接前后点,把spanGaps设为true即可。
内容的提问来源于stack exchange,提问作者Zoker
相关产品推荐
相关产品推荐

