基于动态字符数量的纯CSS字体大小响应式实现

385

我知道这个问题可以很容易地使用JavaScript解决,但我只对纯CSS解决方案感兴趣。

我想找到一种方法来动态调整文本大小,以便它始终适合固定的div大小。这是示例标记:

<div style="width: 200px; height: 1em; overflow: hidden;">
  <p>Some sample dynamic amount of text here</p>
</div>

我在思考,也许可以通过以em为单位指定容器的宽度,并使字体大小继承该值来实现这一点?


1
哇,等等。在em中指定width的事情是相反的。它是取决于font-sizewidth。@JosephSilber 这正是我所想的。 - Ana
1
我对这个问题很好奇。为什么要使用纯CSS解决方案,而不是编写一个简单的JavaScript函数呢? - Austin Mullins
41
动机很简单,因为问题存在,一个纯CSS的解决方案将是惊人的。想象一下应用几个样式并且知道你的动态内容永远不会破坏设计的可能性。 - DMTintner
4
如果有人偶然发现这个问题并不介意使用JS,那么这里有一个插件可以做到:http://fittextjs.com/。 - DMTintner
2
fitText有其限制。例如,我只想在小屏幕上运行它,在500像素宽度之外,我不希望我的标题再放大。这需要编写更多的JavaScript代码。当您使用JavaScript进行布局时,关注点分离很快就会崩溃。它从来不是一个简单的一行代码。 - Costa Michailidis
哎呀,这么多复杂的答案。只需使用CSS rem - ToolmakerSteve
16个回答

593

注意:此解决方案基于视口大小而非内容量而变化。

我刚发现可以使用VW单位实现这一点。它们是与设置视口宽度相关联的单位。虽然存在一些缺点,比如不支持旧浏览器,但这绝对是值得认真考虑的东西。此外,您仍然可以为旧浏览器提供后备方案,如下所示:

p {
    font-size: 30px;
    font-size: 3.5vw;
}

http://css-tricks.com/viewport-sized-typography/https://medium.com/design-ux/66bddb327bb1


48
专业提示:您可以通过执行font-size:calc(100% + 2vw);或类似操作来防止文本变得太小,并获得一些控制权。这有点像“最小字体大小”。浏览器对于calc的支持与对于vw的支持类似。 - Prinzhorn
152
已点赞,但我现在想知道这是否真正回答了原问题。我的理解是,您的文本长度是动态的,您希望更改字体大小以始终适应您的div(200px)的宽度。这如何解决该问题? - Floremin
30
我同意@Floremin的观点。这将根据视口宽度/高度缩放所有字体大小,而不是基于文本容器的宽度/高度。 - MickJuice
31
@Floremin是正确的,这并没有解答问题。这将字体大小计算为容器宽度的函数,而不是与容器宽度相关的字符串长度的函数。 - henry
8
答案涉及不同问题。使用视口单位将使字体大小根据视口大小而变化,而不是根据文本字符数量。 - Pasha Skender
显示剩余11条评论

127

编辑:注意attr(),它与css中的calc()相关。你可能会在未来实现它。

很遗憾,目前没有纯CSS的解决方案。这是我建议你做的:为你的元素添加title属性,并使用文本溢出省略号来防止设计断裂,并让用户知道还有更多的文本。

<div style="width: 200px; height: 1em; text-overflow: ellipsis;" title="Some sample dynamic amount of text here">
 Some sample dynamic amount of text here
</div>

.

.

.

或者,如果你只想根据视口缩小尺寸,CSS3 支持相对于视口的新尺寸。

body {
   font-size: 3.2vw;
}
  1. 3.2vw = 视口宽度的 3.2%
  2. 3.2vh = 视口高度的 3.2%
  3. 3.2vmin = 3.2vw 和 3.2vh 中的较小值
  4. 3.2vmax = 3.2vw 和 3.2vh 中的较大值 请查看 css-tricks.com/....,同时也可以参考 caniuse.com/....

24
问题是关于相对于容器进行缩放,而不是相对于整个视口进行缩放。 - Arnaud Weil
13
好的,我会尽力以通俗易懂的方式翻译以下内容:...问题是关于根据字符数量进行缩放的。 - Bravo

57

18

唯一的方法可能是为不同的屏幕尺寸设置不同的宽度,但这种方法非常不准确,您应该使用js解决方案。

h1 {
    font-size: 20px;
}

@media all and (max-device-width: 720px){
    h1 {
        font-size: 18px;
    }
}

@media all and (max-device-width: 640px){
    h1 {
        font-size: 16px;
    }
}

@media all and (max-device-width: 320px){
    h1 {
        font-size: 12px;
    }
}

10

供参考,一个非CSS解决方案:

以下是一些JS代码,根据文本是否溢出容器来调整字体大小。

