Как выровнять линию по центру, используя Canvas - PullRequest
1 голос
/ 17 марта 2019

Проблема

Я пытаюсь поместить этот рисунок линии в центр моего canvas, когда я пытаюсь использовать moveTo (100, 400) для оси X, это не меняет горизонтальное начальное положение на 100. Если я попробую то же самое с осью y, то она сместит линию вдоль оси x.

Мне также нужна помощь с рисованием y-ось с номерами 1 - 9 по вертикали вдоль оси y, кажется, выравнивается только горизонтально.РЕДАКТИРОВАТЬ !: Я вручную погладил каждую точку на оси Y, поэтому у меня есть числа там, теперь я просто хочу знать, как переместить график в центр !!

Скрипт

var c = document.getElementById("myCanvas");
        var ctx = c.getContext("2d");
        
        ctx.linecap = 'round';
        // draw a scale with the numbers on it
        ctx.lineWidth = 2;
            
        ctx.strokeStyle = '#FF9900';
        ctx.fillStyle = 'blue';
        ctx.beginPath();
        ctx.moveTo(100, 400);             
        for (i = 0; i <= 6; i+=1) {
            
             //put a stroke mark
             ctx.lineTo(100*i,400);
             ctx.lineTo(100*i,405); //markers
             ctx.lineTo(100*i,400);
             
             // write the number 10px below
             ctx.strokeStyle = '#000000';
             // default size is 10px
             ctx.strokeText(i, 100*i, 415);
             ctx.strokeStyle = '#FF9900';
        }    
        // draw a vertical scale with lines on it
        ctx.moveTo(0, -100);
        for (b = 0; b <= 9; b+=1) {
            
            //put a stroke mark
            ctx.lineTo(0,44.5*b);
            ctx.lineTo(5,44.5*b);
            ctx.lineTo(0,44.5*b);
            
            // write the number 10px below
            ctx.strokeStyle = '#000000';
            // default size is 10px                  
       }  
       ctx.strokeStyle = '#000000'
       ctx.strokeText(1, 8, 365);
       ctx.strokeText(2, 8, 320.5);
       ctx.strokeText(3, 8, 276);
       ctx.strokeText(4, 8, 231.5);
       ctx.strokeText(5, 8, 187);
       ctx.strokeText(6, 8, 142.5);
       ctx.strokeText(7, 8, 98);
       ctx.strokeText(8, 8, 53.5);
       ctx.strokeText(9, 8, 9);
       ctx.strokeStyle = '#FF9900';
        ctx.stroke();
<!DOCTYPE html>
<html>
   <head>
      <title>Canvas Axis calibration</title>
       <link rel="stylesheet" type="text/css" href="base.css"/> 
        
   </head>
   <body>
    <canvas id="myCanvas" width="1600" height="500"style="border:1px solid #c3c3c3;">
       Canvas is not playing!
    </canvas>
 

</body>
</html>

Ответы [ 3 ]

1 голос
/ 17 марта 2019

moveTo () просто установите начальную точку для вашей линии, она не рисует фактическую линию.Используйте lineTo () для рисования реальной линии.поэтому moveTo () - это от или с того места, с которого вы начинаете, а lineTo () - это то, куда вы переходите .Таким образом, отправная точка для оси x должна быть moveTo (800, 0) .

var c = document.getElementById("myCanvas"),
    ctx = c.getContext("2d"),
    lineWidth = 2,
    xNumber = 6,
    yNumber = 9,
    xCenter = c.width / 2,
    yCenter = 44.5 * yNumber + 44.5

ctx.linecap = 'round';
// draw a scale with the numbers on it
ctx.lineWidth = lineWidth;
ctx.strokeStyle = '#FF9900';
ctx.fillStyle = 'blue';

ctx.beginPath();
ctx.moveTo(xCenter, yCenter);

for (i = 0; i <= xNumber; ++i) {
    //put a stroke mark
    ctx.lineTo((xCenter + (100 * i)), yCenter);
    ctx.lineTo((xCenter + (100 * i)), (yCenter + 5)); //markers
    ctx.lineTo((xCenter + (100 * i)), yCenter);
             
    // write the number 10px below
    ctx.strokeStyle = '#000000';
    // default size is 10px
    ctx.strokeText(i, (xCenter + (100 * i)), (yCenter + 15));
}

ctx.strokeStyle = '#FF9900';
ctx.stroke()

// draw a vertical scale with lines on it
ctx.beginPath()
ctx.moveTo(xCenter, yCenter);

