如何使用Canvas对齐我的线条图?

问题描述 投票:1回答:3

Problem

我试图将此线条绘制在我的canvas的中心,当我尝试使用moveTo(100,400)作为x轴时,它不会将水平起始位置更改为100.如果我尝试相同的事情使用y轴,它将沿x轴移动线。

我还需要帮助,沿着y轴垂直绘制y轴数字1-9,它似乎只是水平对齐。编辑!:我已经手动抚摸了y轴上的每个点,所以我在那里有数字,现在我只想知道如何将图形移动到中心!

Script

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>
javascript html5 canvas html5-canvas
3个回答
1
投票

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

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)的效果(如果您熟悉齐次坐标和变换矩阵,请注意它有一个严格修改的顺序,请参阅文档)。它实际上是一个可以进行各种2D变换(平移,旋转,缩放,倾斜)的矩阵,translate()只是一个便利函数(相当于调用probaby将是setTransform(1,0,0,1,(1600-500)/2,0),但我还没试过)。

细微变化:

  • var-s添加到循环中:否则变量变为全局变量,这对于像i这样的循环变量通常不是问题,但通常被认为是不好的做法
  • 减少到单个stroke()和两个strokeStyle-s。事情就是用你在调用stroke()的那一刻设置的设置绘制线条,弧线等,它们之间发生了什么并不重要。所以颜色在大多数时候是黑色的,因为strokeText()是即时的,颜色变成米色/任何一个只是为了stroke()
  • 将第二组标签移动到相应的循环中。我不确定循环是否完全正确,因为可以看到9个标签和9个线段,但是绘制了10个线段。

0
投票

想想当地的起源

当你得到一个建筑计划时,你没有得到一张巨大的纸张,因为你正在建造在burbs中,你想要在夏日阳光下移动一些窗户,你不要重绘计划每个墙的新坐标。

没有你得到适合小张的计划,计划是一个位置和方向。墙的位置固定在计划的当地坐标上。

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.translatectx.scalectx.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点。

只需7个功能即可减少打字并满足最基本的2D需求

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

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>
© www.soinside.com 2019 - 2024. All rights reserved.