簡體   English   中英

在 Javascript 中將 em 轉換為 px(並獲取默認字體大小)

[英]Converting em to px in Javascript (and getting default font size)

在某些情況下,獲取em測量的准確像素寬度可能很有用。 例如,假設您有一個元素具有以 em 為單位測量的 CSS 屬性(如邊框或填充),並且您需要獲取邊框或填充的確切像素寬度。 有一個涉及此主題的現有問題:

如何使用 JavaScript 或 JQuery 獲取以像素為單位的默認字體大小?

這個問題詢問有關獲取默認字體大小的問題- 這是將相對em值轉換為精確px值所必需的。

這個答案很好地解釋了如何獲取元素的默認字體大小:

由於 ems 測量寬度,您始終可以通過創建一個 1000 ems 長的 div 並將其 client-Width 屬性除以 1000 來計算確切的像素字體大小。我似乎記得 ems 被截斷到最接近的千分之一,所以你需要 1000 ems避免錯誤截斷像素結果。

因此,以這個答案為指導,我編寫了以下函數來獲取默認字體大小:

function getDefaultFontSize(parentElement)
{
    parentElement = parentElement || document.body;
    var div = document.createElement('div');
    div.style.width = "1000em";
    parentElement.appendChild(div);
    var pixels = div.offsetWidth / 1000;
    parentElement.removeChild(div);
    return pixels;
}

獲得默認字體大小后,只需將em乘以元素的默認字體大小並將結果四舍五入,即可將任何em寬度轉換為px寬度:

Math.round(ems * getDefaultFontSize(element.parentNode))

問題:理想情況下, getDefaultFontSize應該是一個簡單、無副作用的函數,它返回默認字體大小。 但它確實有一個不幸的副作用:它修改了 DOM! 它附加一個孩子,然后刪除孩子。 為了獲得有效的offsetWidth屬性,必須附加孩子。 如果您不將子div附加到 DOM,則offsetWidth屬性將保持為 0,因為該元素永遠不會被渲染。 即使我們立即刪除子元素,此函數仍會產生意想不到的副作用,例如觸發正在偵聽父元素的事件(如 Internet Explorer 的onpropertychange或 W3C 的DOMSubtreeModified事件)。

問題:有沒有辦法編寫一個真正無副作用的getDefaultFontSize()函數,它不會意外觸發事件處理程序或導致其他意外副作用?

編輯:不,沒有。

在不影響 DOM 的情況下獲取給定元素的渲染字體大小:

parseFloat(getComputedStyle(parentElement).fontSize);

這是基於對這個問題的回答。


編輯:

在 IE 中,您必須使用parentElement.currentStyle["fontSize"] ,但這不能保證將大小轉換為px 所以就這樣了。

此外,這個片段不會讓你獲得元素的默認字體大小,而是它的實際字體大小,如果它實際上有一個與之關聯的類和樣式,這很重要。 換句話說,如果元素的字體大小是2em ,您將獲得 2 em 的像素數。 除非內聯指定字體大小,否則您將無法獲得正確的轉換率。

