css font-size和line-height与基线不匹配 [英] css font-size and line-height not matching the baseline

查看:603
本文介绍了css font-size和line-height与基线不匹配的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图做一些应该很简单的事情,但是我在失败和论坛之间度过了一天。



我想调整我的字体以匹配我的基准。 在设计上它只需一次点击,但在CSS中它看起来像地球上最困难的事情。



让我们举一个具有理性值的简单例子。





在这张图片上,我每20px就有一个基线。



因此,对于我的< body> 我来说:

 <风格> 
body {font-size:16px; line-height:20px;}
< / style>

一切正常。我的段落与基线相匹配。



但是当我脚本编写不符合我的< h> 基线了..我做错了什么?这应该遵循我的基准,不是吗?

 < style type =text / css> 
body {font-size:16px; line-height:20px;}
h1 {font-size:5em; line-height:1.25em;}
h2 {font-size:4em; line-height:1.25em;}
h3 {font-size:3em; line-height:1.25em;}
h4 {font-size:2em; line-height:1.25em;}
< / style>

ps:20/16 = 1.25em

在我的督察中,计算返回期望值

  h1 {font-size:84px; line-height:100px;} 
h2 {font-size:68px; line-height:80px;}
h3 {font-size:52px; line-height:60px;}
h4 {font-size:36px; line-height:40px;}

所以应该显示像这样的不是?

解决方案

这有点复杂 - 您必须先测量字体(就像InDesign一样),并计算出line-height,你称之为bottom_gap和其他东西的东西


确定我们可以在JavaScript中做些什么..


你是对的 - 但对于印刷术JS是用来计算CSS(取决于font metrics)

在这里演示了第一步(测量字体)
https://codepen.io/sebilasse/pen/gPBQqm
它只是以图形方式显示[技术背景]测量的结果。
$ b

这个测量是需要的,因为每一个字体在行中表现完全不同。

这是一个可以产生这样一个Typo CSS的生成器

https://codepen.io/sebilasse/pen/BdaPzN



