首页 > 解决方案 > 如何使用 for 循环在 html 5 画布中绘制这个指定的图像?

问题描述

我想使用 for 循环在 HTML 5 画布中创建一些模式。我无法创建图片中指定的适当模式。我也在为 for 循环中的端点 x,y 坐标而苦苦挣扎。

在此处输入图像描述

<html>
    <body>
        <style>
            *{
                margin: 0px;
            }
            body{
                background-color: aqua;
            }
            canvas
            {
                background-color: white;
            }
        </style>
        <canvas id="mycanvas" width="5000" height="5000"></canvas>
        <script>
var canvas = document.getElementById('mycanvas');
var context = canvas.getContext('2d');
           
                for(i=0; i<1000;i=i+10){
                    
                    context.moveTo(i, i*50);
                   context.bezierCurveTo(i*10,i*10,0,100,i,0);
                    context.stroke();   
                }
                
            
    
        </script>
    </body>
</html>

标签: javascripthtmlfor-loopcanvashtml5-canvas

解决方案


这个问题很模糊,因为问题的约束没有定义。该答案分步说明了解决方案。定义函数和数据结构来解决我对问题的解释的子部分。

如果您想要一个即时的剪切和粘贴解决方案,如果不严格定义约束,您将无法获得。

这个答案要求你对 JavaScript 和编程有基本的了解,如果你没有这方面的经验,将无法帮助你解决问题。

重复随机路径。

此解决方案将在画布上重复任意路径。

路径

将路径定义为一组命名函数及其参数。

由于此时我们不知道画布的大小,坐标数据应该很容易缩放以适应任何大小的画布。为此,参数必须是坐标对,并且应该适合大小为 1 x 1 单位的画布(这忽略了画布的方面)。

const path = [
    ["moveTo", [1.1, -0.1]],
    ["bezierCurveTo", [0.75, 1.1, 0.5, 0.25,  0.5, 0.5]],
    ["bezierCurveTo", [0.75, 0.65, 0.25, 0.75,  -0.1, 1.1]],
];

请注意,您也可以使用Path2D对象来定义路径,但这会引入一些关于路径范围的问题。如果我们要覆盖画布,就需要一些东西。

渲染路径

现在是一个渲染路径的函数。

它需要能够

  • 知道要利用什么 2D 上下文。
  • 将路径移动到相对于画布左上角的位置。
  • 缩放路径以适应画布而不改变纵横比
  • 以像素为单位设置描边的宽度
  • 设置描边样式。

.

