Javascript 在画布文本和html元素之间共享基线

Javascript 在画布文本和html元素之间共享基线,javascript,html,css,canvas,vertical-alignment,Javascript,Html,Css,Canvas,Vertical Alignment,我将一些文本绘制到一个HTML画布元素,在该文本旁边有一个简单的CSS样式和绝对定位的HTML按钮,位置由JavaScript计算。我希望两者使用相同的字体,这似乎很容易,我也希望两者共享相同的基线。据我所知,至少考虑到我所面临的一系列约束条件,这一点目前确实很难做到 开箱即用,画布绘制的文本使用,我不能改变这一点(出于兼容性的原因,并在画布中使用不同的字体获得基线对齐的文本)。另一方面,HTML元素上的绝对定位使用顶部或底部边距,但不使用基线 描述了各种垂直测量,但指出它们仅在Chrome上

我将一些文本绘制到一个HTML画布元素,在该文本旁边有一个简单的CSS样式和绝对定位的HTML按钮,位置由JavaScript计算。我希望两者使用相同的字体,这似乎很容易,我也希望两者共享相同的基线。据我所知,至少考虑到我所面临的一系列约束条件,这一点目前确实很难做到

  • 开箱即用,画布绘制的文本使用,我不能改变这一点(出于兼容性的原因,并在画布中使用不同的字体获得基线对齐的文本)。另一方面,HTML元素上的绝对定位使用顶部或底部边距,但不使用基线
  • 描述了各种垂直测量,但指出它们仅在Chrome上可用,甚至只有在设置了一些标志后才可用。我需要的东西,工作在一个体面的百分比(说>80%)的当前浏览器
  • 我不知道将使用什么字体,所以硬编码一些有关字体的度量信息不是一个选项
那么我有什么选择呢

  • 我是否必须将字体渲染到第二个屏幕外画布,以便找到实际尺寸?我甚至可以相信,从画布上标记的像素计算出的这些实际尺寸是否与用于计算按钮大小的尺寸相匹配,即使是对于一些更艺术的字体

  • 我需要吗?顺便说一句,我的问题和我的问题之间的主要区别可能是我用JavaScript做定位,所以我不需要纯CSS解决方案,我可以使用画布可以给我的帮助

  • 是否有其他方法(即没有间隔图像)来利用
    垂直对齐
    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
    。我在制作这个库时发现了这一点: