คุณจะค้นหาความสูงของข้อความบนผ้าใบ HTML ได้อย่างไร


148

spec มีฟังก์ชั่น context.measureText (ข้อความ) ที่จะบอกคุณว่าต้องใช้ความกว้างเท่าใดในการพิมพ์ข้อความนั้น แต่ฉันไม่สามารถหาวิธีที่จะค้นหาความสูงของมันได้ ฉันรู้ว่ามันขึ้นอยู่กับฟอนต์ แต่ฉันไม่รู้ว่าจะแปลงสตริงฟอนต์เป็นความสูงข้อความ


1
ฉันชอบที่จะรู้วิธีที่ดีกว่าคำตอบด้านบน หากมีอัลกอริทึมที่จะใช้แบบอักษรตามอำเภอใจและหาขอบเขตสูงสุด / นาทีในนั้นฉันก็ยินดีที่จะได้ยินเกี่ยวกับมัน =)
beatgammit

@tjameson - ดูเหมือนว่าจะมี ดูคำตอบจาก ellisbben (และการปรับปรุงของฉันกับมัน)
Daniel Earwicker

2
ฉันสงสัยว่าอักขระ Unicode 'FULL BLOCK' (U + 2588) สามารถใช้เป็นการประมาณได้โดยการคูณความกว้างเป็นสองเท่า
Daniel F

1
เป็นที่น่าสังเกตว่าคำตอบนั้นขึ้นอยู่กับความต้องการของคุณเล็กน้อย ตัวอย่างเช่นความสูงที่จำเป็นในการแสดงอักขระ "a" นั้นแตกต่างจากความสูงที่ต้องใช้ในการแสดงอักขระ "y" เนื่องจากตัวขยายที่ขยายด้านล่างพื้นฐานของแบบอักษร คำตอบที่ใช้ HTML ด้านล่างไม่ได้คำนึงถึงสิ่งนี้และจะให้ความสูงทั่วไปที่เหมาะสมสำหรับข้อความใด ๆ ในขณะที่คำตอบของ @ Noitidart ให้ความสูงที่แน่นอนมากขึ้นสำหรับข้อความที่เฉพาะเจาะจง
Dale Anderson

2
โปรดจำไว้ว่าคุณสามารถมีตัวละครที่มีลักษณะเช่นนี้M̶̢̹̝͖̦̖̭͕̭̣͆̃̀̅̒̊͌̿ͅดังนั้นนี่เป็นปัญหาที่ยุ่งยากมากดังนั้นควรแก้ไขกรณีทั่วไป
GetFree

คำตอบ:


78

UPDATE - ตัวอย่างของการทำงานนี้ผมใช้เทคนิคนี้ในการแก้ไข Carota

ต่อไปนี้จากคำตอบของ ellisbben ต่อไปนี้เป็นรุ่นปรับปรุงเพื่อรับ ascent และ descent จาก baseline เช่นเดียวกับtmAscentและtmDescentส่งคืนโดย Win32's GetTextMetric API นี่เป็นสิ่งจำเป็นหากคุณต้องการใช้ข้อความที่มีคำครอบคลุมในแบบอักษร / ขนาดต่าง ๆ

ข้อความขนาดใหญ่บนผืนผ้าใบที่มีเส้นเมตริก

ภาพด้านบนสร้างขึ้นบนผืนผ้าใบใน Safari สีแดงเป็นบรรทัดบนสุดที่ผ้าใบบอกให้วาดข้อความสีเขียวเป็นพื้นฐานและสีน้ำเงินอยู่ด้านล่าง (ดังนั้นสีแดงถึงสีน้ำเงินจึงมีความสูงเต็ม)

ใช้ jQuery เพื่อความกระชับ:

var getTextHeight = function(font) {

  var text = $('<span>Hg</span>').css({ fontFamily: font });
  var block = $('<div style="display: inline-block; width: 1px; height: 0px;"></div>');

  var div = $('<div></div>');
  div.append(text, block);

  var body = $('body');
  body.append(div);

  try {

    var result = {};

    block.css({ verticalAlign: 'baseline' });
    result.ascent = block.offset().top - text.offset().top;

    block.css({ verticalAlign: 'bottom' });
    result.height = block.offset().top - text.offset().top;

    result.descent = result.height - result.ascent;

  } finally {
    div.remove();
  }

  return result;
};

นอกเหนือจากองค์ประกอบข้อความฉันเพิ่ม div ด้วยdisplay: inline-blockดังนั้นฉันจึงสามารถกำหนดvertical-alignสไตล์ของมันแล้วหาว่าเบราว์เซอร์วางไว้ที่ใด

เพื่อให้คุณได้รับกลับวัตถุที่มีascent, descentและheight(ซึ่งเป็นเพียงascent+ descentเพื่อความสะดวก) เพื่อทดสอบมันคุ้มค่าที่จะมีฟังก์ชั่นที่วาดเส้นแนวนอน:

var testLine = function(ctx, x, y, len, style) {
  ctx.strokeStyle = style; 
  ctx.beginPath();
  ctx.moveTo(x, y);
  ctx.lineTo(x + len, y);
  ctx.closePath();
  ctx.stroke();
};

จากนั้นคุณจะเห็นว่าข้อความอยู่ในตำแหน่งใดบนผืนผ้าใบที่สัมพันธ์กับด้านบนพื้นฐานและล่าง:

var font = '36pt Times';
var message = 'Big Text';

ctx.fillStyle = 'black';
ctx.textAlign = 'left';
ctx.textBaseline = 'top'; // important!
ctx.font = font;
ctx.fillText(message, x, y);

// Canvas can tell us the width
var w = ctx.measureText(message).width;

// New function gets the other info we need
var h = getTextHeight(font);