可以基于度量函数on < canvas> ,看起来像这样:

  function getMetrics fontName,fontSize){
//注意:如果没有getComputedStyle,这个库将不起作用。
if(!document.defaultView.getComputedStyle){
throw(ERROR:'document.defaultView.getComputedStyle'not found。该库仅适用于可以报告计算出的CSS值的浏览器。
}
if(!document.querySelector('canvas')){
var _canvas = document.createElement('canvas');
_canvas.width = 220; _canvas.height = 220;
document.body.appendChild(_canvas);
}
//将旧文本度量函数存储在Canvas2D原型上
CanvasRenderingContext2D.prototype.measureTextWidth = CanvasRenderingContext2D.prototype.measureText;
/ **
*获取计算出的CSS值的快捷方式函数
* /
var getCSSValue = function(element,property){
return document.defaultView.getComputedStyle元件,NULL).getPropertyValue(属性);
};
/ **
*新文字度量函数
* /
CanvasRenderingContext2D.prototype.measureText = function(textstring){
var metrics = this.measureTextWidth(textstring ),
fontFamily = getCSSValue(this.canvas,font-family),
fontSize = getCSSValue(this.canvas,font-size)。replace(px,)
isSpace =!(/ \S / .test(textstring));
metrics.fontsize = fontSize;

//对于文本引导值,我们测量一个多行文本容器。
var leadDiv = document.createElement(div);
leadDiv.style.position =absolute;
leadDiv.style.margin = 0;
leadDiv.style.padding = 0;
leadDiv.style.opacity = 0;
leadDiv.style.font = fontSize +px+ fontFamily;
leadDiv.innerHTML = textstring +< br /> + textstring;
document.body.appendChild(leadDiv);
//对文本的前导进行初始猜测(使用标准TeX比率)
metrics.leading = 1.2 * fontSize;
//尝试从浏览器获取真实值
var leadDivHeight = getCSSValue(leadDiv,height);
leadDivHeight = leadDivHeight.replace(px,);
if(leadDivHeight> = fontSize * 2){metrics.leading =(leadDivHeight / 2)| 0; }
document.body.removeChild(leadDiv);
//如果我们不处理空白空间,我们可以计算指标
if(!isSpace){
//有字符,所以测量文本
var canvas =使用document.createElement( 画布);
var padding = 100;
canvas.width = metrics.width + padding;
canvas.height = 3 * fontSize;
canvas.style.opacity = 1;
canvas.style.fontFamily = fontFamily;
canvas.style.fontSize = fontSize;
var ctx = canvas.getContext(2d);
ctx.font = fontSize +px+ fontFamily;

var w = canvas.width,
h = canvas.height,
baseline = h / 2;

//将所有canvas的pixeldata值设置为255,所有内容
//数据为0.这让我们扫描data [i]!= 255.
ctx .fillStyle =white;
ctx.fillRect(-1,-1,w + 2,h + 2);
ctx.fillStyle =black;
ctx.fillText(textstring,padding / 2,baseline);
var pixelData = ctx.getImageData(0,0,w,h).data;

//画布像素数据是w * 4乘以h * 4,因为R,G,B和A是分开的,
//数组中的连续值,而不是存储为32位整数。
var i = 0,
w4 = w * 4,
len = pixelData.length;

//寻找上升点使用正常的正向扫描线
while(++ i< len&& pixelData [i] === 255){}
var ascent =(i / w4)| 0;

//寻找下降点使用反向扫描线
i = len - 1; (--i> 0&& pixelData [i] === 255){}
var descent =(i / w4)| 0;

//找到最小x坐标
(i = 0; i< len&& pixelData [i] === 255;){
i + = W4;
if(i> = len){i =(i-len)+ 4; }}
var minx =((i%w4)/ 4)| 0;

//找到最大x坐标
var step = 1;
for(i = len-3; i> = 0& pixelData [i] === 255;){
i - = w4; (i <0){i =(len-3) - (step ++)* 4; }}
var maxx =((i%w4)/ 4)+ 1 | 0;

//设置字体指标
metrics.ascent =(baseline - ascent);
metrics.descent =(下降 - 基线);
metrics.bounds = {minx:minx - (padding / 2),
maxx:maxx - (padding / 2),
miny:0,
maxy:下降上升};
metrics.height = 1+(下降 - 上升);
} else {
//只有空格,所以我们不能测量文本
metrics.ascent = 0;
metrics.descent = 0;
metrics.bounds = {minx:0,
maxx:metrics.width,//最佳猜想
miny:0,
maxy:0};
metrics.height = 0;
}
返回指标;
};

注意,您还需要一个很好的reset.css来重置浏览器边距和填充。

您可以点击显示CSS,也可以使用生成的CSS混合多种字体:

如果它们具有不同的基本尺寸,则将第二个标准化:

  var factor = CSS1baseSize / CSS2baseSize; 

现在重新计算CSS2中的每种字体

  var size = size * factor; 

查看 https://codepen.io/sebilasse/pen/oENGev?editors=1100



什么如果涉及到图像?
以下演示使用两个相同度量的字体加上一个额外的JS部分。需要为基线网格计算像图片这样的媒体元素:
https:// codepen。 io / sebilasse / pen / ddopBj

I'm trying to do something that should be very simple but I've spent my day between failures and forums..

I would like to adjust my font in order to match my baseline. On indesign it's one click but in css it looks like the most difficult thing on earth..

Lets take a simple example with rational values.

On this image I have a baseline every 20px.

So for my <body> I do:

<style>
body {font-size:16px; line-height:20px;}
</style> 

Everything works perfectly. My paragraph matchs the baseline.

But when I'm scripting my <h> that doesn't match the baseline anymore.. what am I doing wrong? That should follow my baseline, shouldn't it?

<style type="text/css">
    body{font-size: 16px; line-height: 20px;}
    h1{font-size: 5em; line-height: 1.25em;}
    h2{font-size: 4em; line-height: 1.25em;}
    h3{font-size: 3em; line-height: 1.25em;}
    h4{font-size: 2em; line-height: 1.25em;}
</style>

ps: 20/16=1.25em

In my inspector, computed returns the expected values

h1{font-size: 84px; line-height: 100px;}
h2{font-size: 68px; line-height: 80px;}
h3{font-size: 52px; line-height: 60px;}
h4{font-size: 36px; line-height: 40px;}

So that should display something like this no?

解决方案

It is a bit complicated - you have to measure the fonts first (as InDesign does) and calculate "line-height", the thing you called "bottom_gap" and some other stuff

I'm pretty sure we can do something in JavaScript..

You are right – but for Typography JS is used to calculate the CSS (depending on the font metrics)

Did demo the first step (measuring a font) here https://codepen.io/sebilasse/pen/gPBQqm It is just showing graphically what is measured [for the technical background]

This measuring is needed because every font behaves totally different in a "line".

Here is a generator which could generate such a Typo CSS:

https://codepen.io/sebilasse/pen/BdaPzN

A function to measure could be based on <canvas> and look like this :

function getMetrics(fontName, fontSize) {
  // NOTE: if there is no getComputedStyle, this library won't work.
  if(!document.defaultView.getComputedStyle) {
    throw("ERROR: 'document.defaultView.getComputedStyle' not found. This library only works in browsers that can report computed CSS values.");
  }
  if (!document.querySelector('canvas')) {
    var _canvas = document.createElement('canvas');
    _canvas.width = 220; _canvas.height = 220;
    document.body.appendChild(_canvas);
  }
  // Store the old text metrics function on the Canvas2D prototype
  CanvasRenderingContext2D.prototype.measureTextWidth = CanvasRenderingContext2D.prototype.measureText;
  /**
   *  Shortcut function for getting computed CSS values
   */
  var getCSSValue = function(element, property) {
    return document.defaultView.getComputedStyle(element,null).getPropertyValue(property);
  };
  /**
   * The new text metrics function
   */
  CanvasRenderingContext2D.prototype.measureText = function(textstring) {
    var metrics = this.measureTextWidth(textstring),
        fontFamily = getCSSValue(this.canvas,"font-family"),
        fontSize = getCSSValue(this.canvas,"font-size").replace("px",""),
        isSpace = !(/\S/.test(textstring));
        metrics.fontsize = fontSize;

    // For text lead values, we meaure a multiline text container.
    var leadDiv = document.createElement("div");
    leadDiv.style.position = "absolute";
    leadDiv.style.margin = 0;
    leadDiv.style.padding = 0;
    leadDiv.style.opacity = 0;
    leadDiv.style.font = fontSize + "px " + fontFamily;
    leadDiv.innerHTML = textstring + "<br/>" + textstring;
    document.body.appendChild(leadDiv);
    // Make some initial guess at the text leading (using the standard TeX ratio)
    metrics.leading = 1.2 * fontSize;
    // Try to get the real value from the browser
    var leadDivHeight = getCSSValue(leadDiv,"height");
    leadDivHeight = leadDivHeight.replace("px","");
    if (leadDivHeight >= fontSize * 2) { metrics.leading = (leadDivHeight/2) | 0; }
    document.body.removeChild(leadDiv);
    // if we're not dealing with white space, we can compute metrics
    if (!isSpace) {
        // Have characters, so measure the text
        var canvas = document.createElement("canvas");
        var padding = 100;
        canvas.width = metrics.width + padding;
        canvas.height = 3*fontSize;
        canvas.style.opacity = 1;
        canvas.style.fontFamily = fontFamily;
        canvas.style.fontSize = fontSize;
        var ctx = canvas.getContext("2d");
        ctx.font = fontSize + "px " + fontFamily;

        var w = canvas.width,
            h = canvas.height,
            baseline = h/2;

        // Set all canvas pixeldata values to 255, with all the content
        // data being 0. This lets us scan for data[i] != 255.
        ctx.fillStyle = "white";
        ctx.fillRect(-1, -1, w+2, h+2);
        ctx.fillStyle = "black";
        ctx.fillText(textstring, padding/2, baseline);
        var pixelData = ctx.getImageData(0, 0, w, h).data;

        // canvas pixel data is w*4 by h*4, because R, G, B and A are separate,
        // consecutive values in the array, rather than stored as 32 bit ints.
        var i = 0,
            w4 = w * 4,
            len = pixelData.length;

        // Finding the ascent uses a normal, forward scanline
        while (++i < len && pixelData[i] === 255) {}
        var ascent = (i/w4)|0;

        // Finding the descent uses a reverse scanline
        i = len - 1;
        while (--i > 0 && pixelData[i] === 255) {}
        var descent = (i/w4)|0;

        // find the min-x coordinate
        for(i = 0; i<len && pixelData[i] === 255; ) {
          i += w4;
          if(i>=len) { i = (i-len) + 4; }}
        var minx = ((i%w4)/4) | 0;

        // find the max-x coordinate
        var step = 1;
        for(i = len-3; i>=0 && pixelData[i] === 255; ) {
          i -= w4;
          if(i<0) { i = (len - 3) - (step++)*4; }}
        var maxx = ((i%w4)/4) + 1 | 0;

        // set font metrics
        metrics.ascent = (baseline - ascent);
        metrics.descent = (descent - baseline);
        metrics.bounds = { minx: minx - (padding/2),
                           maxx: maxx - (padding/2),
                           miny: 0,
                           maxy: descent-ascent };
        metrics.height = 1+(descent - ascent);
    } else {
        // Only whitespace, so we can't measure the text
        metrics.ascent = 0;
        metrics.descent = 0;
        metrics.bounds = { minx: 0,
                           maxx: metrics.width, // Best guess
                           miny: 0,
                           maxy: 0 };
        metrics.height = 0;
    }
    return metrics;
  };

Note that you also need a good "reset.css" to reset the browser margins and paddings.
You click "show CSS" and you can also use the generated CSS to mix multiple fonts:
If they have different base sizes, normalize the second:

var factor = CSS1baseSize / CSS2baseSize;

and now recalculate each font in CSS2 with

var size = size * factor;

See a demo in https://codepen.io/sebilasse/pen/oENGev?editors=1100

What if it comes to images? The following demo uses two fonts with the same metrics plus an extra JS part. It is needed to calculate media elements like images for the baseline grid : https://codepen.io/sebilasse/pen/ddopBj

这篇关于css font-size和line-height与基线不匹配的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