如何使用javascript和Canvas 2D上下文注释一行?

时间:2015-12-01 23:40:38

标签: javascript canvas

我需要通过在开头和结尾放置文本来注释一行。我还需要插入文本而不会在文本所在的位置显示该行。

有人可以告诉我如何为2D画布上下文的行添加文本。

1 个答案:

答案 0 :(得分:2)

使用setTransform注释行

最好使用CanvasRenderingContext2D.setTransform()注释一行,以便将世界空间(您的文本)与屏幕空间(绘制线条的位置)对齐。一旦你有了这个,那么所有的绘图坐标就好像这条线跟在画布的顶部一样。

线的起点位于(0,0),线的末端位于x坐标处,等于线的长度。您可以使用CanvasRenderingContext2D.textBaseline属性在上方,上方和下方绘制。您可以使用CanvasRenderingContext2D.textAlign属性将文本对齐到末尾和中心。

要在行中留有间隙,请使用CanvasRenderingContext2D.measureText函数获取文本宽度并在文本周围绘制线段。

代码示例具有函数transformToLine(ctx, x1, y1, x2, y2),该函数将当前画布变换设置为与行对齐,然后返回行的长度,以便可以使用它来添加文本。

我还提供了一些代码,展示了如何渲染,在线之前,之后,之后和之下。还有三个实用程序函数可以帮助在行中绘制文本。

transformToLine(ctx, x1, y1, x2, y2)是一个非常方便的功能,有很多用途,不仅用于沿着一条线绘制文本。我个人觉得它非常有用,我把它添加到画布上下文而不是全局函数。

var canvas = document.getElementById("canV"); 
var ctx = canvas.getContext("2d");

// ES6 new math function 
// simple polyfill for IE on others 
var hypot;
if(typeof Math.hypot === 'function'){
    hypot = Math.hypot;
}else{
    hypot = function(x,y){  // Untested 
        return Math.sqrt(Math.pow(x,2)+Math.pow(y,2));
    };
}

// Sets the 2D context tranformation to the line segment x1,y1 to x2, y2 and
// returns the length of the line
function transformToLine(ctx, x1, y1, x2, y2){
    var ang, xd, yd;
    var ang = Math.atan2(y2 - y1, x2 - x1);     // get the line direction
    var xd = Math.cos(ang);                     // get the vector for x axis
    var yd = Math.sin(ang);
    ctx.setTransform(xd, yd, -yd, xd, x1, y1);  // create the trnasform 
    return hypot(x1 - x2, y1 - y2);        // return the lines length
}    
// restores the 2Dcontext transformation to the default
// Use the rather than save and restore if its only the transform that you want
// to save and restore. its a lot quicker.
function transformDefault(ctx){
    ctx.setTransform(1, 0, 0, 1, 0, 0);
}    

// Draws text in a line, drawing the line either side of the text if there is space.
// ctx is the context to be drawn on.
// text is the text
// inset is how many pixels from the start
// x1, y1 and x2, y2 is the line
function textAtStart (ctx, text, inset, x1, y1, x2, y2) {
    var len, textW, lw;
    len = transformToLine(ctx, x1, y1, x2, y2); // set transform and get line length
    ctx.textBaseline = "middle";                // set text over the line
    ctx.textAlign = "left";                     // align left
    textW = ctx.measureText(text).width;        // get the text size
    ctx.fillText(text, inset, 0);               // draw the text
    ctx.beginPath();                            // draw the parts of the line around the text if there is space
    lw = Number(ctx.lineWidth) * 2;             // leave a clearance so the line does not touch the text
    if (inset - lw > 0) {                       // Check for space befor the text
        ctx.moveTo(0, 0);   
        ctx.lineTo(inset - lw, 0);
        if (inset + textW + lw < len) {          // check for space after the text
            ctx.moveTo(inset + textW + lw, 0);
            ctx.lineTo(len, 0);
        }
        ctx.stroke();                           // draw the line
    }
    transformDefault(ctx);                      // restor the transform
}
// Draws text in a line, drawing the line either side of the text if there is space.
// ctx is the context to be drawn on.
// text is the text
// inset is how many pixels from the end
// x1, y1 and x2, y2 is the line
function textAtEnd (ctx, text, inset, x1, y1, x2, y2) {
    var len, textW, lw;
    len = transformToLine(ctx, x1, y1, x2, y2); // set transform and get line length
    ctx.textBaseline = "middle";                // set text over the line
    ctx.textAlign = "right";                    // align left
    textW = ctx.measureText(text).width;        // get the text size
    ctx.fillText(text, len - inset, 0);        // draw the text
    ctx.beginPath();                            // draw the parts of the line around the text if there is space
    lw = Number(ctx.lineWidth) * 2;             // leave a clearance so the line does not touch the text
    if (len - inset - textW - lw > 0) {         // Check for space befor the text
        ctx.moveTo(0, 0);   
        ctx.lineTo(len - inset - textW - lw, 0);
        if (len - inset + lw  < len) {          // check for space after the text
            ctx.moveTo(len - inset + lw, 0);
            ctx.lineTo(len, 0);
        }
        ctx.stroke();                           // draw the line
    }
    transformDefault(ctx);                      // restor the transform
}