回顾来看,这个逻辑的一个有用的下一步应该是根据溢出的量来计算新的大小,以实现“适应可用空间的文本”。

Codepen 使用稍作修改的代码,但与下面的想法相同:

function checkTextOverflow(element) {
    var container = document.getElementById(element);

    // Reset font-size to 100% to begin
    container.style.fontSize = "100%";

    // Check if the text is wider than its container,
    // if so then reduce font-size
    if (container.scrollWidth > container.clientWidth) {

        // TODO: Add logic to adjust the value according to overflow difference.
        container.style.fontSize = "70%";
    }
}

对我来说,当用户在下拉菜单中进行选择时,我会调用这个函数,然后我的菜单中的一个div会被填充(这是我动态文本出现的地方)。
    checkTextOverflow("my_container_div");

此外,我还使用CSS省略号("...")来截断更长的文本,就像这样:
#my_container_div {
    width: 200px; /* width required for text-overflow to work */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

所以,最终:
- 短文本:例如 "APPLES" 完全渲染,漂亮的大字体。
- 长文本:例如 "APPLES & ORANGES" 通过上述JS缩放函数,缩小70%。
- 超长文本:例如 "APPLES & ORANGES & BANAN..." 通过上述JS缩放函数和CSS规则,缩小70%并用"..."省略号截断。
你还可以尝试使用CSS字间距来使文本变窄,同时保持相同的字体大小。

由于用户要求纯CSS解决方案,因此被投票否决。 - AnotherLongUsername
谢谢。这是唯一真正有效的解决方案。你有办法让缩放百分比自动化,而不是固定在70%吗? - undefined
如果我要这样做的话,我可能会先看看溢出的数量,然后使用该值来计算最终的百分比。基本上是为了实现真正的“适应可用空间的文本”。如果你想要更进一步,还可以考虑字母间距。但请记住,所有这些可能会导致不太吸引人的“科学怪人美学”。 - undefined

8
calc(42px + (60 - 42) * (100vw - 768px) / (1440 - 768));

使用这个公式。

对于比1440和768更大或更小的任何尺寸,你可以给它一个静态值,或者采用相同的方法。

vw方案的缺点是你无法设置比例尺,例如在1440屏幕分辨率下5vw可能最终成为60px的字体大小,你想要的理想字体大小,但当你将窗口宽度缩小到768时,它可能会变成12px,而不是你想要的最小值。通过这种方法,你可以设置上限和下限,字体将在两者之间自动调整。


2
这很聪明、简单,而且酷毙了。使用 clamp() 可以使最小和最大字体大小紧凑表达,易于阅读。 - Isabelle Wedin
应该在答案中添加clamp - Creepin

4
许多人在@DMTinter的帖子评论中提到,OP问的是字符数量的变化。他还问了CSS,但正如@Alexander所指出的,“仅使用CSS是不可能的”。据我所知,目前似乎是真的,因此人们也想知道下一个最好的方法。
我对此并不特别自豪,但它确实有效。看起来需要大量代码才能完成它。这是核心内容:
function fitText(el){
  var text = el.text();
  var fsize = parseInt(el.css('font-size'));
  var measured = measureText(text, fsize);

  if (measured.width > el.width()){
    console.log('reducing');
    while(true){
      fsize = parseInt(el.css('font-size'));
      var m = measureText(text, fsize );
      if(m.width > el.width()){
        el.css('font-size', --fsize + 'px');
      }
      else{
        break;
      }
    }
  }
  else if (measured.width < el.width()){
    console.log('increasing');
    while(true){
      fsize = parseInt(el.css('font-size'));
      var m = measureText(text, fsize);
      if(m.width < el.width()-4){ // not sure why -4 is needed (often)
        el.css('font-size', ++fsize + 'px');
      }
      else{
        break;
      }
    }
  }
}

这里有一个JS Bin: http://jsbin.com/pidavon/edit?html,css,js,console,output
请提出可能的改进意见(我不太想使用画布来测量文本...似乎开销太大(?))。感谢@Pete提供了measureText函数:https://dev59.com/EHVD5IYBdhLWcg3wAWsO#4032497

这太棒了,我正在使用这个修改过的版本来为一个需要夹紧不同文本长度的React项目服务。虽然我想知道允许多行需要多少工作量。 - Harrison Howard
@HarrisonHoward 谢谢,似乎允许多行会增加复杂性,但我还没有仔细考虑过。如果您在这方面做了任何事,请在此添加您的作品链接,因为那可能非常有用。谢谢。 - jbobbins
我曾尝试过实现这个功能,但由于我的尝试是在高度已经测量过后进行的,所以均未成功。我期望measureText能够以某种方式实现这一点?不确定,现在我只是使用line-clamp和最小字号。 - Harrison Howard
我对measureText和scaleFontSize进行了一些改进,希望能减少其他框架/库中可能遇到的一些开销和挑战。scaleFontSize现在使用数学计算来确定下一个字体大小,而不是循环,measureText将使用当前元素进行测量,而不是生成一个新元素。 https://jsfiddle.net/harrisonhowardax/jLrsn40u/13/ - Harrison Howard