testLine(ctx, x, y, w, 'red');
testLine(ctx, x, y + h.ascent, w, 'green');
testLine(ctx, x, y + h.height, w, 'blue');

3
ทำไมไม่ใช้ข้อความนี้เพื่อกำหนดความสูง? abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ทั้งนี้ขึ้นอยู่กับฟอนต์ที่คุณอาจมีอักขระที่สูงหรือต่ำกว่า g และ M
omatase

1
@ellisbben เป็นเรื่องที่ควรสังเกตว่าผลลัพธ์ของสิ่งนี้แตกต่างจากของคุณเล็กน้อยแม้ว่าฉันไม่รู้ว่าทำไม ตัวอย่างเช่นคุณบอกว่า Courier New 8pt ==> สูง 12 พิกเซลในขณะที่สิ่งนี้บอกว่า: Courier New 8pt ==> สูง 13 พิกเซล ฉันเพิ่ม "g" ให้กับวิธีการของคุณ แต่นั่นก็ไม่ได้แตกต่างกัน สิ่งมหัศจรรย์หนึ่งซึ่งค่าจะมีประโยชน์มากที่สุด (ไม่จำเป็นต้องถูกต้องทางเทคนิค)
Orwellophile

3
ฉันสามารถทำงานได้อย่างถูกต้องเมื่อฉันเปลี่ยนบรรทัดแรกของgetTextHeight()เป็นvar text = $('<span>Hg</span>').css({ 'font-family': fontName, 'font-size' : fontSize });เท่านั้นคือการเพิ่มขนาดแยกกัน
cameron.bracken

1
วิธีทำให้ข้อความที่ไม่ใช่ภาษาอังกฤษทำงานได้อย่างไร ดูjsfiddle.net/siddjain/6vURk
morpheus

1
ขอบคุณ ! ปรับเปลี่ยน<div></div>เป็น<div style="white-space : nowrap;"></div>จัดการกับสตริงที่ยาวมาก
Mickaël Gauvin

40

คุณสามารถวัดความสูงในแนวดิ่งได้อย่างใกล้ชิดโดยตรวจสอบความยาวของทุน M

ctx.font='bold 10px Arial';

lineHeight=ctx.measureText('M').width;

8
ความกว้างทำให้เราประมาณความสูงของเส้นได้อย่างไร
Richard Barker

11
พวกเขาหมายถึงความกว้างของตัวพิมพ์ใหญ่ 'M' ที่ขนาดตัวอักษรที่กำหนดนั้นมีความยาวเท่ากับความสูงของเส้น (ไม่รู้ว่านี่เป็นเรื่องจริงหรือไม่ แต่นั่นคือคำตอบที่พูด)
นาธาน

2
นี่เป็นการประมาณที่ดีพอสมควรที่ฉันใช้สำหรับการสร้างต้นแบบอย่างรวดเร็ว มันไม่สมบูรณ์แบบ แต่มันเป็นโซลูชัน 90%
Austin D

37

ข้อมูลจำเพาะผ้าใบไม่ได้ให้วิธีการวัดความสูงของสตริงกับเรา อย่างไรก็ตามคุณสามารถกำหนดขนาดของข้อความเป็นพิกเซลและโดยทั่วไปคุณสามารถทราบได้ว่าขอบเขตแนวตั้งนั้นค่อนข้างง่าย

หากคุณต้องการบางสิ่งที่แม่นยำยิ่งขึ้นคุณสามารถโยนข้อความลงบนผืนผ้าใบแล้วรับข้อมูลพิกเซลและหาจำนวนพิกเซลที่ใช้ในแนวตั้ง นี่จะค่อนข้างง่าย แต่ไม่ค่อยมีประสิทธิภาพ คุณสามารถทำสิ่งนี้ได้ (ใช้งานได้ แต่วาดข้อความบางส่วนลงบนผืนผ้าใบที่คุณต้องการลบ):

function measureTextHeight(ctx, left, top, width, height) {

    // Draw the text in the specified area
    ctx.save();
    ctx.translate(left, top + Math.round(height * 0.8));
    ctx.mozDrawText('gM'); // This seems like tall text...  Doesn't it?
    ctx.restore();

    // Get the pixel data from the canvas
    var data = ctx.getImageData(left, top, width, height).data,
        first = false, 
        last = false,
        r = height,
        c = 0;

    // Find the last line with a non-white pixel
    while(!last && r) {
        r--;
        for(c = 0; c < width; c++) {
            if(data[r * width * 4 + c * 4 + 3]) {
                last = r;
                break;
            }
        }
    }

    // Find the first line with a non-white pixel
    while(r) {
        r--;
        for(c = 0; c < width; c++) {
            if(data[r * width * 4 + c * 4 + 3]) {
                first = r;
                break;
            }
        }

        // If we've got it then return the height
        if(first != r) return last - first;
    }

    // We screwed something up...  What do you expect from free code?
    return 0;
}

// Set the font
context.mozTextStyle = '32px Arial';

// Specify a context and a rect that is safe to draw in when calling measureTextHeight
var height = measureTextHeight(context, 0, 0, 50, 50);
console.log(height);

สำหรับ Bespin นั้นพวกเขาทำการปลอมความสูงโดยการวัดความกว้างของตัวพิมพ์เล็ก ... ฉันไม่รู้ว่ามันใช้งานอย่างไรและฉันจะไม่แนะนำวิธีนี้ นี่คือวิธี Bespin ที่เกี่ยวข้อง:

