2017-10-09 223 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