js实现折线图
使用 Canvas API 绘制折线图
通过原生 JavaScript 和 Canvas API 可以实现基础折线图。以下是一个完整示例:
// 获取 Canvas 元素和上下文
const canvas = document.getElementById('lineChart');
const ctx = canvas.getContext('2d');
// 数据准备
const data = [30, 60, 45, 80, 65, 95];
const labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'];
// 设置图表样式
const padding = 40;
const chartWidth = canvas.width - padding * 2;
const chartHeight = canvas.height - padding * 2;
const maxValue = Math.max(...data);
// 绘制坐标轴
ctx.beginPath();
ctx.moveTo(padding, padding);
ctx.lineTo(padding, padding + chartHeight);
ctx.lineTo(padding + chartWidth, padding + chartHeight);
ctx.strokeStyle = '#ccc';
ctx.stroke();
// 绘制折线
ctx.beginPath();
data.forEach((value, index) => {
const x = padding + (index * chartWidth) / (data.length - 1);
const y = padding + chartHeight - (value / maxValue) * chartHeight;
if (index === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
// 绘制数据点
ctx.fillStyle = '#3498db';
ctx.fillRect(x - 3, y - 3, 6, 6);
});
ctx.strokeStyle = '#3498db';
ctx.lineWidth = 2;
ctx.stroke();
// 添加标签
ctx.fillStyle = '#666';
ctx.textAlign = 'center';
labels.forEach((label, index) => {
const x = padding + (index * chartWidth) / (data.length - 1);
ctx.fillText(label, x, canvas.height - padding / 2);
});
使用 Chart.js 库实现
Chart.js 是流行的开源图表库,实现折线图更简单:
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<canvas id="myChart"></canvas>
<script>
const ctx = document.getElementById('myChart');
new Chart(ctx, {
type: 'line',
data: {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
datasets: [{
label: 'Sales 2023',
data: [30, 60, 45, 80, 65, 95],
borderColor: 'rgb(75, 192, 192)',
tension: 0.1,
fill: false
}]
},
options: {
responsive: true,
scales: {
y: {
beginAtZero: true
}
}
}
});
</script>
使用 D3.js 实现高级折线图
D3.js 适合需要高度定制的场景:

import * as d3 from 'd3';
// 数据准备
const data = [
{ month: 'Jan', value: 30 },
{ month: 'Feb', value: 60 },
{ month: 'Mar', value: 45 },
{ month: 'Apr', value: 80 },
{ month: 'May', value: 65 },
{ month: 'Jun', value: 95 }
];
// 创建SVG容器
const svg = d3.select('#chart')
.append('svg')
.attr('width', 600)
.attr('height', 400);
// 设置比例尺
const xScale = d3.scaleBand()
.domain(data.map(d => d.month))
.range([50, 550])
.padding(0.2);
const yScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)])
.range([350, 50]);
// 创建折线生成器
const line = d3.line()
.x(d => xScale(d.month) + xScale.bandwidth() / 2)
.y(d => yScale(d.value));
// 绘制折线
svg.append('path')
.datum(data)
.attr('fill', 'none')
.attr('stroke', 'steelblue')
.attr('stroke-width', 2)
.attr('d', line);
// 添加坐标轴
svg.append('g')
.attr('transform', 'translate(0,350)')
.call(d3.axisBottom(xScale));
svg.append('g')
.attr('transform', 'translate(50,0)')
.call(d3.axisLeft(yScale));
性能优化建议
- 大数据量时考虑使用 WebGL 渲染方案(如 ECharts GL)
- 避免频繁重绘,使用 requestAnimationFrame 进行动画
- 对静态图表使用 canvas.toDataURL() 缓存为图片
- 使用防抖技术处理窗口 resize 事件
响应式设计处理
function resizeChart() {
const container = document.getElementById('chart-container');
const aspectRatio = 16 / 9;
if (container.offsetWidth / aspectRatio < container.offsetHeight) {
canvas.width = container.offsetWidth - 30;
canvas.height = canvas.width / aspectRatio;
} else {
canvas.height = container.offsetHeight - 30;
canvas.width = canvas.height * aspectRatio;
}
// 重新绘制图表
drawChart();
}
window.addEventListener('resize', () => {
requestAnimationFrame(resizeChart);
});
以上方案可根据项目需求选择,原生 Canvas 适合轻量需求,Chart.js 适合快速开发,D3.js 适合复杂可视化场景。