// Draws text in a line, drawing the line either side of the text if there is space.
// ctx is the context to be drawn on.
// text is the text
// x1, y1 and x2, y2 is the line
function textAtCenter (ctx, text, x1, y1, x2, y2) {
    var len, textW, lw;
    len = transformToLine(ctx, x1, y1, x2, y2); // set transform and get line length
    ctx.textBaseline = "middle";                // set text over the line
    ctx.textAlign = "center";                   // align center
    textW = ctx.measureText(text).width;        // get the text size
    ctx.fillText(text, len / 2, 0);             // draw the text
    ctx.beginPath();                            // draw the parts of the line around the text if there is space
    lw = Number(ctx.lineWidth) * 2;             // leave a clearance so the line does not touch the text
    if (len / 2 - textW / 2 - lw > 0) {          // Check for space befor the text
        ctx.moveTo(0, 0);   
        ctx.lineTo(len / 2 - textW / 2 - lw, 0);
        if (len / 2 + textW / 2 + lw  < len) {          // check for space after the text
            ctx.moveTo(len / 2 + textW / 2 + lw, 0);
            ctx.lineTo(len, 0);
        }
        ctx.stroke();                           // draw the line
    }
    transformDefault(ctx);                      // restor the transform
}

// Thats all you need to put text on a line.
// below is an example of how it used


// variables and constants. My coding style always has constants upperCase with 
// snake case used for clarity is needed.
const PI2 = Math.PI*2;
var i;
const CW = canvas.width/2;  // center Width CW
const CH = canvas.height/2; // center Height CH
var x1,y1,x2,y2,len,angleText, lengthText;

// set up the ctx 
ctx.font = "12px verdana";
ctx.lineJoin = "round";

// clear the sreen
ctx.clearRect(0,0,canvas.width,canvas.height)

// draw 16 lines in a circle towards the center.
for(i = 0; i < PI2; i += PI2/16){
    // get the start and end location of a line
    x1 = Math.cos(i)*(CW*0.95)+CW;
    y1 = Math.sin(i)*(CH*0.95)+CH;
    x2 = Math.cos(i)*(CW*0.2)+CW;
    y2 = Math.sin(i)*(CH*0.2)+CH;
    
    angleText = "Ang: "+Math.round((360/PI2)*i); // get the aprox angle in deg
    
    
    // set the transformation to the line 
    // This is the secret to drawing alon a line.
    len = transformToLine(ctx,x1,y1,x2,y2);
    
    //If you dont want the line text to be upside down

    // world space is now allong the line with the orign
    // at the line start x1,y1 and the end of the line at
    // world coordinate len,0 with down at 90 clockwise from 
    // the line
    
    lengthText = "Len: "+Math.round(len)
    
    ctx.fillStyle = "black"; //text black
    ctx.strokeStyle = "black"; //line black
    // Draw the angle  text above the line.
    ctx.textAlign = "left"; // align to the start of the line
    ctx.textBaseline = "bottom";
    ctx.fillText(angleText,0,-2);
    
    // draw the length under the line    
    ctx.textBaseline = "top";
    ctx.fillText(lengthText,0,0);


    // Draw end just befor the end of the line
    // Get the size of the word end so we can make some space for it
    // by not drawing the line over it
    var endW = ctx.measureText("END").width;
    
    // align to right
    ctx.textAlign = "right";
    ctx.textBaseline = "middle";  // draw over the line
    // using stroke text for small fonts is a cheaters whay of making it bold.
    ctx.strokeText("END",len-10,0); // draw it 10 pixels from the ens
    
    // put some text befor the start of the line
    ctx.fillStyle = "red";

    ctx.textAlign = "right"; // align text right so its draw away from the line
    ctx.fillText("->",-2,0); // draw text 2 pixels befor start
    
    ctx.textAlign = "left"; // align text right so its draw away from the line
    ctx.fillText("<-",len+2,0); // draw it 2 pixels past end pixels from the ens

    
    // Draw a -+- in the center of the line just because we can
    var centerW = ctx.measureText("-+-").width;
    ctx.textAlign = "center"; // align text to center
    ctx.fillText("-+-",len/2,0); // draw text at the center of the line
    

    // draw the line. As the world space is along the line
    // the line will just follow the x axies so it is easy to position
    // spaces and break the line up to make space for text
    ctx.beginPath();
    ctx.moveTo(0,0);
    ctx.lineTo(len/2-centerW/2-2,0);
    // space for center text
    ctx.moveTo(len/2+centerW/2+2,0);
    ctx.lineTo(len-endW - 12,0);
    // Space for the word END
    ctx.moveTo(len -8,0); // last part of the line
    ctx.lineTo(len ,0);
    ctx.stroke();  // draw it
    
    // restore the transformation.
    transformDefault(ctx);
}
.canC { width:500px;  height:500px;}
<canvas class="canC" id="canV" width=500 height=500></canvas>