3

Bootstrap 5 现在默认启用。 - Hashim Aziz

2

好的,你的动态文本一定来自某个地方。在我的情况下,看起来是这样的:

<div class="large" :data-contentlength="Math.floor(item.name.length/7)">[[ item.name ]]</div>

以及我的CSS类:

.large[data-contentlength="1"]{ font-size: 1.2em; }
.large[data-contentlength="2"]{ font-size: 1.1em; }
.large[data-contentlength="3"]{ font-size: 1.0em; }
.large[data-contentlength="4"]{ font-size: 0.9em; }
.large[data-contentlength="5"]{ font-size: 0.8em; }
.large[data-contentlength="6"]{ font-size: 0.7em; }
.large[data-contentlength="7"]{ font-size: 0.6em; }

我还有针对“非大型”文本的类:

[data-length="1"]{ font-size: 1.00em; }
...

编辑: 当所有浏览器都支持attr()时,这变得更容易了: https://developer.mozilla.org/en-US/docs/Web/CSS/attr#browser_compatibility

此外,如果css可以将2个单位值(例如px和ch)相除,则可以使其更具动态性,但现在必须手动完成。

请参见此处:

https://jsfiddle.net/qns0pov2/3/

创建一个1ch的立方体,并查看它在目标单位中的大小(在fiddle中为px),计算每行字符数,并使用该值获取每个内容长度的完美字体大小。

fiddle还显示了该方法的问题:平均字符宽度小于1ch(基于0),但是有像M这样的字符比较大(约为70%)。

因此,如果您希望确保字符适合空间,请将fiddle调整为:--ch-width: calc(8.109 * 1.7);

如果您更感兴趣的是平均情况:--ch-width: calc(8.109 * 0.92);


1
这是一个被低估的解决方案!在我看来,它回答了这个问题,虽然它不是100%动态的,但可以创建几个漂亮的步长和字体大小变化。 - DavidG
该文章明确指出是纯CSS,Math.floor(item.name.length)不是。 - mloureiro
这是一个例子,你可以手动写入内容长度,就像我在链接的fiddle中所做的那样。 - Soraphis

1

我从BootStrap的某个地方得到了这个动态字体大小的calc()函数,并进行了一些调整。基于4pt系统和remhttps://www.finsweet.com/client-first/docs/sizes,适用于Webflow项目:

html {font-size: 16px;}

@import url('https://fonts.googleapis.com/css2?family=Poppins&display=swap');
body {font-family: 'Poppins', sans-serif;}

/*---SETUP BASE SIZE ---*/
html {font-size: 16px;}

/*---LINE-HEIGHTS + MARGINS---*/
[class^="display"], h1, h2, h3, h4 {
    margin-top: 0;
    margin-bottom: 1rem;
    font-weight: 600;
}

.display-1, .display-2, .display-3, .display-4 {
    line-height: 1.2;
    
}

h1, h2, h3, h4 {
    line-height: 1.4;
}

p, ul, ol {
    margin-bottom: 0.7rem;
    line-height: 1.45;
}

.lead {
    margin-bottom: 1rem;
    line-height: 1.4;
}

/*---FONT SIZES 1279px DOWN---*/
@media (max-width: 1279px) {
    .display-1 {
        font-size: calc(1.625rem + 4.5vw);
    }

    .display-2 {
        font-size: calc(1.575rem + 3.9vw);
    }

    .display-3 {
        font-size: calc(1.525rem + 3.3vw);
    }

    .display-4 {
        font-size: calc(1.475rem + 2.7vw);
    }
  
    /*---HEADINGS---*/
    h1 {
        font-size: calc(1.375rem + 1.5vw);
    }

    h2 {
        font-size: calc(1.325rem + 0.9vw);
    }

    h3 {
        font-size: calc(1.3rem + 0.6vw);
    }

    h4 {
        font-size: calc(1.275rem + 0.3vw);
    }

    /*---PARAGRAPHS/UL/OL---*/
    p, ul, ol  {
        font-size: calc(0.823rem + 0.3vw);
    }

    .lead {
        font-size: calc(1.01rem + 0.3vw);
    }
}