for (b = 0; b <= yNumber; ++b) {
    //put a stroke mark
    if(b === 0) continue;

    ctx.lineTo(xCenter, (yCenter - (44.5 * b)));
    ctx.lineTo((xCenter - 5), (yCenter - (44.5 * b)));
    ctx.lineTo(xCenter, (yCenter - (44.5 * b)));  
    ctx.strokeStyle = '#000000';
    ctx.strokeText(b, (xCenter - 15), (yCenter - (44.5 * b)));
}

ctx.strokeStyle = '#FF9900';
ctx.stroke();
<!DOCTYPE html>
<html>
   <head>
      <title>Canvas Axis calibration</title>
       <link rel="stylesheet" type="text/css" href="base.css"/> 
        
   </head>
   <body>
    <canvas id="myCanvas" width="1600" height="500"style="border:1px solid #c3c3c3;">
       Canvas is not playing!
    </canvas>
 

</body>
</html>
0 голосов
/ 17 марта 2019

Подумайте о местном происхождении

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

Нет, вы получаете план, который умещается на небольшом листе, на плане есть местоположение и ориентация.Положение стен привязано к местным координатам плана.

То же самое относится и к рисованию в 2D.Вы можете определить прямоугольник как 4 точки вокруг начала координат.[[-10,-10],[10,-10],[10,10],[-10,10]] и когда вы рисуете его, вы устанавливаете его местоположение и ориентацию, вы не меняете положение каждой точки на новое местоположение.

Рисуете локальную координату в мире с помощью setTransform

В2D API положение и ориентация задаются с помощью преобразования.

 function drawPath(x,y, points) {  // only position changes
     ctx.setTransform(1,0,0,1,x,y); // set the location
     ctx.beginPath();
     for(const [x,y] of points) {
         ctx.lineTo(x,y);
     }
     ctx.stroke();
 }

 const box = [[-10,-10],[10,-10],[10,10],[-10,10]];
 drawPath(100, 100, box);

И с масштабированием и поворотом

 function drawPath(x,y,scale, rotate, points) {
     const xdx = Math.cos(rotate) * scale;
     const xdy = Math.sin(rotate) * scale;
     ctx.setTransform(xdx, xdy, -xdy, xdx, x, y); // set the location
     ctx.beginPath();
     for(const [x,y] of points) {
         ctx.lineTo(x,y);
     }
     ctx.stroke();
 }

  drawPath(100, 100, 2, 0.5, box);

const box = [[-10,-10],[10,-10],[10,10],[-10,10]];
const W = canvas.width;
const H = canvas.height;
const ctx = canvas.getContext("2d");
ctx.font = "2opx arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = "red";
const rand = v => Math.random() * v;
drawRandom();

function drawPath(x, y,scale, rotate, points) {
    const xdx = Math.cos(rotate) * scale;
    const xdy = Math.sin(rotate) * scale;
    ctx.setTransform(xdx, xdy, -xdy, xdx, x, y); // set the location
    ctx.fillText("Hi",0,0);
    ctx.beginPath();
    for(const [x,y] of points) {
        ctx.lineTo(x, y);
    }
    ctx.closePath();
    ctx.setTransform(1, 0, 0, 1, 0, 0); // Resets so line width remains 1 px

    ctx.stroke();
 }


 function drawRandom() {
     drawPath(rand(W), rand(H), rand(2) + 0.5, rand(Math.PI * 2), box);
     setTimeout(drawRandom, 500);
 }
canvas {
   border: 1px solid black;
}
<canvas id="canvas" width ="400" height="400"></canvas>

Все, что вам нужно, это ctx.setTransform и, возможно, ctx.transform, если вы выполняете фальсификацию анимации.Я никогда не использую ctx.translate, ctx.scale, ctx.rotate, потому что они медленные, и трудно представить, где вы находитесь, о, и я сказал, что они МЕДЛЕННЫЕ !!!!

Для сбросапреобразуйте (уберите масштаб, поверните и вернитесь к 0,0) вызовите ctx.resetTransform() или ctx.setTransform(1,0,0,1,0,0)

И еще кое-что относительно вашего подхода к коду.

Детальное кодирование

Похоже, вы хотите нарисовать график.

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

Не повторяйте

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

Определите стили один раз и назовите их

Например, установка стиля 2D-контекста - это боль.Рисунок обычно имеет только несколько различных стилей, поэтому создайте объект с именованными стилями

const styles = {
    textHang: {
        textAlign : "center",
        textBaseline : "top",
        fillStyle: "blue",
        font: "16px Arial",
    },
};

И функцию, которая будет устанавливать стиль

 const setStyle = (style, c = ctx) => Object.assign(c, style);