var fixCanvas = function(ctx) {
    // upgrade Firefox 3.0.x text rendering to HTML 5 standard
    if (!ctx.fillText && ctx.mozDrawText) {
        ctx.fillText = function(textToDraw, x, y, maxWidth) {
            ctx.translate(x, y);
            ctx.mozTextStyle = ctx.font;
            ctx.mozDrawText(textToDraw);
            ctx.translate(-x, -y);
        }
    }

    if (!ctx.measureText && ctx.mozMeasureText) {
        ctx.measureText = function(text) {
            ctx.mozTextStyle = ctx.font;
            var width = ctx.mozMeasureText(text);
            return { width: width };
        }
    }

    if (ctx.measureText && !ctx.html5MeasureText) {
        ctx.html5MeasureText = ctx.measureText;
        ctx.measureText = function(text) {
            var textMetrics = ctx.html5MeasureText(text);

            // fake it 'til you make it
            textMetrics.ascent = ctx.html5MeasureText("m").width;

            return textMetrics;
        }
    }

    // for other browsers
    if (!ctx.fillText) {
        ctx.fillText = function() {}
    }

    if (!ctx.measureText) {
        ctx.measureText = function() { return 10; }
    }
};

28
ฉันสงสัยว่านี่คือสิ่งที่คนที่เขียนสเป็ค HTML5 ไว้ในใจ
Steve Hanov

17
นี่คือการแฮ็กที่แย่มากที่ฉันรักอย่างแน่นอน +1
Allain Lalonde

1
ฉันไม่เข้าใจ การเชื่อมต่อระหว่างตัวอักษรขึ้นและความกว้างของตัวอักษร "m" อยู่ที่ไหน?
kayahr

6
emเป็นการวัดแบบอักษรที่เกี่ยวข้องโดยที่หนึ่ง em เท่ากับความสูงของตัวอักษรMในขนาดแบบอักษรเริ่มต้น
jerone

1
ใช่ความสูงไม่ใช่ความกว้าง ... ฉันยังสับสนกับการเชื่อมต่อ นอกจากนี้ฉันคิดว่า ems นั้นไม่เกี่ยวข้องเนื่องจากเราให้ความสำคัญกับความสูงเป็นพิกเซลเท่านั้น
Prestaul

35

เบราว์เซอร์เริ่มสนับสนุนตัวชี้วัดข้อความขั้นสูงซึ่งจะทำให้งานนี้เล็กน้อยเมื่อได้รับการสนับสนุนอย่างกว้างขวาง:

let metrics = ctx.measureText(text);
let fontHeight = metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent;
let actualHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;

fontHeight ทำให้คุณได้รับความสูงของกล่องขอบเขตที่คงที่โดยไม่คำนึงถึงสตริงที่ถูกเรนเดอร์ actualHeightเฉพาะเจาะจงกับสตริงที่ถูกเรนเดอร์

ข้อมูลจำเพาะ: https://www.w3.org/TR/2012/CR-2dcontext-20121217/#dom-textmetrics-fontboundingboxascentและส่วนที่อยู่ด้านล่าง

สถานะการสนับสนุน (20 ส.ค. 2017):


2
ทุกคนโปรดลงคะแนนในหน้าข้อผิดพลาดเพื่อให้คุณสมบัติเหล่านี้ใช้งานได้เร็วขึ้น
Jeremiah Rose

21

แก้ไข: คุณใช้ผ้าใบแปลงหรือไม่ ถ้าเป็นเช่นนั้นคุณจะต้องติดตามเมทริกซ์การแปลง วิธีต่อไปนี้ควรวัดความสูงของข้อความด้วยการแปลงเริ่มต้น

แก้ไข # 2: รหัสด้านล่างผิดปกติไม่ได้ให้คำตอบที่ถูกต้องเมื่อฉันเรียกใช้ในหน้า StackOverflow นี้ เป็นไปได้อย่างสิ้นเชิงว่าการมีกฎสไตล์บางอย่างอาจทำลายฟังก์ชันนี้ได้

Canvas ใช้ฟอนต์ตามที่กำหนดโดย CSS ดังนั้นในทางทฤษฎีเราสามารถเพิ่มข้อความที่มีสไตล์อย่างเหมาะสมลงในเอกสารและวัดความสูงได้ ฉันคิดว่าสิ่งนี้ง่ายกว่าการแสดงข้อความอย่างมากจากนั้นตรวจสอบข้อมูลพิกเซลและควรเคารพตัวขึ้นและลง ตรวจสอบสิ่งต่อไปนี้:

var determineFontHeight = function(fontStyle) {
  var body = document.getElementsByTagName("body")[0];
  var dummy = document.createElement("div");
  var dummyText = document.createTextNode("M");
  dummy.appendChild(dummyText);
  dummy.setAttribute("style", fontStyle);
  body.appendChild(dummy);
  var result = dummy.offsetHeight;
  body.removeChild(dummy);
  return result;
};

//A little test...
var exampleFamilies = ["Helvetica", "Verdana", "Times New Roman", "Courier New"];
var exampleSizes = [8, 10, 12, 16, 24, 36, 48, 96];
for(var i = 0; i < exampleFamilies.length; i++) {
  var family = exampleFamilies[i];
  for(var j = 0; j < exampleSizes.length; j++) {
    var size = exampleSizes[j] + "pt";
    var style = "font-family: " + family + "; font-size: " + size + ";";
    var pixelHeight = determineFontHeight(style);
    console.log(family + " " + size + " ==> " + pixelHeight + " pixels high.");
  }
}

คุณจะต้องตรวจสอบให้แน่ใจว่าคุณได้รับรูปแบบตัวอักษรที่ถูกต้องในองค์ประกอบ DOM ที่คุณวัดความสูงของ แต่ค่อนข้างตรงไปตรงมา; จริงๆคุณควรใช้สิ่งที่ชอบ

var canvas = /* ... */
var context = canvas.getContext("2d");
var canvasFont = " ... ";
var fontHeight = determineFontHeight("font: " + canvasFont + ";");
context.font = canvasFont;
/*
  do your stuff with your font and its height here.
*/

