Как нарисовать это указанное изображение в html 5 холст, используя для циклов? - PullRequest
0 голосов
/ 24 февраля 2020

Я хочу создать несколько шаблонов на холсте HTML 5, используя циклы for. Я не могу создать соответствующие шаблоны, как указано на картинке. также я борюсь с конечными точками x, y координаты для циклов.

enter image description here

<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>

1 Ответ

0 голосов
/ 25 февраля 2020

Вопрос неопределенный, поскольку ограничения проблемы не определены. Этот ответ иллюстрирует решение по шагам. Определение функций и структур данных, которые решают части моей интерпретации проблемы.

Если вы хотите мгновенно вырезать и вставить решение, вы не получите это без строгого определения ограничений.

Этот ответ требует от вас базового c понимания JavaScript и программирования и не поможет вам решить проблему, если у вас нет этого опыта.

Повторите случайные пути.

Это решение будет повторять произвольный путь через холст.

Путь

Определить путь как набор именованной функции и их аргументов.

Как и на данный момент мы надеваем Не знаю размера холста, данные координат должны легко масштабироваться, чтобы соответствовать холсту любого размера. Для этого аргументы должны быть парами координат и соответствовать холсту размером 1 на 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 Экстент таких путей не так прост и требует кода для вычисления реального экстента на основе доступных данных. Это выходит за рамки базового c вопроса и этого ответа.

Состояние визуализации

Это решение делает предположения относительно состояния 2D-контекста. Некоторые состояния будут влиять на степень каждого пути. Предполагается, что 2D-контекст находится в состоянии по умолчанию.

Интервал

В примере изображения, который вы указали, пути одинаково разнесены во всех точках пути. Поскольку в примере решения используется произвольный путь, это ограничение не всегда возможно, фактически для случайного пути очень маловероятно, что это условие может быть выполнено.

Решение состоит в том, что вы должны быть очень осторожны с путем Вы определяете. Или вы можете игнорировать ограничение.

Путь

  • должен учитывать аспект изображения
  • не может быть закрыт
  • должен быть достаточно длинным, чтобы покрыть холст

Если вы хотите, чтобы каждый путь имел все точки вдоль него на одинаковом расстоянии друг от друга, то вам нужно использовать другой метод, который создает собственный путь для каждой позиции на холсте. Это снова выходит за рамки простого ответа.

...