我有一個更好的答案。 我的代碼將存儲1em的長度(在 JavaScript 變量em以 CSS 像素px單位:

  1. 將此div放在 HTML 代碼中的任何位置

    <div id="div" style="height:0;width:0;outline:none;border:none;padding:none;margin:none;box-sizing:content-box;"></div>
  2. 將此函數放在您的 JavaScript 文件中

    var em; function getValue(id){ var div = document.getElementById(id); div.style.height = '1em'; return ( em = div.offsetHeight ); }

現在,每當您在參數中使用該測試 div 的 id 調用此函數'getValue'時,您將擁有一個變量名稱em ,它將包含 1 em 的 px 值。

如果您需要快速而骯臟的東西(並且基於正文的基本字體大小,而不是元素),我會選擇:

Number(getComputedStyle(document.body,null).fontSize.replace(/[^\d]/g, ''))

 Number(  // Casts numeric strings to number
   getComputedStyle(  // takes element and returns CSSStyleDeclaration object
     document.body,null) // use document.body to get first "styled" element
         .fontSize  // get fontSize property
          .replace(/[^\d]/g, '')  // simple regex that will strip out non-numbers
 ) // returns number

以下解決方案使用二分搜索和 window.matchMedia 來找出窗口的 em 寬度。 然后我們將窗口 em 寬度除以窗口像素寬度以獲得最終結果。 不涉及 DOM,無副作用! 在性能方面,我的comp上大約需要 8 次迭代,大約 0.2 毫秒。

const singleEmInPixels = getSingleEmInPixels();
console.log(`1em = ${singleEmInPixels}px`);

/**
 * returns the amount of pixels for a single em
 */
function getSingleEmInPixels() {
    let low = 0;
    let high = 200;
    let emWidth = Math.round((high - low) / 2) + low;
    const time = performance.now();
    let iters = 0;
    const maxIters = 10;
    while (high - low > 1) {
        const match = window.matchMedia(`(min-width: ${emWidth}em)`).matches;
        iters += 1;
        if (match) {
            low = emWidth;
        } else {
            high = emWidth;
        }
        emWidth = Math.round((high - low) / 2) + low;
        if (iters > maxIters) {
            console.warn(`max iterations reached ${iters}`);
            break;
        }
    }
    const singleEmPx = Math.ceil(window.innerWidth / emWidth);
    console.log(`window em width = ${emWidth}, time elapsed =  ${(performance.now() - time)}ms`);
    return singleEmPx;
}

我需要 em 尺寸,這就是我着陸的地方。 我受到@10011101111 的解決方案的啟發,編寫了這個通用解決方案。 請注意,這可能不一定沒有 OP 要求的副作用,但可以對其進行調整以將副作用降至最低。

這個解決方案適用於那些和我有同樣問題的人。

要獲得全像素結果:

function getSize(size = '1em', parent = document.body) {
    let l = document.createElement('div')
    l.style.visibility = 'hidden'
    l.style.boxSize = 'content-box'
    l.style.position = 'absolute'
    l.style.maxHeight = 'none'
    l.style.height = size
    parent.appendChild(l)
    size = l.clientHeight
    l.remove()
    return size
}

要獲得更精確的結果:

function getSizePrecise(size = '1em', parent = document.body) {
    let l = document.createElement('div'), i = 1, s, t
    l.style.visibility = 'hidden'
    l.style.boxSize = 'content-box'
    l.style.position = 'absolute'
    l.style.maxHeight = 'none'
    l.style.height = size
    parent.appendChild(l)
    t = l.clientHeight
    do {
        s = t
        i *= 10
        l.style.height = 'calc(' + i + '*' + size + ')'
        t = l.clientHeight
    } while(t !== s * 10)
    l.remove()
    return t / i
}

請注意,您可以傳入任何單位的任何大小。 例如:

getSizePrecise('1ex')
getSizePrecise('1.111ex')

您也可以通過刪除calc()函數並直接將除單位之外的大小值乘以 JavaScript 中的乘數來適應 CSS < 3 兼容性。

如果值溢出,這可能會返回 0。

在試圖找出溢出限制時,我在 Firefox 88.0 上達到了 17895696 或 17895697 的像素值。 所以,我更新了上面的函數如下:

function getSizePrecise(size = '1em', parent = document.body) {
    let l = document.createElement('div'), i = 1, s, t
    l.style.visibility = 'hidden'
    l.style.boxSize = 'content-box'
    l.style.position = 'absolute'
    l.style.maxHeight = 'none'
    l.style.height = size
    parent.appendChild(l)
    t = l.clientHeight
    do {
        if (t > 1789569.6)
            break
        s = t
        i *= 10
        l.style.height = 'calc(' + i + '*' + size + ')'
        t = l.clientHeight
    } while(t !== s * 10)
    l.remove()
    return t / i
}

.6 絕對不是必需的(因為 t 可能是一個完整的值),但我將其留在那里以供參考。

以下 JS 函數將任何 CSS 長度單位值轉換為另一個。
干杯! 🙂

// Usage examples: convertCSSLengthUnit('26.556016597510375mm'); convertCSSLengthUnit('26.556016597510375mm', 'px'); convertCSSLengthUnit('100px', 'mm');
function convertCSSLengthUnit(fromUnitValue, toUnit){
  let value = fromUnitValue.match(/[0-9]+\.*[0-9]*/g)[0];
  let unit = fromUnitValue.match(/[a-zA-Z]+/g)[0];

  let frag = document.createRange().createContextualFragment(`
    <div style='all: initial; pointer-events: none; display: block; position: absolute; border: none; padding: 0; margin: 0; background: rgba(0,0,0,0); color: color: rgba(0,0,0,0); width: 1${unit}; height: 1px;'></div>
  `);
  document.body.appendChild(frag);
  let measureElement = document.body.children[document.body.children.length-1];
  let toUnitValuePixel = measureElement.getBoundingClientRect().width * value; // X
  measureElement.remove();

  if(toUnit){
    let frag = document.createRange().createContextualFragment(`
      <div style='all: initial; pointer-events: none; display: block; position: absolute; border: none; padding: 0; margin: 0; background: rgba(0,0,0,0); color: color: rgba(0,0,0,0); width: 1${toUnit}; height: 1px;'></div>
    `);
    document.body.appendChild(frag);
    let measureElement = document.body.children[document.body.children.length-1];
    let valueUnit = measureElement.getBoundingClientRect().width; // Y
    measureElement.remove();

    // Given: Aem and Bmm with B=1. We know: Aem = Xpx and Bmm = Ypx. Therefore: 1px = Bmm/Y Result: Aem = Xpx = X * 1px = X * Bmm/Y <=> Aem = X * 1mm/Y (with B=1) <=> Aem = X/Ymm
    return (toUnitValuePixel / valueUnit) + toUnit;
  }
  return toUnitValuePixel + 'px';
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM