2017-10-09 221 views
1

编辑︰最初我只检查桌面浏览器 - 但与移动浏览器,图片更加复杂。Canvas.measureText在浏览器上的差异是巨大的

我遇到了一些有关浏览器和它的文本渲染功能的奇怪问题,我不确定我是否可以做任何事情来避免这种情况。

似乎WebKit和(不太一致)Android上的Firefox使用2D Canvas库创建稍大的文本。现在我想忽略视觉外观,而是将注意力放在文字测量上,因为可以轻松比较这些测量。

我已经使用了两种常见的方法来计算文本宽度:

  • 画布2D API和测量文本
  • DOM方法

在这个问题中概述:Calculate text width with JavaScript 然而,两者的结果都差不多(在所有浏览器中)。

function getTextWidth(text, font) { 
    // if given, use cached canvas for better performance 
    // else, create new canvas 
    var canvas = getTextWidth.canvas || (getTextWidth.canvas = document.createElement("canvas")); 
    var context = canvas.getContext("2d"); 
    context.font = font; 
    var metrics = context.measureText(text); 
    return metrics.width; 
}; 

function getTextWidthDOM(text, font) { 
    var f = font || '12px arial', 
     o = $('<span>' + text + '</span>') 
      .css({'font': f, 'float': 'left', 'white-space': 'nowrap'}) 
      .css({'visibility': 'hidden'}) 
      .appendTo($('body')), 
     w = o.width(); 

    return w; 
} 

我修改了使用谷歌字体的小提琴一点,允许执行一组样品字体的文本度(请等待被点击测量按钮之前先加载网络字体):

http://jsfiddle.net/aj7v5e4L/15/ (更新强制字体粗细和样式)

运行此在各种浏览器显示(使用字符串“S”),我有问题:

Measurements for the string 'S'

所有桌面浏览器之间的差异都很小 - 只有Safari脱颖而出 - 根据字体的不同,它大约在我看过的1%和4%的范围内。所以它不是很大 - 但是抛弃了我的计算。

更新:测试了几个移动浏览器 - 在iOS上,所有与Safari相同的级别(在引擎盖下使用WebKit,所以没有惊喜) - 而且Android上的Firefox非常开关。

我读过所有浏览器(例如较老的IE浏览器)都不支持子像素精度 - 但即使舍入也无济于事 - 因为我可能会得到不同的宽度。

不使用webfont,只使用标准字体,上下文自带返回Chrome和Safari之间完全相同的测量值 - 所以我认为它仅与webfonts相关。

我对我现在可能做的事情有点困惑 - 因为我认为我只是做了一些错误的事情,因为我没有在网络上发现任何东西 - 但小提琴就像它可以得到的那样简单。我花了整整一天的时间 - 所以你们现在是我唯一的希望。

我头脑中有一些丑陋的解决方法(例如,在受影响的浏览器上渲染文本的4%小) - 我真的很想避免。

+0

你的结果表是用字符串'“S”'做成的?如果是这样的话,我已经在第一个字体的firefox上获得了'15.73333..',这更接近你的Safari浏览器结果,而不是任何其他的。目前没有使用真正的键盘,但是当您强制使用字体重量时会发生什么? – Kaiido

+0

是的,是用字符串“S”创建的 - 甚至没有触及移动浏览器的领域 - 这可能会引发更多问题;) - 将尝试字体重量 – Michael

回答

1

看来,Safari(和一些其他人)确实支持获取子像素级别,但不绘制...

当您将字号设置为9.5pt时,此值将转换为12.6666 ... px

即使Safari浏览器也换来这样的一个高的精度值:

console.log(getComputedStyle(document.body)['font-size']); 
 
// on Safari returns 12.666666984558105px oO
body{font-size:9.5pt}

它无法在非整数字体大小正确绘制,而不是只在一个画布:

console.log(getRangeWidth("S", '12.3px serif')); 
 
// safari: 6.673828125 | FF 6.8333282470703125 
 
console.log(getRangeWidth("S", '12.4px serif')); 
 
// safari: 6.673828125 | FF 6.883331298828125 
 
console.log(getRangeWidth("S", '12.5px serif')); 
 
// safari 7.22998046875 | FF 6.95001220703125 
 
console.log(getRangeWidth("S", '12.6px serif')); 
 
// safari 7.22998046875 | FF 7 
 

 
// High precision DOM based measurement 
 
function getRangeWidth(text, font) { 
 
    var f = font || '12px arial', 
 
     o = $('<span>' + text + '</span>') 
 
      .css({'font': f, 'white-space': 'nowrap'}) 
 
      .appendTo($('body')), 
 
     r = document.createRange(); 
 
r.selectNode(o[0]); 
 
var w = r.getBoundingClientRect().width; 
 
o.remove(); 
 
return w; 
 
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

所以为了避免这些怪癖, 尝试始终使用带整数值的px单位。

+0

Android上的Ps:FF看起来非常奇怪:12.3!= 12.4 == 12.5 == 12.6 ... – Kaiido

+0

太棒了 - 有一个快速的外观和像素整数字体大小确实似乎是一致的(需要做更多的检查)。一个新的小提琴演示,并允许在飞行中编辑字体大小:http://jsfiddle.net/aj7v5e4L/17/非常感谢你Kaiido,这真的帮助。 – Michael