function drawFittedPath(ctx, path, x, y, lineWidth, style) {
    const scale = Math.max(ctx.canvas.width, ctx.canvas.height);
    ctx.setTransform(scale, 0, 0, scale, x, y);
    ctx.beginPath();
    for(const subPath of path) { ctx[subPathp[0](...subPath[1]) }
    ctx.strokeStyle = style;
    ctx.lineWidth = lineWidth;
    ctx.setTransform(1, 0, 0, 1, 0, 0);
    ctx.stroke();
}

在哪里渲染

由于需要渲染路径以覆盖整个画布,我们需要知道相对于画布左上角的路径可见的位置。为此,我们可以创建一个查询函数,该函数采用路径和 2D 上下文并返回有关查询的信息。

为了避免重复代码,其中有第二个函数从定义的路径数据中提取 x 或 y 坐标。

function pathQuery(ctx, path, query) {
    const pathCoords2Array = (offset, stride = 2) => {
        const arr = [];
        for(const subPath of path) {
            let i = offset;
            while (i < subPath[1].length) { 
                arr.push(subPath[1][i]);
                i += stride;
            }
        }
        return arr;
    }
    query = query.toLowerCase();
    const xCoords = pathCoords2Array(0, 2);
    const yCoords = pathCoords2Array(1, 2);
    const scale = Math.max(ctx.canvas.width, ctx.canvas.height);
    const result = {x:0,y:0};
    if (query.includes("left")) { result.x = -Math.max(...xCoords) * scale; }
    else { result.x = ctx.canvas.width - Math.min(...xCoords) * scale; }
    if (query.includes("top")) { result.y = -Math.max(...yCoords) * scale; }
    else { result.y = ctx.canvas.height - Math.min(...yCoords) * scale; }
    return result;
}

填充画布

现在有了所有功能,我们可以添加渲染路径以填充画布的最后部分。

在这种情况下,我们需要定义路径之间的间距。为简单起见,我们可以将其定义为我们想要查看的路径数,并计算执行此操作所需的 x 和 y 步骤。

function fillCanvasWithPath(ctx, path, count, lineWidth, style) {
    const topLeft = pathQuery(ctx, path, "topLeft");
    const botRight = pathQuery(ctx, path, "bottomRight");
    const xStep = ctx.canvas.width / count;
    const yStep = ctx.canvas.height / count;
    var x = topLeft.x - lineWidth / 2;
    var y = topLeft.y - lineWidth / 2;
    while (x < botRight.x + lineWidth && y < botRight.y + lineWidth) {
        drawFittedPath(ctx, path, x, y, lineWidth, style);
        x += xStep;
        y += yStep;
    }
}

例子

const path = [
    ["moveTo", [1.1, -0.1]],
    ["bezierCurveTo", [0.75, 0.1, 0.5, 0.15,  0.5, 0.3]],
    ["bezierCurveTo", [0.5, 0.65, 0.05, 0.2,  0.2, 0.6]],
    ["bezierCurveTo", [0.35, 0.9, 0.0, 0.75,  -0.1, 1.1]],
];

const ctx = canvas.getContext("2d");
ctx.lineJoin = "round";
fillCanvasWithPath(ctx, path,50, 6, "#000");


function drawFittedPath(ctx, path, x, y, lineWidth, style) {
    const scale = Math.max(ctx.canvas.width, ctx.canvas.height);
    ctx.setTransform(scale, 0, 0, scale, x, y);
    ctx.beginPath();
    for(const subPath of path) { ctx[subPath[0]](...subPath[1]) }
    ctx.strokeStyle = style;
    ctx.lineWidth = lineWidth;
    ctx.setTransform(1, 0, 0, 1, 0, 0);
    ctx.stroke();
}

function pathQuery(ctx, path, query) {
    const pathCoords2Array = (offset, stride = 2) => {
        const arr = [];
        for(const subPath of path) {
            let i = offset;
            while (i < subPath[1].length) { 
                arr.push(subPath[1][i]);
                i += stride;
            }
        }
        return arr;
    }
    query = query.toLowerCase();
    const xCoords = pathCoords2Array(0, 2);
    const yCoords = pathCoords2Array(1, 2);
    const scale = Math.max(ctx.canvas.width, ctx.canvas.height);
    const result = {x:0,y:0};
    if (query.includes("left")) { result.x = -Math.max(...xCoords) * scale; }
    else { result.x = ctx.canvas.width - Math.min(...xCoords) * scale; }
    if (query.includes("top")) { result.y = -Math.max(...yCoords) * scale; }
    else { result.y = ctx.canvas.height - Math.min(...yCoords) * scale; }
    return result;
}

function fillCanvasWithPath(ctx, path, count, lineWidth, style) {
    const topLeft = pathQuery(ctx, path, "topLeft");
    const botRight = pathQuery(ctx, path, "bottomRight");
    const xStep = ctx.canvas.width / count;
    const yStep = ctx.canvas.height / count;
    var x = topLeft.x - lineWidth / 2;
    var y = topLeft.y - lineWidth / 2;
    while (x < botRight.x + lineWidth && y < botRight.y + lineWidth) {
        drawFittedPath(ctx, path, x, y, lineWidth, style);
        x += xStep;
        y += yStep;
    }
}
    
canvas {
  border: 2px solid #666;
}
<canvas id="canvas" width="1024" height="1024"></canvas>

问题

路径类型

该示例要求查询路径的范围。它假设路径是从坐标对创建的。然而,一些路径可以用比坐标更多的信息来创建,例如ctx.arc ,这些路径的范围并不是那么简单,需要代码来根据可用数据计算实际范围。这超出了基本问题和这个答案的范围。

渲染状态

该解决方案对 2D 上下文状态进行了假设。一些状态会影响每条路径的范围。假设是 2D 上下文处于默认状态。

间距

在您提供的示例图像中,路径在路径上的所有点处等距分布。由于示例解决方案使用任意路径,因此这种限制并不总是可能的,事实上,对于随机路径,这种条件不太可能满足。

解决方案是您必须非常小心您定义的路径。或者您可以忽略约束。

路径

  • 需要考虑图像的方面
  • 无法关闭
  • 必须足够长以覆盖画布

如果您希望每条路径沿其所有点的距离相等,那么您需要使用不同的方法为画布上的每个位置创建自定义路径。这再次超出了简单答案的范围。


推荐阅读