Теперь вы можете установить стиль

 const ctx = myCanvas.getContext("2d");

 setStyle(styles, styles.textHang);
 ctx.fillText("The text", 100, 100);

Базовый 2D точечный помощник

Вы работаете в 2D, и 2D использует много точек.Вы будете добавлять умножение, копирование ... 2D точек снова и снова и снова.

Сократите набор текста и охватите самые базовые 2D-задачи с помощью всего 7 функций

const P2 = (x = 0, y = 0) => ({x,y});
const P2Set = (p, pAs) => (p.x = pAs.x, p.y = pAs.y, p);
const P2Copy = p => P2(p.x, p.y);
const P2Mult = (p, val) => (p.x *= val, p.y *= val, p);
const P2Add = (p, pAdd) => (p.x += pAdd.x, p.y += pAdd.y, p);
const P2Sub = (p, pSub) => (p.x -= pSub.x, p.y -= pSub.y, p);
const P2Dist = (p1, p2) => ((p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2) ** 0.5;

Нет строки?2D API

2D API великолепен, но отсутствует.Просто нарисовать линию безумно долго, foo bar ....

ctx.linecap = 'round';
ctx.lineWidth = 2;            
ctx.strokeStyle = '#FF9900';
ctx.beginPath();
ctx.moveTo(10, 10);             
ctx.lineTo(410, 410);
ctx.stroke();

Ни в коем случае нельзя создавать функции, использовать именованные стили, не вводить координаты, использовать точки.

Некоторые распространенные 2DЗадачи как функции

const clear = (c = ctx) => (setPos(), c.clearRect(0,0,c.canvas.width,c.canvas.height));
const line = (p1, p2, c = ctx) => (c.moveTo(p1.x, p1.y), c.lineTo(p2.x, p2.y))
const setPos = (p, c = ctx) => p ? c.setTransform(1, 0, 0, 1, p.x, p.y) : c.resetTransform();
const path = (p, path, c = ctx) => {
    c.setTransform(1,0,0,1,p.x,p.y);
    for(const seg of path) {  // each segment
        let first = true;
        for(const p of seg) {  // each point
            first ? (c.moveTo(p.x,p.y), first = false):(c.lineTo(p.x, p.y));
        }
    }
}

Пример

Следующее берет все вышеперечисленное и создает 2 оси.Это может показаться слишком сложным, но по мере того, как вы добавляете сложность к своему рисунку, вы быстро обнаруживаете, что вам нужно все меньше и меньше кода.

/* Set up the context get common values eg W,H for width and height */
const W = canvas.width;
const H = canvas.height;
const ctx = canvas.getContext("2d");


// Helper functions will use a global ctx, or pass a 2d context as last argument
// P2 is a point. I use p to mean a point
const P2 = (x = 0, y = 0) => ({x,y});
const P2Set = (p, pAs) => (p.x = pAs.x, p.y = pAs.y, p);
const P2Copy = p => P2(p.x, p.y);
const P2Mult = (p, val) => (p.x *= val, p.y *= val, p);
const P2Add = (p, pAdd) => (p.x += pAdd.x, p.y += pAdd.y, p);
const P2Sub = (p, pSub) => (p.x -= pSub.x, p.y -= pSub.y, p);
const P2Dist = (p1, p2) => ((p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2) ** 0.5;
const setStyle = (style, c = ctx) => Object.assign(c, style);
const clear = (c = ctx) => (setPos(0, c), c.clearRect(0,0,c.canvas.width,c.canvas.height));
const line = (p1, p2, c = ctx) => (c.moveTo(p1.x, p1.y), c.lineTo(p2.x, p2.y))
const setPos = (p, c = ctx) => p ? c.setTransform(1, 0, 0, 1, p.x, p.y) : c.resetTransform();
const path = (p, path, c = ctx) => {
    setPos(p,c);
    for(const seg of path) {  // each segment
        let first = true;
        for(const p of seg) {  // each point
            first ? (c.moveTo(p.x,p.y), first = false):(c.lineTo(p.x, p.y));
        }
    }
}

const styles = { // define any of the 2D context properties you wish to set
    textHang: {textAlign : "center", textBaseline : "top"},
    textLeft: {textAlign : "left", textBaseline : "middle"},
    markTextStyle: {fillStyle: "blue", font: "16px Arial"},
    markStyle: {
        strokeStyle: "black",
        lineCap: "round",
        lineWidth: 2,
    },
};
const paths = {  // Array of arrays of points. each sub array is a line segment
    markLeft: [[P2(-2, 0), P2(5, 0)]],    
    markUp: [[P2(0, 2), P2(0, -5)]],    
}

// Draw an axis from point to point, using mark to mark, lineStyle for the line
// marks is an array of names for each mark, markStyle is the style for the text marks
// markDist is the distance out (90 to the right) to put the text marks
function drawAxis(fromP, toP, mark, lineStyle, marks, markStyle, markDist) {
    const norm = P2Mult(P2Sub(P2Copy(toP), fromP), 1 / P2Dist(fromP, toP));
    const step = P2Mult(P2Sub(P2Copy(toP), fromP), 1 / (marks.length-1));
    const pos = P2Copy(fromP);
    setStyle(lineStyle);
    ctx.beginPath();
    setPos(); // without argument pos is 0,0
    line(fromP, toP);
    for(const m of marks) {
        path(pos, mark);
        P2Add(pos, step);
    }
    ctx.stroke();
    P2Set(pos, fromP);
    setStyle(markStyle);
    for(const m of marks) {
        setPos(pos);
        ctx.fillText(m,-norm.y * markDist, norm.x * markDist)
        P2Add(pos, step)
    }
}

const insetW = W * 0.1; 
const insetH = H * 0.1; 
const axisText =  ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
clear();
drawAxis(
    P2(insetW, H - insetH), P2(insetW, insetH), paths.markLeft,
    styles.markStyle,
    axisText,
    {...styles.textLeft, ...styles.markTextStyle},
    -18
);
drawAxis(
    P2(insetW, H - insetH), P2(W - insetW, H - insetH), paths.markUp,
    styles.markStyle,
    axisText,
    {...styles.textHang, ...styles.markTextStyle},
    6
);
canvas {
   border: 1px solid black;
}
<canvas id="canvas" width ="400" height="400"></canvas>
0 голосов
/ 17 марта 2019

CanvasRenderingContext2D имеет метод для этого: translate(). Он просто устанавливает смещение координат, которое будет применяться ко всему, что вы потом рисуете:

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
        
ctx.linecap = 'round';
ctx.lineWidth = 2;
ctx.fillStyle = 'blue';

ctx.translate((1600-500)/2,0);  //             <-----------

ctx.strokeStyle = '#000000';
ctx.beginPath();
ctx.moveTo(100, 400);             
for (var i = 0; i <= 6; i+=1) {
  ctx.lineTo(100*i,400);
  ctx.lineTo(100*i,405);
  ctx.lineTo(100*i,400);
  ctx.strokeText(i, 100*i, 415);
}    
ctx.moveTo(0, -100);
for (var b = 0; b <= 9; b+=1) {
  ctx.lineTo(0,44.5*b);
  ctx.lineTo(5,44.5*b);
  ctx.lineTo(0,44.5*b);
  if(b<9)
    ctx.strokeText(b+1, 8, 365-44.5*b);
}  
ctx.strokeStyle = '#FF9900';
ctx.stroke();
<!DOCTYPE html>
<html>
  <head>
    <title>Canvas Axis calibration</title>
    <link rel="stylesheet" type="text/css" href="base.css"/> 
  </head>
  <body>
     <canvas id="myCanvas" width="1600" height="500"style="border:1px solid #c3c3c3;">Canvas is not playing!</canvas>
  </body>
</html>

Здесь я предположил, что чертеж имеет ширину 500 единиц, что не совсем корректно, но вы обязательно увидите результат translate(). Эффект translate() можно сбросить с помощью вызова setTransform(1, 0, 0, 1, 0, 0) (если вы знакомы с однородными координатами и матрицами преобразования, обратите внимание, что он имеет сильно измененный порядок, см. В документации). На самом деле это матрица, которая может выполнять все виды двумерных преобразований (перемещение, вращение, масштабирование, наклон), translate() - это просто вспомогательная функция (эквивалентный вызов вероятности будет setTransform(1,0,0,1,(1600-500)/2,0), но я не пробовал).

Незначительные изменения:

  • добавил var в циклы: в противном случае переменные становятся глобальными, что обычно не является проблемой для переменной цикла, такой как i, но обычно считается плохой практикой
  • уменьшено до одного stroke() и двух strokeStyle -х. Дело в том, что линии, дуги и тому подобное рисуются с настройками, которые установлены в тот самый момент, когда вы вызываете stroke(), не имеет значения, что произошло между ними. Таким образом, цвет в большинстве случаев черный, так как strokeText() является немедленным, и цвет становится тем бежевым / любым другим цветом только для stroke()
  • переместил второй набор меток в соответствующий цикл. Я не уверен, что цикл полностью правильный, так как 9 меток и 9 линейных сегментов видны, но 10 линейных сегментов нарисованы.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...