1
+1 วิธีที่ดีกว่า IMO มันควรจะเป็นไปได้ที่จะได้รับตำแหน่งของพื้นฐานเช่นกัน
Daniel Earwicker

ได้เพิ่มคำตอบที่ได้รับพื้นฐาน
Daniel Earwicker

มันใช้ได้ไหม? ฉันไม่ได้คิดจะติดมันในกอง สิ่งนี้อาจไม่จำเป็นต้องเพิ่มใน DOM ใช่ไหม
beatgammit

ฉันไม่รู้ว่าขนาดและตำแหน่งของโหนดใดอยู่เมื่อมันไม่ได้เป็นส่วนหนึ่งของเอกสาร ฉันสนใจเป็นพิเศษในการอ่านข้อมูลอ้างอิงที่กล่าวถึงนั้นหากคุณรู้จัก
ellisbben

5
+1 สำหรับโค้ดที่ซับซ้อนซึ่งจะเป็นบริบทหน้าจอข้อความ (ข้อความ) ความสูงในเอกภพคู่ขนานด้วย Canvas API ที่ดีกว่า
rsp

11

ความสูงของข้อความเป็นพิกเซลเท่ากับขนาดตัวอักษร (เป็น pts) หรือไม่หากคุณกำหนดแบบอักษรโดยใช้ context.font


2
นี่คือสิ่งที่มันอ้างสิทธิ์โดยแหล่งที่มานี้: html5canvastutorials.com/tutorials/html5-canvas-text-metrics
Rui Marques

สำหรับกรณีง่ายๆ: คุณสามารถแยกความสูงได้จากชื่อตัวอักษร: parseInt (ctx.font.split ('') [0] .replace ('px', '')); // การแยกสตริง: "10px Verdana"
Sean

คุณสามารถใช้ px, pt, em และ% สำหรับขนาดตัวอักษร นี่คือเหตุผลที่แม่นยำคำตอบนี้ทำให้เข้าใจผิด
Jacksonkr

@ Jacksonkr ใช่แล้ว แต่คุณยังสามารถแยกมันออกและปรับได้ตามความเหมาะสมใช่ไหม หรือมีข้อ จำกัด อยู่ที่วิธีการนี้หรือไม่?
Pacerier

@Pacerier ข้อ จำกัด คือคุณอาจแนะนำข้อบกพร่องบางอย่างที่ทำให้คุณดึงผมออกมา เพียงจำไว้ว่าประเภทหน่วยผสมสามารถนำไปสู่รหัส buggy / spaghetti ที่กล่าวว่าฉันไม่ได้แฮ็กเป็นครั้งคราวตราบเท่าที่ความเสี่ยงสำหรับปัญหาต่ำ
Jacksonkr

10

ในฐานะที่เป็น JJ Stiff แนะนำคุณสามารถเพิ่มข้อความของคุณในช่วงและจากนั้นวัดความสูงของช่วงการชดเชย

var d = document.createElement("span");
d.font = "20px arial";
d.textContent = "Hello world!";
document.body.appendChild(d);
var emHeight = d.offsetHeight;
document.body.removeChild(d);

ตามที่ปรากฏในHTML5Rocks


3
นี่เป็นวิธีแก้ปัญหาที่ดีมากขอบคุณ ... แต่ฉันไม่รู้ว่าทำไมถ้าช่วงนี้ไม่ได้ถูกเพิ่มในหน้าและมองเห็นได้ก่อนที่ฉันจะได้รับ offsetHeight! มันจะเพิ่มความสูงเป็น ZERO ใน Chrome และ Firefox เสมอ!
Mustafah

1
คุณพูดถูกฉันคิดว่ามันจะต้องถูกเพิ่มเข้าไปใน Dom เพื่อให้มีพื้นที่ว่าง นี่คือ JS Fiddle ของการทำงานนี้: jsfiddle.net/mpalmerlee/4NfVR/4ฉันได้อัปเดตโค้ดข้างต้นแล้ว
Matt Palmerlee

การใช้ไคลเอ็นต์ความสูงก็เป็นไปได้เช่นกัน คำตอบนี้เป็นวิธีการแก้ปัญหาที่ค่อนข้างจะเป็นวิธีแก้ปัญหาที่น่าเกลียด +1 อย่างไรก็ตาม
Daniel F

นี้ไม่พิจารณาความสูงที่แท้จริงของข้อความที่มองเห็นและมักจะมาพร้อมกับระยะขอบเพิ่มเติมด้านบนของข้อความ ...
Ain Tohvri

8

เพียงเพิ่มคำตอบของ Daniel (ซึ่งยอดเยี่ยม! และถูกต้องที่สุด!) เวอร์ชันที่ไม่มี JQuery:

function objOff(obj)
{
    var currleft = currtop = 0;
    if( obj.offsetParent )
    { do { currleft += obj.offsetLeft; currtop += obj.offsetTop; }
      while( obj = obj.offsetParent ); }
    else { currleft += obj.offsetLeft; currtop += obj.offsetTop; }
    return [currleft,currtop];
}
function FontMetric(fontName,fontSize) 
{
    var text = document.createElement("span");
    text.style.fontFamily = fontName;
    text.style.fontSize = fontSize + "px";
    text.innerHTML = "ABCjgq|"; 
    // if you will use some weird fonts, like handwriting or symbols, then you need to edit this test string for chars that will have most extreme accend/descend values

    var block = document.createElement("div");
    block.style.display = "inline-block";
    block.style.width = "1px";
    block.style.height = "0px";

    var div = document.createElement("div");
    div.appendChild(text);
    div.appendChild(block);

    // this test div must be visible otherwise offsetLeft/offsetTop will return 0
    // but still let's try to avoid any potential glitches in various browsers
    // by making it's height 0px, and overflow hidden
    div.style.height = "0px";
    div.style.overflow = "hidden";

    // I tried without adding it to body - won't work. So we gotta do this one.
    document.body.appendChild(div);

    block.style.verticalAlign = "baseline";
    var bp = objOff(block);
    var tp = objOff(text);
    var taccent = bp[1] - tp[1];
    block.style.verticalAlign = "bottom";
    bp = objOff(block);
    tp = objOff(text);
    var theight = bp[1] - tp[1];
    var tdescent = theight - taccent;

    // now take it off :-)
    document.body.removeChild(div);

    // return text accent, descent and total height
    return [taccent,theight,tdescent];
}

