我需要通过在开头和结尾放置文本来注释一行。我还需要插入文本而不会在文本所在的位置显示该行。
有人可以告诉我如何为2D画布上下文的行添加文本。
答案 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>