/*---FONT SIZES ABOVE 1279px---*/
@media screen and (min-width: 1280px) {
    .display-1 {
        font-size: 5.22rem;
    }

    .display-2 {
        font-size: 4.7rem;
    }

    .display-3 {
        font-size: 4.16rem;
    }

    .display-4 {
        font-size: 3.63rem;
    }
    /*---HEADINGS---*/
    h1 {
        font-size: 2.58rem;
    }

    h2 {
        font-size: 2.05rem;
    }

    h3 {
        font-size: 1.78rem;
    }

    h4 {
        font-size: 1.52rem;
    }

    p, ul, ol {
        font-size: 1.0625rem;
    }

    .lead {
        font-size: 1.25rem;
    }
}
<section>
    <div class="container">
        <p style="color:#8C8C8C;"><i>Note: Resize window too see text grow/shrink in browser window <= 1279px</i></p>
        <br>
        <h1 class="display-1">Display 1</h1>
        <h1 class="display-2">Display 2</h1>
        <h1 class="display-3">Display 3</h1>
        <h1 class="display-4">Display 4</h1>
        <br>
        <br>
        <br>
        <br>
        <h1>h1. The quick brown fox jumps over the lazy dog</h1>
        <h2>h2. The quick brown fox jumps over the lazy dog</h2>
        <h3>h3. The quick brown fox jumps over the lazy dog</h3>
        <h4>h4. The quick brown fox jumps over the lazy dog</h4>
        <p>The earliest known appearance of the phrase was in The Boston Journal. In an article titled "Current Notes" in the February 9, 1885, edition, the phrase is mentioned as a good practice sentence for writing students: "A favorite copy set by writing teachers for their pupils is the following, because it contains every letter of the alphabet: 'A quick brown fox jumps over the lazy dog.'"[2] Dozens of other newspapers published the phrase over the next few months, all using the version of the sentence starting with "A" rather than "The"</p>
        <p>The earliest known use of the phrase starting with "The" is from the 1888 book Illustrative Shorthand by Linda Bronson.[4] The modern form (starting with "The") became more common even though it is slightly longer than the original (starting with "A").</p>
        <p>A 1908 edition of the Los Angeles Herald Sunday Magazine records that when the New York Herald was equipping an office with typewriters "a few years ago", staff found that the common practice sentence of "now is the time for all good men to come to the aid of the party" did not familiarize typists with the entire alphabet, and ran onto two lines in a newspaper column. They write that a staff member named Arthur F. Curtis invented the "quick brown fox" pangram to address this.</p>
        <br>
        <br>
        <br>
        <br>
        <p class="lead">Lead paragraph: As the use of typewriters grew in the late 19th century.</p>
        <p>The phrase began appearing in typing lesson books as a practice sentence. Early examples include How to Become Expert in Typewriting: A Complete Instructor Designed Especially for the Remington Typewriter (1890),[6] and Typewriting Instructor and Stenographer's Hand-book (1892). By the turn of the 20th century, the phrase had become widely known. In the January 10, 1903, issue of Pitman's Phonetic Journal, it is referred to as "the well known memorized typing line embracing all the letters of the alphabet".</p>
        <p>Robert Baden-Powell's book Scouting for Boys (1908) uses the phrase as a practice sentence for signaling.</p>
        <p>The first message sent on the Moscow–Washington hotline on August 30, 1963, was the test phrase "THE QUICK BROWN FOX JUMPED OVER THE LAZY DOG'S BACK 1234567890".</p>
        <br>
        <br>
        <br>
        <br>
        <ul class="list-unordered">
            <li>During the 20th century, technicians tested typewriters and teleprinters by typing the sentence.</li>
            <li>During the 20th century, technicians tested typewriters and teleprinters by typing the sentence.</li>
            <li>During the 20th century, technicians tested typewriters and teleprinters by typing the sentence.</li>
            <li>During the 20th century, technicians tested typewriters and teleprinters by typing the sentence.</li>
            <li>During the 20th century, technicians tested typewriters and teleprinters by typing the sentence.</li>
            <li>During the 20th century, technicians tested typewriters and teleprinters by typing the sentence.</li>
            <li>During the 20th century, technicians tested typewriters and teleprinters by typing the sentence.</li>
        </ul>
        <br>
        <br>
        <br>
        <br>
        <ol class="list-ordered">
            <li>During the 20th century, technicians tested typewriters and teleprinters by typing the sentence.</li>
            <li>During the 20th century, technicians tested typewriters and teleprinters by typing the sentence.</li>
            <li>During the 20th century, technicians tested typewriters and teleprinters by typing the sentence.</li>
            <li>During the 20th century, technicians tested typewriters and teleprinters by typing the sentence.</li>
            <li>During the 20th century, technicians tested typewriters and teleprinters by typing the sentence.</li>
            <li>During the 20th century, technicians tested typewriters and teleprinters by typing the sentence.</li>
            <li>During the 20th century, technicians tested typewriters and teleprinters by typing the sentence.</li>
        </ol>
        <br>
        <br>
        <br>
        <br>
    </div>
</section>

欢迎使用


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接