ฉันเพิ่งทดสอบโค้ดด้านบนและใช้งานได้ดีกับ Chrome, FF และ Safari ล่าสุดบน Mac

แก้ไข:ฉันได้เพิ่มขนาดตัวอักษรและทดสอบด้วย webfont แทนแบบอักษรระบบ - ใช้งานได้ดี


7

ฉันแก้ปัญหานี้ได้ยากโดยใช้การจัดการพิกเซล

นี่คือคำตอบกราฟิก:

นี่คือรหัส:

    function textHeight (text, font) {

    var fontDraw = document.createElement("canvas");

    var height = 100;
    var width = 100;

    // here we expect that font size will be less canvas geometry
    fontDraw.setAttribute("height", height);
    fontDraw.setAttribute("width", width);

    var ctx = fontDraw.getContext('2d');
    // black is default
    ctx.fillRect(0, 0, width, height);
    ctx.textBaseline = 'top';
    ctx.fillStyle = 'white';
    ctx.font = font;
    ctx.fillText(text/*'Eg'*/, 0, 0);

    var pixels = ctx.getImageData(0, 0, width, height).data;

    // row numbers where we first find letter end where it ends 
    var start = -1;
    var end = -1;

    for (var row = 0; row < height; row++) {
        for (var column = 0; column < width; column++) {

            var index = (row * width + column) * 4;

            // if pixel is not white (background color)
            if (pixels[index] == 0) {
                // we havent met white (font color) pixel
                // on the row and the letters was detected
                if (column == width - 1 && start != -1) {
                    end = row;
                    row = height;
                    break;
                }
                continue;
            }
            else {
                // we find top of letter
                if (start == -1) {
                    start = row;
                }
                // ..letters body
                break;
            }

        }

    }
   /*
    document.body.appendChild(fontDraw);
    fontDraw.style.pixelLeft = 400;
    fontDraw.style.pixelTop = 400;
    fontDraw.style.position = "absolute";
   */

    return end - start;

}

2
ฉันเชื่อว่าวิธีนี้ไม่คำนึงถึงตัวอักษรประเช่นตัวพิมพ์เล็กฉันและ j
wusauarus

3

ฉันกำลังเขียนเทอร์มินัลอีมูเลเตอร์ดังนั้นฉันต้องวาดรูปสี่เหลี่ยมผืนผ้ารอบตัวละคร

var size = 10
var lineHeight = 1.2 // CSS "line-height: normal" is between 1 and 1.2
context.font = size+'px/'+lineHeight+'em monospace'
width = context.measureText('m').width
height = size * lineHeight

เห็นได้ชัดว่าถ้าคุณต้องการจำนวนเนื้อที่ที่แน่นอนของตัวละครมันจะไม่ช่วย แต่มันจะให้การประมาณที่ดีสำหรับการใช้งานบางอย่าง


3

ฉันใช้ห้องสมุดที่ดีสำหรับการวัดความสูงและความกว้างของข้อความโดยใช้ผ้าใบ HTML สิ่งนี้ควรทำในสิ่งที่คุณต้องการ

https://github.com/ChrisBellew/text-measurer.js


คุณน่าจะอ่านวิธีนำเสนอห้องสมุดโอเพ่นซอร์สส่วนตัวได้อย่างไร? ก่อนโพสต์ห้องสมุดของคุณเพื่อตอบคำถามเพิ่มเติม
Martijn Pieters

3

นี่คือฟังก์ชั่นที่เรียบง่าย ไม่จำเป็นต้องใช้ห้องสมุด

ฉันเขียนฟังก์ชันนี้เพื่อให้ได้ขอบเขตด้านบนและด้านล่างสัมพันธ์กับ baseline หากมีการตั้งค่าtextBaseline alphabeticสิ่งที่มันทำคือมันสร้างแคนวาสอื่นแล้ววาดตรงนั้นจากนั้นค้นหาพิกเซลที่ว่างเปล่าส่วนใหญ่และไม่ว่างเปล่าด้านบนสุด และนั่นคือขอบเขตบนและล่าง มันส่งคืนมันเป็นค่าสัมพัทธ์ดังนั้นหากความสูงเท่ากับ 20px และไม่มีอะไรต่ำกว่าเส้นฐานขอบเขตบนสุดก็คือ-20และมีอะไรที่ด้านล่างพื้นฐานแล้วด้านบนมีที่ผูกไว้

คุณต้องระบุอักขระให้กับมัน มิฉะนั้นจะให้ความสูง 0 และความกว้าง 0 เห็นได้ชัด

การใช้งาน:

alert(measureHeight('40px serif', 40, 'rg').height)

นี่คือฟังก์ชั่น:

