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

如何用原生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函数的逻辑是:

  1. 遍历数组找到最大值
  2. 将最大值向上取整到最近的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 05:17:09