Javascript 在画布文本和html元素之间共享基线
我将一些文本绘制到一个HTML画布元素,在该文本旁边有一个简单的CSS样式和绝对定位的HTML按钮,位置由JavaScript计算。我希望两者使用相同的字体,这似乎很容易,我也希望两者共享相同的基线。据我所知,至少考虑到我所面临的一系列约束条件,这一点目前确实很难做到Javascript 在画布文本和html元素之间共享基线,javascript,html,css,canvas,vertical-alignment,Javascript,Html,Css,Canvas,Vertical Alignment,我将一些文本绘制到一个HTML画布元素,在该文本旁边有一个简单的CSS样式和绝对定位的HTML按钮,位置由JavaScript计算。我希望两者使用相同的字体,这似乎很容易,我也希望两者共享相同的基线。据我所知,至少考虑到我所面临的一系列约束条件,这一点目前确实很难做到 开箱即用,画布绘制的文本使用,我不能改变这一点(出于兼容性的原因,并在画布中使用不同的字体获得基线对齐的文本)。另一方面,HTML元素上的绝对定位使用顶部或底部边距,但不使用基线 描述了各种垂直测量,但指出它们仅在Chrome上
- 开箱即用,画布绘制的文本使用,我不能改变这一点(出于兼容性的原因,并在画布中使用不同的字体获得基线对齐的文本)。另一方面,HTML元素上的绝对定位使用顶部或底部边距,但不使用基线
- 描述了各种垂直测量,但指出它们仅在Chrome上可用,甚至只有在设置了一些标志后才可用。我需要的东西,工作在一个体面的百分比(说>80%)的当前浏览器
- 我不知道将使用什么字体,所以硬编码一些有关字体的度量信息不是一个选项
垂直对齐
CSS属性,方法是创建具有定义良好基线的外部框,然后将按钮嵌套在与相同基线对齐的外部框内您可以添加一点,根据不同浏览器的文本渲染引擎,度量标准会略有不同 如果没有低层次的度量标准,文本是很难而且不容易获得正确的。正如你所说的,它们目前只在Chrome的实验标志下可用
另一种方法是使用第二个画布元素简单地创建按钮。您仍然可以接收单击事件等,但现在您的字体条件与主画布中的相同。可能有一种方法可以使用画布功能在画布上绘制svg,以及svg中更发达的和其他与文本相关的元素 但是,这个解决方案远不是完美的,并且有很多警告,但我们将在这个小示例之后讨论它:
function-basedLineText(canvasStr、ctx、HTMLStr、HTMLContainer、baseline、x、y、font){
//创建svg元素
var svgNS=http://www.w3.org/2000/svg';
var svg=document.createElements(svgNS,'svg');
var txt=document.createElements(svgNS,“text”);
var tspan=document.createElements(svgNS,“tspan”);
txt.setAttribute('主基线',基线);
setAttribute('x',x);
setAttribute('y',y);
tspan.setAttribute('style','font:'+font);
txt.appendChild(tspan);
appendChild(txt);
//我们将首先处理画布部分
tspan.textContent=canvassr;
//我们需要将其附加到文档中以获取其边界框
HTMLContainer.appendChild(svg);
//删除文本周围的空白
var bB=svg.getBBox();
setAttribute('viewBox',bB.x+','+bB.y+','+bB.width+','+bB.height);
setAttribute('width',bB.width);
setAttribute('height',bB.height);
//获取文本的结束x位置
var left=txt.getBoundingClientRect().right;
//把它画到画布上
var svgData=新的XMLSerializer().serializeToString(svg);
var img=新图像();
img.onload=函数(){
ctx.drawImage(this,x,y);
};
img.src='data:image/svg+xml;charset=utf8',+encodeURIComponent(svgData);
//现在是HTML方面
tspan.textContent=HTMLStr;
//文本大小可能已更改
var bB=svg.getBBox();
setAttribute('viewBox',bB.x+','+bB.y+','+bB.width+','+bB.height);
setAttribute('width',bB.width);
setAttribute('height',bB.height);
//移动我们绝对定位的容器
HTMLContainer.style.top=y+'px';
HTMLContainer.style.left=(x+left)+'px';
}
var ctx=canvas.getContext('2d');
basedLineText('hello',ctx',world',tst1',字母',20,20',12px“无衬线”);
basedLineText('hello',ctx',world',tst2',middle',20,60',32px monospace');
basedLineText('hello',ctx',world',tst3',hanging',20,80',64px草书')代码>
div,
帆布{
位置:绝对位置;
排名:0;
左:0px;
}
/*只有html内容受影响*/
div{
填充:绿色;
}
这是一种有趣的方法。我没有考虑过SVG。我想知道我是否可以不用“将图像绘制到画布”步骤,通过使用SVG来测量我将要绘制到画布的文本。将需要一些实验,但这听起来像是另一种选择。不幸的是,我不认为画布文本方法和svg方法在所有UAs中都足够相似,可以避免drawImage部分。这显然很旧,但如果您可以将textbeliness
属性更改为“middle”,则可以很好地对齐它们。您只需要获取按钮的行高css属性。然后您可以计算y
在画布上绘制文本的位置,如下所示:var y=cssLineHeight/2
。我在制作这个库时发现了这一点: