如何在JavaScript中实现类似Python的Stem Plot(茎图)?
在JavaScript中实现Python风格的茎图
什么是茎图
茎图会在图表覆盖的每个x位置绘制一条从基线到y值的垂直线,并在垂直线顶端放置一个标记点。
你可以通过以下两种方式在JavaScript中实现这类图表:
方法一:原生Canvas实现
无需依赖任何库,直接用HTML5 Canvas API绘制,灵活性拉满:
<canvas id="stemPlot" width="600" height="400"></canvas> <script> // 准备示例数据 const data = [ { x: 0, y: 25 }, { x: 1, y: 40 }, { x: 2, y: 15 }, { x: 3, y: 35 }, { x: 4, y: 20 }, { x: 5, y: 45 } ]; const baselineY = 350; // 基线的y坐标(Canvas坐标系y轴向下为正) const padding = 50; const canvas = document.getElementById('stemPlot'); const ctx = canvas.getContext('2d'); // 计算缩放比例,适配Canvas尺寸 const maxX = Math.max(...data.map(d => d.x)); const maxY = Math.max(...data.map(d => d.y)); const xScale = (canvas.width - 2 * padding) / maxX; const yScale = (baselineY - padding) / maxY; // 绘制底部基线 ctx.beginPath(); ctx.moveTo(padding, baselineY); ctx.lineTo(canvas.width - padding, baselineY); ctx.strokeStyle = '#333'; ctx.stroke(); // 遍历数据绘制茎线和标记点 data.forEach(point => { const xPos = padding + point.x * xScale; const yPos = baselineY - point.y * yScale; // 绘制茎(垂直线) ctx.beginPath(); ctx.moveTo(xPos, baselineY); ctx.lineTo(xPos, yPos); ctx.strokeStyle = '#666'; ctx.lineWidth = 1; ctx.stroke(); // 绘制顶端标记点 ctx.beginPath(); ctx.arc(xPos, yPos, 4, 0, Math.PI * 2); ctx.fillStyle = '#ff4444'; ctx.fill(); ctx.strokeStyle = '#000'; ctx.stroke(); }); </script>
你可以根据需求调整基线位置、标记样式、颜色、尺寸等参数。
方法二:基于Chart.js扩展实现
如果习惯用可视化库,可以通过Chart.js的自定义插件改造现有图表类型:
<canvas id="stemPlotChartjs" width="600" height="400"></canvas> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script> const ctx = document.getElementById('stemPlotChartjs').getContext('2d'); new Chart(ctx, { type: 'line', data: { labels: [0, 1, 2, 3, 4, 5], datasets: [{ label: '茎图数据', data: [25, 40, 15, 35, 20, 45], pointBackgroundColor: '#ff4444', pointRadius: 4, showLine: false, // 隐藏数据点之间的连线 borderWidth: 0 }] }, options: { scales: { y: { beginAtZero: true, grid: { display: false } }, x: { grid: { display: false } } }, plugins: { // 自定义插件绘制茎线 afterDatasetsDraw: (chart) => { const ctx = chart.ctx; const yAxis = chart.scales.y; const xAxis = chart.scales.x; const baselineY = yAxis.getPixelForValue(0); // 以y=0为基线 chart.data.datasets.forEach((dataset) => { dataset.data.forEach((value, index) => { const xPos = xAxis.getPixelForTick(index); const yPos = yAxis.getPixelForValue(value); // 绘制茎线 ctx.beginPath(); ctx.moveTo(xPos, baselineY); ctx.lineTo(xPos, yPos); ctx.strokeStyle = '#666'; ctx.lineWidth = 1; ctx.stroke(); }); }); } } } }); </script>
该方案利用Chart.js的插件机制,在绘制完数据点后额外添加从基线到每个点的垂直线,快速实现茎图效果。
内容的提问来源于stack exchange,提问作者hermi
相关产品推荐
相关产品推荐