function measureHeight(aFont, aSize, aChars, aOptions={}) {
    // if you do pass aOptions.ctx, keep in mind that the ctx properties will be changed and not set back. so you should have a devoted canvas for this
    // if you dont pass in a width to aOptions, it will return it to you in the return object
    // the returned width is Math.ceil'ed
    console.error('aChars: "' + aChars + '"');
    var defaultOptions = {
        width: undefined, // if you specify a width then i wont have to use measureText to get the width
        canAndCtx: undefined, // set it to object {can:,ctx:} // if not provided, i will make one
        range: 3
    };

    aOptions.range = aOptions.range || 3; // multiples the aSize by this much

    if (aChars === '') {
        // no characters, so obviously everything is 0
        return {
            relativeBot: 0,
            relativeTop: 0,
            height: 0,
            width: 0
        };
        // otherwise i will get IndexSizeError: Index or size is negative or greater than the allowed amount error somewhere below
    }

    // validateOptionsObj(aOptions, defaultOptions); // not needed because all defaults are undefined

    var can;
    var ctx; 
    if (!aOptions.canAndCtx) {
        can = document.createElement('canvas');;
        can.mozOpaque = 'true'; // improved performanceo on firefox i guess
        ctx = can.getContext('2d');

        // can.style.position = 'absolute';
        // can.style.zIndex = 10000;
        // can.style.left = 0;
        // can.style.top = 0;
        // document.body.appendChild(can);
    } else {
        can = aOptions.canAndCtx.can;
        ctx = aOptions.canAndCtx.ctx;
    }

    var w = aOptions.width;
    if (!w) {
        ctx.textBaseline = 'alphabetic';
        ctx.textAlign = 'left'; 
        ctx.font = aFont;
        w = ctx.measureText(aChars).width;
    }

    w = Math.ceil(w); // needed as i use w in the calc for the loop, it needs to be a whole number

    // must set width/height, as it wont paint outside of the bounds
    can.width = w;
    can.height = aSize * aOptions.range;

    ctx.font = aFont; // need to set the .font again, because after changing width/height it makes it forget for some reason
    ctx.textBaseline = 'alphabetic';
    ctx.textAlign = 'left'; 

    ctx.fillStyle = 'white';

    console.log('w:', w);

    var avgOfRange = (aOptions.range + 1) / 2;
    var yBaseline = Math.ceil(aSize * avgOfRange);
    console.log('yBaseline:', yBaseline);

    ctx.fillText(aChars, 0, yBaseline);

    var yEnd = aSize * aOptions.range;

    var data = ctx.getImageData(0, 0, w, yEnd).data;
    // console.log('data:', data)

    var botBound = -1;
    var topBound = -1;

    // measureHeightY:
    for (y=0; y<=yEnd; y++) {
        for (var x = 0; x < w; x += 1) {
            var n = 4 * (w * y + x);
            var r = data[n];
            var g = data[n + 1];
            var b = data[n + 2];
            // var a = data[n + 3];

            if (r+g+b > 0) { // non black px found
                if (topBound == -1) { 
                    topBound = y;
                }
                botBound = y; // break measureHeightY; // dont break measureHeightY ever, keep going, we till yEnd. so we get proper height for strings like "`." or ":" or "!"
                break;
            }
        }
    }

    return {
        relativeBot: botBound - yBaseline, // relative to baseline of 0 // bottom most row having non-black
        relativeTop: topBound - yBaseline, // relative to baseline of 0 // top most row having non-black
        height: (botBound - topBound) + 1,
        width: w// EDIT: comma has been added to fix old broken code.
    };
}

relativeBot, relativeTopและheightเป็นสิ่งที่มีประโยชน์ในวัตถุส่งคืน

นี่คือตัวอย่างการใช้งาน:

<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
<script>
function measureHeight(aFont, aSize, aChars, aOptions={}) {
	// if you do pass aOptions.ctx, keep in mind that the ctx properties will be changed and not set back. so you should have a devoted canvas for this
	// if you dont pass in a width to aOptions, it will return it to you in the return object
	// the returned width is Math.ceil'ed
	console.error('aChars: "' + aChars + '"');
	var defaultOptions = {
		width: undefined, // if you specify a width then i wont have to use measureText to get the width
		canAndCtx: undefined, // set it to object {can:,ctx:} // if not provided, i will make one
		range: 3
	};
	
	aOptions.range = aOptions.range || 3; // multiples the aSize by this much
	
	if (aChars === '') {
		// no characters, so obviously everything is 0
		return {
			relativeBot: 0,
			relativeTop: 0,
			height: 0,
			width: 0
		};
		// otherwise i will get IndexSizeError: Index or size is negative or greater than the allowed amount error somewhere below
	}
	
	// validateOptionsObj(aOptions, defaultOptions); // not needed because all defaults are undefined
	
	var can;
	var ctx; 
	if (!aOptions.canAndCtx) {
		can = document.createElement('canvas');;
		can.mozOpaque = 'true'; // improved performanceo on firefox i guess
		ctx = can.getContext('2d');
		
		// can.style.position = 'absolute';
		// can.style.zIndex = 10000;
		// can.style.left = 0;
		// can.style.top = 0;
		// document.body.appendChild(can);
	} else {
		can = aOptions.canAndCtx.can;
		ctx = aOptions.canAndCtx.ctx;
	}
	
	var w = aOptions.width;
	if (!w) {
		ctx.textBaseline = 'alphabetic';
		ctx.textAlign = 'left';	
		ctx.font = aFont;
		w = ctx.measureText(aChars).width;
	}
	
	w = Math.ceil(w); // needed as i use w in the calc for the loop, it needs to be a whole number
	
	// must set width/height, as it wont paint outside of the bounds
	can.width = w;
	can.height = aSize * aOptions.range;
	
	ctx.font = aFont; // need to set the .font again, because after changing width/height it makes it forget for some reason
	ctx.textBaseline = 'alphabetic';
	ctx.textAlign = 'left';	
	
	ctx.fillStyle = 'white';
	
	console.log('w:', w);
	
	var avgOfRange = (aOptions.range + 1) / 2;
	var yBaseline = Math.ceil(aSize * avgOfRange);
	console.log('yBaseline:', yBaseline);
	
	ctx.fillText(aChars, 0, yBaseline);
	
	var yEnd = aSize * aOptions.range;
	
	var data = ctx.getImageData(0, 0, w, yEnd).data;
	// console.log('data:', data)
	
	var botBound = -1;
	var topBound = -1;
	
	// measureHeightY:
	for (y=0; y<=yEnd; y++) {
		for (var x = 0; x < w; x += 1) {
			var n = 4 * (w * y + x);
			var r = data[n];
			var g = data[n + 1];
			var b = data[n + 2];
			// var a = data[n + 3];
			
			if (r+g+b > 0) { // non black px found
				if (topBound == -1) { 
					topBound = y;
				}
				botBound = y; // break measureHeightY; // dont break measureHeightY ever, keep going, we till yEnd. so we get proper height for strings like "`." or ":" or "!"
				break;
			}
		}
	}
	
	return {
		relativeBot: botBound - yBaseline, // relative to baseline of 0 // bottom most row having non-black
		relativeTop: topBound - yBaseline, // relative to baseline of 0 // top most row having non-black
		height: (botBound - topBound) + 1,
		width: w
	};
}

