如何用原生JavaScript实现Canvas折线图平滑曲线?及getMaxY函数验证
原生Canvas实现平滑折线(复刻YouTube热图样式)及函数验证
一、将折线转为平滑曲线(基础平滑效果)
YouTube视频热图的平滑曲线可以用二次贝塞尔曲线实现,核心思路是取相邻两个数据点的中点作为控制点,让折线自然过渡。替换原代码中lineTo的循环逻辑即可:
修改后的绘制逻辑:
c.beginPath(); c.moveTo(0, height); // 先移动到第一个数据点 c.lineTo(getXPixel(0), getYPixel(values[0])); for(var i = 1; i < values.length; i++) { // 计算当前点和前一个点的中点作为控制点 var prevX = getXPixel(i-1); var prevY = getYPixel(values[i-1]); var currX = getXPixel(i); var currY = getYPixel(values[i]); var controlX = (prevX + currX) / 2; var controlY = (prevY + currY) / 2; // 用二次贝塞尔曲线连接到中点,再直线到当前点保证端点准确 c.quadraticCurveTo(prevX, prevY, controlX, controlY); c.lineTo(currX, currY); } // 闭合路径填充 c.lineTo(width, height); c.lineTo(0, height); c.fillStyle = "rgba(44, 44, 44, 0.2)"; c.fill();
二次贝塞尔已经足够实现基础的平滑热图样式,若想要更柔和的效果,也可以尝试三次贝塞尔曲线,但复杂度会稍高。
二、getMaxY函数的正确性分析
原getMaxY函数的逻辑是:
- 遍历数组找到最大值
- 将最大值向上取整到最近的10的倍数(比如97→100,64→70)
正确性判断:
- 函数的逻辑实现是准确的,能正确找到数组最大值并完成向上取整操作。
- 但这个逻辑是否符合需求要分情况:
- 如果需要让折线图的最高点和画布顶部保留间距,避免数据点贴死在顶部,这个设计是合理的。
- 如果需要严格以数组最大值作为Y轴上限,直接去掉
max += 10 - max % 10;这一行即可。
优化建议:
如果要兼容数组中存在负数的情况(示例中均为正数),可以把初始max设为数组第一个元素而非0,避免负数场景下的计算错误:
function getMaxY() { if (values.length === 0) return 0; // 处理空数组边界情况 var max = values[0]; for(var i = 1; i < values.length; i ++) { if(values[i] > max) { max = values[i]; } } max += 10 - max % 10; return max; }
完整代码示例
JavaScript代码
var canvas = document.getElementById("myCanvas"); var c = canvas.getContext("2d"); var values = [10, 22, 64, 9, 97, 5, 11, 45, 33, 15, 55, 19]; var width = 600; var height = 50; c.strokeRect(0, 0, width, height); c.beginPath(); c.moveTo(0, height); // 移动到第一个数据点 c.lineTo(getXPixel(0), getYPixel(values[0])); for(var i = 1; i < values.length; i++) { var prevX = getXPixel(i-1); var prevY = getYPixel(values[i-1]); var currX = getXPixel(i); var currY = getYPixel(values[i]); // 计算中点作为二次贝塞尔控制点 var controlX = (prevX + currX) / 2; var controlY = (prevY + currY) / 2; c.quadraticCurveTo(prevX, prevY, controlX, controlY); c.lineTo(currX, currY); } c.lineTo(width, height); c.lineTo(0, height); c.fillStyle = "rgba(44, 44, 44, 0.2)"; c.fill(); function getMaxY() { var max = 0; for(var i = 0; i < values.length; i ++) { if(values[i] > max) { max = values[i]; } } max += 10 - max % 10; return max; } function getXPixel(val) { return (width / (values.length-1)) * val; } function getYPixel(val) { return height - ((height / getMaxY()) * val); }
HTML代码
<canvas id="myCanvas" width="600" height="50"></canvas>
内容的提问来源于stack exchange,提问作者Toniq
相关产品推荐
相关产品推荐

