如何使用javascript和Canvas 2D上下文注释线条
How to annotate a line using javascript and Canvas 2D context?
我需要通过在开始和结束处放置文本来注释一行。我还需要插入文本,而文本所在的行不可见。
有人能告诉我如何在2D画布上下文的行中添加文本吗。
使用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>
相关文章:
- 将函数的上下文应用于javascript变量
- Twitter Bootstrap typeahead:使用“this”获取上下文/调用元素
- 使用JQuery的动态上下文菜单
- 如何访问UIWebView'的子窗口上下文
- Javascript中的备选注释方法
- html5视频中的Youtube类型注释
- 如何允许在TinyMCE中使用valid_elements进行注释标记
- JQuery在单击正文时隐藏上下文菜单
- JQuery上下文菜单显示/隐藏问题
- 如何从HTTP上下文对象中获取Post数据
- HTML字符串作为上下文
- 为什么我的上下文选择器和.buttonset()在ie中花费了这么长时间
- 丢失对象“;这个“;方法中的上下文
- 防止在移动Safari(iPad/iPhone)中长按/长按默认上下文菜单
- d3防止在上下文菜单上触发mouseout
- 是否可以在不更改上下文的情况下调用函数.apply
- NodeJ中的注释会影响性能吗
- 调用$.each()函数时上下文发生变化
- 何时可以;我的用户脚本在Javascript中触发右键单击(上下文菜单)
- 如何使用javascript和Canvas 2D上下文注释线条