</script>
</head>
<body style="background-color:steelblue;">
<input type="button" value="reuse can" onClick="alert(measureHeight('40px serif', 40, 'rg', {canAndCtx:{can:document.getElementById('can'), ctx:document.getElementById('can').getContext('2d')}}).height)">
<input type="button" value="dont reuse can" onClick="alert(measureHeight('40px serif', 40, 'rg').height)">
<canvas id="can"></canvas>
<h1>This is a Heading</h1>
<p>This is a paragraph.</p>
</body>
</html>

relativeBotและrelativeTopเป็นสิ่งที่คุณเห็นในภาพนี้ได้ที่นี่:

https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Drawing_text



2

ตลกที่ TextMetrics มีความกว้างเท่านั้นและไม่มีความสูง:

http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#textmetrics

คุณสามารถใช้ Span เป็นตัวอย่างได้หรือไม่?

http://mudcu.be/journal/2011/01/html5-typographic-metrics/#alignFix


1
เหตุผลที่มีเพียงความกว้างคือ: stackoverflow.com/a/12112978/1085483
Rui Marques

2

นี่คือสิ่งที่ฉันทำตามคำตอบอื่น ๆ ที่นี่:

function measureText(text, font) {
	const span = document.createElement('span');
	span.appendChild(document.createTextNode(text));
	Object.assign(span.style, {
		font: font,
		margin: '0',
		padding: '0',
		border: '0',
		whiteSpace: 'nowrap'
	});
	document.body.appendChild(span);
	const {width, height} = span.getBoundingClientRect();
	span.remove();
	return {width, height};
}

var font = "italic 100px Georgia";
var text = "abc this is a test";
console.log(measureText(text, font));


1

ก่อนอื่นคุณต้องกำหนดความสูงของขนาดตัวอักษรจากนั้นตามค่าของความสูงของตัวอักษรเพื่อกำหนดความสูงปัจจุบันของข้อความของคุณคือเท่าใดบรรทัดข้อความข้ามแน่นอนความสูงเดียวกันของ ฟอนต์จำเป็นต้องสะสมหากข้อความไม่เกินความสูงของกล่องข้อความที่ใหญ่ที่สุดการแสดงทั้งหมดมิฉะนั้นจะแสดงเฉพาะข้อความภายในกล่องข้อความเท่านั้น ค่าสูงจำเป็นต้องมีคำนิยามของคุณเอง ความสูงที่ตั้งไว้ล่วงหน้าที่มากขึ้นความสูงของข้อความที่จะต้องแสดงและดักจับ

หลังจากประมวลผลแล้ว (แก้ปัญหา)

ก่อนประมวลผลกระทบ (ยังไม่ได้แก้ไข)

  AutoWrappedText.auto_wrap = function(ctx, text, maxWidth, maxHeight) {
var words = text.split("");
var lines = [];
var currentLine = words[0];

var total_height = 0;
for (var i = 1; i < words.length; i++) {
    var word = words[i];
    var width = ctx.measureText(currentLine + word).width;
    if (width < maxWidth) {
        currentLine += word;
    } else {
        lines.push(currentLine);
        currentLine = word;
        // TODO dynamically get font size
        total_height += 25;

        if (total_height >= maxHeight) {
          break
        }
    }
}
if (total_height + 25 < maxHeight) {
  lines.push(currentLine);
} else {
  lines[lines.length - 1] += "…";
}
return lines;};

1

ฉันพบว่าJUST FOR ARIALวิธีที่ง่ายที่สุดเร็วที่สุดและแม่นยำที่สุดในการค้นหาความสูงของกล่องที่มีขอบเขตคือการใช้ความกว้างของตัวอักษรบางตัว หากคุณวางแผนที่จะใช้แบบอักษรบางตัวโดยไม่ให้ผู้ใช้เลือกแบบอื่นคุณสามารถทำวิจัยเล็กน้อยเพื่อค้นหาตัวอักษรที่ถูกต้องที่เหมาะกับตัวอักษรนั้น

<!DOCTYPE html>
<html>
<body>

<canvas id="myCanvas" width="700" height="200" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>

<script>
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.font = "100px Arial";
var txt = "Hello guys!"
var Hsup=ctx.measureText("H").width;
var Hbox=ctx.measureText("W").width;
var W=ctx.measureText(txt).width;
var W2=ctx.measureText(txt.substr(0, 9)).width;

ctx.fillText(txt, 10, 100);
ctx.rect(10,100, W, -Hsup);
ctx.rect(10,100+Hbox-Hsup, W2, -Hbox);
ctx.stroke();
</script>

