javascript - 在 html5-canvas 上绘制函数图的正确方法

标签 javascript math html5-canvas

出于自学目的,我尝试在 html5-canvas 上绘制数学函数图。

我知道有些图书馆可以做到这一点,但正如我所说:教育目的。

<小时/>

所以问题是:如何绘制函数的图形,例如:f(x) = 1/x 或 f(x) = tan(x)强>

我尝试这样做的方式:

  • 从(例如 x = -100 到 x == 100)循环
  • 计算 x 处的值
  • 从 (x, valueAtX) 到 (x+1, valueAtX_plus1) 画一条线

结果: enter image description here

我想要什么: enter image description here

<小时/>

如果有人可以:给我一些伪代码或步骤如何检测函数在给定点是否连续,然后正确绘制它,我将非常感激!

P.S:在问这个问题之前,我已经搜索了很长时间的答案,但我找不到任何答案,如果这个问题是重复的,那么我真的很抱歉,请链接到原始问题。

最佳答案

处理垂直渐近线。

您需要在边界处裁剪函数。当您逐步执行离散步骤时,您将跳过 x 值,其中 y 在顶部和底部被剪裁。

您可以求解 f(x) = graphTop 和 f(x) = graphBottom 或找到每个垂直渐近线的 x,但这对于更复杂的函数来说可能会变得非常困难。

进行近似的最简单方法,除非您使用带有标尺的图表并从中获取数据,否则近似值与真实值一样好。

只需减少循环步骤即可。你有 -100 到 100,我猜你正在步进 1 个单位。而不是步长 1 单位步长的 1/10 或更小。不要绘制每个步骤,只需绘制每个单元。但在子步骤中检查 y 值是否超出 y 范围。

从一个子步骤到下一个子步骤,您有多种可能性。

  1. Y 从图内移动到图外
  2. y 从图外移动到图内
  3. y 在地 block 上从外部移动到外部
  4. y 从内移动到内

对于 1,2,我们可以在发生这种情况时开始一条新路径,对于 4,只需正常绘制即可。

最大的问题是 3,除了消除这种情况发生的可能性之外,确实没有其他解决方案。为此,您可以减少子步骤,但永远无法将它们做得足够小。考虑 f(x) = tan(x ^ 2) 来捕获所有情况 3 需要一个对数变小的 subStep,当 CPU 资源有限时,这不是一件好事。所以我们只能做出最好的选择。

如果渐近线左侧和右侧的 y 剪辑在 x 轴上相距大约 2/10(2/subStepCount 请参阅代码)像素(缩放像素),则以下内容将起作用但如果他们靠近的话就会失败。要增加子步骤,请设置 var subStepCount = ?。我添加了一项检查,看看最后两个图之间的差异是否大于图高度的 1/3,以捕获一些不良图。

    
    var ctx = canvas.getContext("2d");
    var h = canvas.height;    
    var w = canvas.width;
    var cw = w / 2; // centers
    var ch = h / 2; 
    var subStepCount = 10;  // number of sub setps
    var scale = 10;         // scale of the plot

  
    function plot(func,col,lineWidth){
        var invScale = 1 / scale;    // inverted scale is the size of a pixel
        var top = ch * invScale;     // get top and bottom
        var bottom = -ch * invScale;
        var subStep = invScale / subStepCount; // get the sub steps between pixels
        var x,y,yy,xx,subX;                    // xx,yy are the coords of prev point
        var start = (-cw - 1) * invScale;      // get the start and end
        var end = (cw + 1) * invScale;
        // set render styles
        ctx.strokeStyle = col;
        ctx.lineWidth = lineWidth * invScale; // scale line to be constant size

        ctx.beginPath();
        for(x = start; x < end; x += invScale){ // pixel steps
            for(subX = 0; subX < invScale; subX += subStep){  // sub steps
                y = func(x+subX);                    // get y for x 
                if(yy !== undefined){                // is this not the first point
                    if(y > top || y < bottom){       // this y outside ?
                        if(yy < top && yy > bottom){ // last yy inside?
                            ctx.lineTo(x + subX,y);
                        }
                    } else {                         // this y must be inside
                        if(yy > top || yy < bottom){ // was last yy outside
                            ctx.moveTo(xx,yy);       // start a new path
                        }
                        if(subX === 0){              // are we at a pixel 
                            if(y > bottom && y < top){  // are we inside
                                // if the step is large then might be a line break
                                if(Math.abs(yy-y) > (top - bottom) * (1/3)){ 
                                    ctx.moveTo(x,y);  
                                }else{
                                    ctx.lineTo(x,y);
                                }
                            }
                        }
                    }
                }else{
                    if(subX === 0){
                        ctx.moveTo(x,y);
                    }
                }
                yy  = y;
                xx = x+ subX;
            }
        }
        ctx.stroke();

        
    }
        
    // set the plot scale and orientation 
    ctx.setTransform(scale,0,0,-scale,cw, ch);
    // two example function plots
    plot((x)=>Math.tan(Math.cos(x/2) * 10),"#F88",1)
    plot((x)=>Math.tan(x),"blue",2)
canvas {
   border : 1px solid black;
 }
<canvas id=canvas width = 512 height = 256></canvas>

关于javascript - 在 html5-canvas 上绘制函数图的正确方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43388716/

相关文章:

javascript - 如何在旋转javascript后获取裁剪坐标

javascript - 根据线条( Canvas )的位置添加文本标签

php - 如何从 JavaScript 文件中获取值并将其发送到 PHP 文件?

使用内置 javascript 模式对 var block 进行 JavaScript 缩进

javascript - 无法让引导动画在滑动窗口中的 div 上工作

java - 四元数比较?

r - 日期时间 - 确定多个(n)日期时间范围是否在 R 中相互重叠

javascript - 访问被拒绝的 ajax 请求 javascript

javascript - 如何每隔 n 秒更改 Canvas 上圆圈的颜色?

javascript - Fabricjs - 如何检测鼠标移动时的 Canvas ?