【问题标题】:Making a Graph/Curve with Javascript and HTML使用 Javascript 和 HTML 制作图形/曲线
【发布时间】:2020-06-05 00:20:46
【问题描述】:

我正在尝试制作一个可以输入数字的程序,它会根据这些数字创建一个图形(例如 Desmos,但您输入的数字不需要像 (1, 0) 和您可以控制数字的间距)。我用 Javascript 和 HTML 编写代码,用于实际的输入和画布。

我尝试使用this 网站作为基础来帮助我创建曲线。

这是组合的 Javascript 和 HTML 代码:

<!DOCTYPE html>
<html>
<body>

<form>
  <label></label>
  <input id="numbers" value="Numbers">
  <button onClick="refresh()">Submit</button>
</form>
<form>
  <label></label>
  <input id="spacing" value="Spacing">
  <button onClick="refresh()">Submit</button>
</form>
<canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;"></canvas>
<script>
  refresh()
  numbers = document.getElementById("numbers").value;
  function refresh(){
    var c = document.getElementById("myCanvas");
    var ctx = c.getContext("2d");
    ctx.beginPath();
    ctx.moveTo(20, 20);
    ctx.bezierCurveTo(numbers);
    ctx.stroke();
  }
</script>
</html>

请注意,我知道画布不起作用(变量 numbers 正在工作,有什么帮助吗?),但我主要担心的是 this 代码不会像我想要的那样创建图表。我想输入数字,并让它在“地面”(画布底部)上方绘制不可见点x数字,然后使用贝塞尔曲线使曲线穿过那些不可见点(不要点,这是我能想到的唯一解释方式)。

【问题讨论】:

  • 当代码被加载并运行时,numbers 变量被赋予一个值。这个变量只设置一次。它是在第一次调用 refresh 之后设置的。
  • @enhzflep,我该怎么做?另外,如何正确设置numbers 的值?感谢您的反馈!
  • 如果您在 refresh 函数中做的第一件事是获取新数据,具体来说,是通过设置变量然后在几行之后使用?
  • @enhzflep,我会试试的!但是,我主要关心的是绘制点以使图表正确。我希望输入标记为“数字”来控制 y 轴,另一个输入“间距”来控制 x 轴和点之间的空间。
  • 如果您使用的是 HTML5,带有 id 的元素已经存在作为全局范围内的变量,使用该名称。不需要document.getElementById他们。

标签: javascript html canvas input bezier


【解决方案1】:

在我的实习中,我被要求为我们的一些内部操作开发一个轻量级的 Web 应用程序。我想要包含的功能之一是绘制选定员工的销售数据的图形/图表。我还希望能够更改图表 x 轴的分辨率,以便我可以按天、周、月、季度或年查看销售额。

这是我只使用基本的 JavaScript 和 HTML Canvas 的想法。

关于 fix_dpi() 函数(这可能会让像我这样的新程序员感到困惑),这里有一篇文章解释了它是什么以及为什么要对其进行调整:https://medium.com/wdstack/fixing-html5-2d-canvas-blur-8ebe27db07da

另外,请注意我的drawYTicks() 函数对“50K”和“500K”标签有特殊情况。如果您需要更通用的东西,只需从 drawXTicks() 复制代码并对其域进行必要的小改动。

代码:

<script>
    let canvas = document.getElementById('canvas'), ctx = canvas.getContext('2d'), dpi = window.devicePixelRatio;
    let xOrig = canvas.width * 0.15;
    let yOrig = canvas.height * 0.85;
    let xLen = canvas.width * 0.7;
    let yLen = canvas.height * 0.7;

    function fix_dpi() {
        let style = {
            height() {
                return +getComputedStyle(canvas).getPropertyValue('height').slice(0, -2);
            },
            width() {
                return +getComputedStyle(canvas).getPropertyValue('width').slice(0, -2);
            }
        }
        canvas.setAttribute('width', style.width() * dpi);
        canvas.setAttribute('height', style.height() * dpi);
        xOrig = canvas.width * 0.15;
        yOrig = canvas.height * 0.85;
        xLen = canvas.width * 0.7;
        yLen = canvas.height * 0.7;
    }

    function draw() {
        fix_dpi();  //fixes dpi for sharper image...

        var dataSet = // int[][] containing the data.
        var xLabs = // a string[] containing x-labels.
        var yLabs = // a string[] containing y-labels.
        var range = // an integer expressing the top of your range (x-axis);
        var domain = // an integer expressing the top of your domain (y-axis)

        console.log(xLabs);
        console.log(xLabs.length);
        console.log(yLabs);
        console.log(yLabs.length);

        drawChartTitle("Employee (Total) Sales History");
        fillDataSpace(dataSet, range, domain, '#3F3');
        drawDataLine(dataSet, range, domain, '#000');
        drawDataPoints(dataSet, range, domain, '#F00');
        drawXTicks(xLabs);
        drawYTicks(yLabs);
        drawAxes();
    }

    function drawChartTitle(title) {
        ctx.save();
        ctx.fillStyle = '#000';
        ctx.font = '24px Times New Roman';
        ctx.textAlign = 'center';
        ctx.fillText(title, canvas.width / 2, canvas.height * 0.1);
        ctx.restore();
    }

    function drawAxes(color='#000') {
        ctx.save();
        ctx.beginPath();
        ctx.strokeStyle = color;
        ctx.moveTo(xOrig, (yOrig - yLen));
        ctx.lineTo(xOrig, yOrig);
        ctx.lineTo((xOrig + xLen),yOrig);
        ctx.stroke();
        ctx.restore();
    }

    function drawYTicks(yLabs) {
        //Note:  two of the ticks have to be handled separately
        //for this program, 50K and 500K.
        ctx.save();
        let tickLen = canvas.height / 50;
        let yDivs = yLen / (yLabs.length - 1);
        let tickX = xOrig - tickLen / 2;
        let labX = xOrig - tickLen;

        ctx.beginPath();
        ctx.strokeStyle = '#000';
        ctx.fillStyle = '#000';
        ctx.font = '12px Times New Roman';
        ctx.textAlign = 'right';

        if (yLabs.length >= 4) {
            yDivs = yLen / (yLabs.length - 3);
            //Draw million dollar ticks and labels
            for (let i = 3; i < yLabs.length; i++) {
                let tickY = yOrig - yDivs * (i-2);
                ctx.moveTo(tickX, tickY);
                ctx.lineTo((tickX + tickLen), tickY);
            }
            for (let i = 3; i < yLabs.length; i++) {
                let labY = yOrig - yDivs * (i-2) + 4;
                ctx.fillText(yLabs[i], labX, labY);
            }
            //Draw 50K and 500K
            let fifty = yOrig - yDivs * 0.1;
            ctx.moveTo(tickX, fifty);
            ctx.lineTo((tickX + tickLen), fifty);
            ctx.fillText(yLabs[1], labX, fifty + 4);

            let fHundredK = yOrig - yDivs * 0.5;
            ctx.moveTo(tickX, fHundredK);
            ctx.lineTo((tickX + tickLen), fHundredK);
            ctx.fillText(yLabs[2], labX, fHundredK + 4);
        }
        else if (yLabs.length == 3) {
            yDivs = yLen / (yLabs.length - 2);
            //Draw 50K and 500K only
            let fifty = yOrig - yDivs * 0.1;
            ctx.moveTo(tickX, fifty);
            ctx.lineTo((tickX + tickLen), fifty);
            ctx.fillText(yLabs[1], labX, fifty + 4);

            let fHundredK = yOrig - yDivs;
            ctx.moveTo(tickX, fHundredK);
            ctx.lineTo((tickX + tickLen), fHundredK);
            ctx.fillText(yLabs[2], labX, fHundredK + 4);
        }
        else {
            //Draw 50K only
            let fifty = yOrig - yDivs;
            ctx.moveTo(tickX, fifty);
            ctx.lineTo((tickX + tickLen), fifty);
            ctx.fillText(yLabs[1], labX, fifty + 4);
        }

        let zero = yOrig;
        ctx.moveTo(tickX, zero);
        ctx.lineTo((tickX + tickLen), zero);
        ctx.fillText(yLabs[0], labX, zero + 4);

        ctx.stroke();
        ctx.restore();
    }

    function drawXTicks(xLabs) {
        ctx.save();
        let tickLen = canvas.height / 50;
        let xDivs = xLen / (xLabs.length - 1);

        ctx.beginPath();
        ctx.strokeStyle = '#000';
        for (let i = 0; i < xLabs.length; i++) {
            let tickX = xOrig + xDivs * i;
            let tickY = yOrig + tickLen / 2;
            ctx.moveTo(tickX, tickY);
            ctx.lineTo(tickX, (tickY - tickLen));
        }
        ctx.stroke();
        ctx.restore();

        for (let i = 0; i < xLabs.length; i++) {
            ctx.save();
            ctx.fillStyle = '#000';
            ctx.font = '12px Times New Roman';
            ctx.textAlign = 'right';
            ctx.translate((canvas.width*0.15) + (xDivs * i), canvas.height*0.15);
            ctx.rotate(-Math.PI / 4);
            let labY = canvas.height * 0.52;
            let labX = -canvas.width * 0.38;
            ctx.fillText(xLabs[i], labX, labY);
            ctx.restore();
        }
    }

    function drawDataPoints(coords, maxX, maxY, color='#000') {
        ctx.save();
        if (coords.length >= 2) {
            let xScale = xLen / maxX;
            let yScale = yLen / maxY;
            let pointCir = canvas.height / 200;

            ctx.beginPath();
            for (let i = 0; i < coords.length; i++) {
                let xp = xOrig + coords[i][0] * xScale;
                let yp = yOrig - coords[i][1] * yScale;

                ctx.moveTo(xp, yp);
                ctx.arc(xp, yp, pointCir, 0, Math.PI * 2);
            }
            ctx.fillStyle = color;
            ctx.fill();
            ctx.strokeStyle = color;
            ctx.stroke();
        }
        else {
            ctx.fillStyle = '#000';
            ctx.font = '24px Sans-Serif';

            ctx.fillText('There is no data to display', (xOrig + xLen * 0.3), (yOrig - yLen * 0.5));
        }
        ctx.restore();
    }

    function drawDataLine(coords, maxX, maxY, color='#000') {
        ctx.save();
        if (coords.length >= 2) {
            let xScale = xLen / maxX;
            let yScale = yLen / maxY;
            let xp = xOrig + coords[0][0] * xScale;
            let yp = yOrig - coords[0][1] * yScale;

            ctx.beginPath();
            ctx.moveTo(xp, yp);
            for (let i = 1; i < coords.length; i++) {
                xp = xOrig + coords[i][0] * xScale;
                yp = yOrig - coords[i][1] * yScale;

                ctx.lineTo(xp, yp);
            }
            ctx.strokeStyle = color;
            ctx.stroke();
        }
        else {
            ctx.fillStyle = '#000';
            ctx.font = '24px Sans-Serif';

            ctx.fillText('There is no data to display', (xOrig + xLen * 0.3), (yOrig - yLen * 0.5));
        }
        ctx.restore();
    }

    function fillDataSpace(coords, maxX, maxY, color = '#00F') {
        ctx.save();
        if (coords.length >= 2) {
            let xScale = xLen / maxX;
            let yScale = yLen / maxY;
            let xp = xOrig + coords[0][0] * xScale;
            let yp = yOrig - coords[0][1] * yScale;

            var lingrad = ctx.createLinearGradient(xOrig, yOrig - yLen, xOrig + xLen, yOrig);
            lingrad.addColorStop(0, '#FFF');
            lingrad.addColorStop(0.5, color);
            lingrad.addColorStop(1, '#FFF');

            ctx.beginPath();
            ctx.moveTo(xp, yp);
            for (let i = 1; i < coords.length; i++) {
                xp = xOrig + coords[i][0] * xScale;
                yp = yOrig - coords[i][1] * yScale;

                ctx.lineTo(xp, yp);
            }
            ctx.lineTo(xOrig + xLen, yOrig);
            ctx.lineTo(xOrig, yOrig);
            ctx.closePath();
            ctx.strokeStyle = lingrad;
            ctx.stroke();
            ctx.fillStyle = lingrad;
            ctx.fill();
        }
        else {
            ctx.fillStyle = '#000';
            ctx.font = '24px Sans-Serif';

            ctx.fillText('There is no data to display', (xOrig + xLen * 0.3), (yOrig - yLen * 0.5));
        }
        ctx.restore();
    }
</script>

这是一些渲染数据的样子:Employee Sales Chart

最后说明:我在程序中使用静态类对象处理数据点和数据标签的集合,并将该对象传递给我的项目的 HTML (Razor) 页面。但是,如果您有办法将数据获取到您的 JS 函数,则此解决方案应该可以工作。如果不出意外,您可以先定义dataSetxLabsyLabsrangedomain 变量,然后再弄清楚如何将更大的数据集传递给函数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-06
    • 1970-01-01
    • 1970-01-01
    • 2014-12-06
    • 2018-05-17
    • 1970-01-01
    相关资源
    最近更新 更多