<p><strong>Note:</strong> The canvas tag is not supported in Internet 
Explorer 8 and earlier versions.</p>

</body>
</html>


0

การตั้งค่าขนาดตัวอักษรอาจไม่สามารถใช้งานได้จริง

ctx.font = ''

จะใช้อันที่กำหนดโดย CSS เช่นเดียวกับแท็กแบบอักษรใด ๆ ที่ฝังตัว ถ้าคุณใช้ฟอนต์ CSS คุณไม่ทราบว่าความสูงนั้นมาจากวิธีการเขียนโปรแกรมโดยใช้เมธอด measureText ซึ่งสั้นมาก ในบันทึกอื่น ๆ แม้ว่า IE8 จะส่งคืนความกว้างและความสูง


0

ใช้งานได้ 1) สำหรับข้อความหลายบรรทัดเช่นกัน 2) และแม้กระทั่งใน IE9!

<div class="measureText" id="measureText">
</div>


.measureText {
  margin: 0;
  padding: 0;
  border: 0;
  font-family: Arial;
  position: fixed;
  visibility: hidden;
  height: auto;
  width: auto;
  white-space: pre-wrap;
  line-height: 100%;
}

function getTextFieldMeasure(fontSize, value) {
    const div = document.getElementById("measureText");

    // returns wrong result for multiline text with last line empty
    let arr = value.split('\n');
    if (arr[arr.length-1].length == 0) {
        value += '.';
    }

    div.innerText = value;
    div.style['font-size']= fontSize + "px";
    let rect = div.getBoundingClientRect();

    return {width: rect.width, height: rect.height};
};

0

ฉันรู้ว่านี่เป็นคำถามที่ตอบแล้ว แต่สำหรับการอ้างอิงในอนาคตฉันต้องการเพิ่มวิธีแก้ปัญหาสั้น ๆ ขั้นต่ำ JS-only (ไม่มี jQuery) ฉันเชื่อว่าผู้คนจะได้รับประโยชน์จาก:

var measureTextHeight = function(fontFamily, fontSize) 
{
    var text = document.createElement('span');
    text.style.fontFamily = fontFamily;
    text.style.fontSize = fontSize + "px";
    text.textContent = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ";
    document.body.appendChild(text);
    var result = text.getBoundingClientRect().height;
    document.body.removeChild(text);
    return result;
};

-3

ในสถานการณ์ปกติสิ่งต่อไปนี้ควรใช้งานได้:

var can = CanvasElement.getContext('2d');          //get context
var lineHeight = /[0-9]+(?=pt|px)/.exec(can.font); //get height from font variable

5
ผิดอย่างสมบูรณ์และเต็มที่ มีความแตกต่างอย่างมากระหว่างขนาดจุดและขนาดพิกเซล ขนาดพอยต์ส่งผลให้มีข้อความขนาดแตกต่างกันขึ้นอยู่กับ DPI ที่หน้ากำลังแสดงผลโดยที่ขนาดพิกเซลไม่ได้นำมาพิจารณา
Orvid King

2
คุณคุ้นเคยกับแนวคิดของพิกเซลหน้าจอหรือไม่? คุณอาจพบว่าให้ความกระจ่าง ไม่ว่า pt และ px จะบ่งบอกถึงความสูงที่แตกต่างกัน เป็นที่น่าสังเกตว่าแบบอักษรสามารถเติมน้อยกว่า "ความสูง" หรือมากกว่านั้นได้ ฉันไม่แน่ใจว่าพิกเซลผ้าใบมีการปรับขนาดหรือไม่ แต่ฉันถือว่าเป็นเช่นนั้น มันเป็นคำตอบที่ "ผิด" ง่ายและสามารถใช้ได้ในหลาย ๆ สถานการณ์
Lodewijk

-4

นี่มันบ้า ... ความสูงของข้อความคือขนาดตัวอักษร .. คุณยังไม่ได้อ่านเอกสารใช่ไหม?

context.font = "22px arial";

สิ่งนี้จะกำหนดความสูงเป็น 22px

เหตุผลเดียวที่มีคือ ..

context.measureText(string).width

เป็นเพราะที่ความกว้างของสตริงไม่สามารถกำหนดได้เว้นแต่จะรู้ว่าสตริงที่คุณต้องการความกว้าง แต่สำหรับทุกสายที่วาดด้วยตัวอักษร .. ความสูงจะเป็น 22px

หากคุณใช้การวัดอื่นนอกเหนือจาก px ความสูงจะยังคงเหมือนเดิม แต่ด้วยการวัดนั้นดังนั้นที่สำคัญที่สุดที่คุณต้องทำคือแปลงการวัด


ตัวอักษรบางตัวสามารถขยายเกินหรือต่ำกว่าขีด จำกัด ได้โปรดดูwhatwg.org/specs/web-apps/current-work/images/baselines.png
Octavia Togami

1
ใช้ถ้อยคำไม่ดี แต่ก็มักจะเป็นจริง
Lodewijk

อ่านคำตอบทั้งหมดเหล่านี้ แต่สำหรับแอพที่เรียบง่ายของฉันนี่เป็นคำตอบที่ถูกต้อง pt === px
rob

ไม่ถูกต้องสมบูรณ์ ลองวิธีการแก้ปัญหาบางอย่างที่นำเสนอด้วยแบบอักษรที่แตกต่างกันและคุณจะพบความแตกต่างขนาดใหญ่
Dale Anderson

-4

วิธีแก้ปัญหาโดยประมาณ:

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.font = "100px Arial";
var txt = "Hello guys!"
var wt = ctx.measureText(txt).width;
var height = wt / txt.length;

นี่จะเป็นผลลัพธ์ที่แม่นยำในแบบอักษรที่มีช่องว่าง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.