×

如何使用 React Textfit 创建响应式 React 组件

作者:Terry2021.10.12来源:Web前端之家浏览:6194评论:0
关键词:reactreact-textfit

500.jpg

React 中进行开发涉及定义可重用组件并将它们组装到应用程序的各个部分以实现所需的用户界面 (UI)。在本文中,我们将介绍该react-textfit库,它可以轻松创建响应式 React 组件,这些组件可在布局中出现的任何位置进行可预测的显示。

文本拟合问题

由于 React 组件是描述 UI 特定部分的 JavaScript 代码片段,因此它们实际上彼此独立。他们的视觉风格通常嵌入其中,作为其定义的一部分。鉴于它们可能用于不同的位置和布局,这可能非常有用。

但是在可重用组件中嵌入样式也有一些缺点。在响应性的情况下可以看到一个示例。假设你想要一行文本——比如一个标题——完全填满为它保留的高度和宽度的空间,但不要换行——所有这些都不需要为每种可能的情况编写自定义 CSS 规则。(您可能需要的示例包括商业标语、广告信息或嵌入在导航栏组件中的文本。)

a.jpg

CSS 和可移植性

在定义响应式 React 组件的样式时,您需要考虑可能包装它以相应地调整字体大小的每个可能的父组件的大小、布局或样式。可以想象,考虑到所有可能的容器大小并不是真正可行的——即使您可以使用 CSS 来做到这一点。你会追逐太多的视口场景,因为它不适合编写媒体查询。但是除了媒体查询之外,CSS 中并没有真正的方法来确保文本块始终适合一行。

创建可重用的 React 组件

幸运的是,一些 React 库可以轻松解决这个问题。它们允许您定义可重用的 React 组件,其中无论可重用组件放置在哪个容器中,文本的行为都符合预期。 到本文结束时,您将能够使用这些库来解决上述文本拟合问题,并使一个可重用的组件。所以,让我们来看看你应该知道的一切,以使你的文本自动适应 React 中的可用空间。

首先,我们将看看为什么面对这样的问题如此重要,为什么通用的解决方案可能还不够,尤其是在使用 React 时。然后,react-textfit将介绍 React 库并用于实现单行和多行文本的解决方案。

b.jpg

可重用组件中的文本拟合问题

我们来看看下面的演示,通过一个例子来解释文本拟合问题。

<div id="container"> </div>

import React, {
  useState,
  useEffect
} from "https://cdn.skypack.dev/react@17.0.1";
import * as ReactDOM from "https://cdn.skypack.dev/react-dom@17.0.1";

function ReactIFrame(props) {
  const { children, ...remainingProps } = props;

  const [contentRef, setContentRef] = useState(null);
  const mountNode = contentRef?.contentWindow?.document?.body;

  return (
    <iframe {...remainingProps} ref={setContentRef}>
      {mountNode && ReactDOM.createPortal(children, mountNode)}
    </iframe>
  );
}

function Headline(props) {
  const { headline } = props;

  return <h1 style={{ fontSize: "5.5vw" }}>{headline}</h1>;
}

function SingleLineDemo() {
  const [headline, setHeadline] = useState(
    "A Sample Headline Occupying Its Space"
  );

  const [iframeBody, setIframeBody] = useState(undefined);

  useEffect(() => {
    setIframeBody(
      <div
        style={{
          width: "100%",
          maxWidth: "100%",
          background: "#C0C0C0",
          resize: "both",
          overflow: "auto"
        }}
      >
        <Headline headline={headline} />
      </div>
    );
  }, [headline]);

  return (
    <>
      <h5>User's Screen</h5>
      <ReactIFrame
        style={{
          border: "1px solid red",
          width: "50%",
          resize: "both",
          overflow: "auto"
        }}
      >
        {iframeBody}
      </ReactIFrame>
      <div>
        <h5>Edit Headline Here</h5>
        <textarea
          style={{
            border: "1px solid",
            paddingTop: "20px",
            paddingBottom: "20px",
            width: "50%",
            minHeight: "50px",
            height: "50px"
          }}
          placeholder={headline}
          rows="5"
          value={headline}
          onChange={(e) => setHeadline(e.target.value)}
        />
      </div>
    </>
  );
}

ReactDOM.render(<SingleLineDemo />, document.getElementById("container"));

目标是使标题适合为其保留的空间,而不管用户屏幕的大小。在此示例中,视口单位用于定义font-size标题。因此,在调整iframe代表用户屏幕的红色边框时,标题将始终适合其父级<div>。所以这个方法当然可以让标题文字适应任何屏幕宽度。但是,Headline样式化组件不可重用。这是因为它的设计只考虑了这个特定的文本。通过添加到标题文本或调整 parent 的大小<div>,文本将不再适合一行。(您可以尝试更改演示中的文本。)我们真的希望可重用组件比这更具适应性。

如前所述,CSS 媒体查询提供了另一种解决方案,它允许您根据屏幕大小调整文本字体大小。将网页作为一个整体考虑时,这是理想的解决方案。但是用媒体查询来追逐无数可能的容器宽度是不切实际的。这将导致大量工作。另外,并且会使您的组件不那么便携。

react-textfit 作为响应式 React 文本的解决方案

那么让我们看看react-textfitReact 库如何让文本自动适应可用空间,真正使组件可重用。

<div id="container"> </div>

import React, {
    useState,
    useEffect
  } from "https://cdn.skypack.dev/react@17.0.1";
  import * as ReactDOM from "https://cdn.skypack.dev/react-dom@17.0.1";
  import { Textfit } from "https://cdn.skypack.dev/react-textfit@1.1.1";
 
  function ReactIFrame(props) {
    const { children, ...remainingProps } = props;
 
    const [contentRef, setContentRef] = useState(null);
    const mountNode = contentRef?.contentWindow?.document?.body;
 
    return (
      <iframe {...remainingProps} ref={setContentRef}>
        {mountNode && ReactDOM.createPortal(children, mountNode)}
      </iframe>
    );
  }
 
  function Headline(props) {
    const { headline } = props;
    const [resized, setResized] = useState(false);
     
    return (
      <Textfit
        mode={"single"}
        forceSingleModeWidth={true}
        // simulating a resizing event to make the page update
        onHover={() => setResized((state) => !state)}
        onMouseEnter={() => setResized((state) => !state)}
      >
        {headline}
      </Textfit>
    );
   
  }
 
  function SingleLineDemo() {
    const [headline, setHeadline] = useState(
      "A Sample Headline Occupying Its Space"
    );
 
    const [iframeBody, setIframeBody] = useState(undefined);
 
    useEffect(() => {
      setIframeBody(
        <div
          style={{
            width: "100%",
            maxWidth: "100%",
            background: "#C0C0C0",
            resize: "both",
            overflow: "auto"
          }}
        >
          <Headline headline={headline} />
        </div>
      );
    }, [headline]);
 
    return (
      <>
        <h5>User's Screen</h5>
        <p>Hover the headline to make it fit the space</p>
        <ReactIFrame
          style={{
            border: "1px solid red",
            width: "50%",
            resize: "both",
            overflow: "auto"
          }}
        >
          {iframeBody}
        </ReactIFrame>
        <div>
          <h5>Edit Headline Here</h5>
          <textarea
            style={{
              border: "1px solid",
              paddingTop: "20px",
              paddingBottom: "20px",
              width: "50%",
              minHeight: "50px",
              height: "50px"
            }}
            placeholder={headline}
            rows="5"
            value={headline}
            onChange={(e) => setHeadline(e.target.value)}
          />
        </div>
      </>
    );
  }
 
  ReactDOM.render(<SingleLineDemo />, document.getElementById("container"));

如您所见,上述问题都有。多亏了react-textfit,您现在可以更改标题或调整父级 的大小<div>,同时让您的标题紧贴可用空间。

如何Textfit工作

现在,让我们详细看看它是如何react-textfit工作的。

正如该项目的官方 GitHub 页面所述【https://github.com/malte-wessel/react-textfit】,react-textfit【https://www.npmjs.com/package/react-textfit】是一个用于在任何可重用组件中拟合标题和段落的库。它可以有效地找到合适的样式,并适用于任何 CSS 样式配置,例如paddingline-height等。

您可以通过启动以下命令将其添加到您的依赖项中:

npm install react-textfit --save

然后您将能够访问该Textfit组件以适应任何文本,如下所示:

import { Textfit } from 'react-textfit';

Textfit将被翻译成<div>HTML 元素,并允许您在任何可重用组件或 HTML 元素中放入单行和多行文本。

要使用它,您只需让它环绕文本,如下所示:

<Textfit>
  Sample text</Textfit>

或任何包含以下内容的 HTML 元素:

<Textfit>
  <span>Sample text</span></Textfit>

由于Textfit是 a <div>,您可以通过 Reactstyle道具将 CSS 规则传递给它,如下所示:

<Textfit
  style={{"width": "200px"}}>
  Sample text</Textfit>

或者通过将其分配给 CSS 类className,如下所示:

<Textfit
  className={"divWidth200"}>
  Sample text</Textfit>

Textfit 道具

Textfit还附带了一些道具,可用于根据需要适合您的文本。让我们来看看它们:

  • mode是一个可以假定两个值的字符串:singlemulti。它定义了组件用来适应文本的方法。该single模式应该用于标题,以及multi段落模式。默认值为multi

  • min是一个数字,表示文本允许达到的最小字体大小(以像素为单位)。默认值为1

  • max是一个数字,表示文本允许达到的最大字体大小(以像素为单位)。默认值为100

  • forceSingleModeWidth是一个布尔值,仅在single模式期间使用,以使Textfit组件完全忽略元素的高度。默认值为true

  • throttle是一个数字,表示以毫秒为单位的窗口调整大小节流阀。默认值为50

  • onReady 是一个在文本适合时调用的函数。

两个最重要的是minmax,它们允许您分别设置字体大小的下限和上限。然后是modeprop,它定义了Textfit组件的行为方式。这需要更详细的解释。所以,让我们看看这两种模式的运行情况。

如何在可重用组件中适应单行文本

单行文本由标题、标题和标签表示。它通常包含在<h1><h2><h3>,或更普遍<p><span>HTML元素。在处理单行文本时,拟合问题几乎是不可避免的。这是因为它的字体大小往往比段落中使用的字体大得多。当single模式被上述modeprop in激活时Textfit,将应用以下涉及强制和可选步骤的算法:

1. binary search to fit the element's width2. if forceSingleModeWidth=false and text overflows height
    2a. binary search to also fit the element's height

如此处所述,二分搜索算法用于检索正确的字体大小,以使Textfit组件中包含的文本适合其宽度。然后,如果forceSingleModeWidth设置为false,则使用相同的方法——但也要考虑元素的高度。

使 React 组件可重用:单行演示

现在,让我们single通过现场演示来看看实际的 Textfit模式:

<div id="container"> </div>

import { Textfit } from "https://cdn.skypack.dev/react-textfit@1.1.1";
import React, { useState } from "https://cdn.skypack.dev/react@17.0.1";
import * as ReactDOM from "https://cdn.skypack.dev/react-dom@17.0.1";

function SingleLineDemo() {
  const [text, setText] = useState("Sample Headline!");
  const [resized, setResized] = useState(false);

  return (
    <>
      <div>
        <h2>Single Line Text</h2>
        <Textfit
          mode={"single"}
          forceSingleModeWidth={false}
          // simulating a resizing event to make the page update
          onMouseLeave={() => setResized((state) => !state)}
          style={{
            border: "1px solid",
            paddingTop: "20px",
            paddingBottom: "20px",
            width: "100%",
            maxWidth: "100%",
            minHeight: "50px",
            height: "50px",
            resize: "both",
            overflow: "auto"
          }}
        >
          {text}
        </Textfit>
      </div>
      <div>
        <h2>Edit Text Here</h2>
        <textarea
          style={{
            border: "1px solid",
            paddingTop: "20px",
            paddingBottom: "20px",
            width: "100%",
            minHeight: "50px",
            height: "50px"
          }}
          placeholder={text}
          rows="5"
          value={text}
          onChange={(e) => setText(e.target.value)}
        />
      </div>
    </>
  );
}

ReactDOM.render(<SingleLineDemo />, document.getElementById("container"));

如您所见,通过使文本更长,其字体大小将相应地更新Textfit以使其与其大小匹配。在保持文本不变的同时调整文本框的大小时,应用完全相同的逻辑。这就是小屏幕会发生的情况。因此,Textfit代表了在任何 React 组件或 HTML 元素中制作标题和标题响应的完美解决方案。

如何在响应式 React 组件中适应多行文本

多行文本由段落、副标题和描述表示。它通常被包含在<p><em><div>HTML元素。多行文本的拟合问题在高度方面很常见。事实上,在处理较小的屏幕时,由于可用宽度减少,您的文本会变得更高。因此,这可能会使您的文本超出具有固定高度的卡片或部分。

multi模式在 中激活时Textfit,将应用以下涉及两个强制性步骤的算法:

1. binary search to fit the element's height2. if text overflows width
    2a. binary search to also fit the element's width

该二进制搜索算法来获取正确的字体大小,以使包含在文本Textfit组件适合它的高度。然后,使用相同的方法,但也要考虑元素的宽度。如您所见,与single模式中发生的情况相反,高度优先于宽度。这可以通过上述原因进行解释。

使 React 组件可重用:多行演示

现在,让我们multi通过现场演示来看看实际的 Textfit模式:

<div id="container"> </div>

import { Textfit } from "https://cdn.skypack.dev/react-textfit@1.1.1";
import React, { useState } from "https://cdn.skypack.dev/react@17.0.1";
import * as ReactDOM from "https://cdn.skypack.dev/react-dom@17.0.1";

function MultiLineDemo() {
  const [text, setText] = useState("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.");
  const [resized, setResized] = useState(false);

  return (
    <>
      <div>
        <h2>Multi Line Text</h2>
        <Textfit
          mode={"multi"}
          // simulating a resizing event to make the page update
          onMouseLeave={() => setResized((state) => !state)}
          style={{
            border: "1px solid",
            paddingTop: "20px",
            paddingBottom: "20px",
            width: "100%",
            maxWidth: "100%",
            maxHeight: "400px",
            height: "50px",
            resize: "both",
            overflow: "auto"
          }}
        >
          {text}
        </Textfit>
      </div>
      <div>
        <h2>Edit Text Here</h2>
        <textarea
          style={{
            border: "1px solid",
            paddingTop: "20px",
            paddingBottom: "20px",
            width: "100%",
            minHeight: "50px",
            height: "50px"
          }}
          placeholder={text}
          rows="5"
          value={text}
          onChange={(e) => setText(e.target.value)}
        />
      </div>
    </>
  );
}

ReactDOM.render(<MultiLineDemo />, document.getElementById("container"));

通过与实时演示交互并使多行文本更长,其字体大小将更新,以使文本适合 HTML 元素尺寸。Textfit在保持文本不变的同时调整组件大小时也会发生同样的事情。这就是小屏幕会发生的情况。因此,Textfit是在任何 HTML 元素或 React 组件中使段落和长描述响应的好解决方案。

结论

由于智能手机和平板电脑已成为用于访问 Web 的最广泛使用的设备,响应能力已成为一个不容忽视的问题。在本文中,我们研究了该领域中的一个特定问题。特别是,我们探索了一个特定的文本拟合问题,为什么解决这个问题如此重要,以及如何在 React 中做到这一点。

react-textfit库是一个有用的、开源的、有效的 React 库,它允许您轻松地使您的文本(单行和多行)适合任何 React 组件。我希望你发现解释和演示有用。谢谢阅读!如有任何问题、意见或建议,请随时与我联系。


您的支持是我们创作的动力!
温馨提示:本文作者系Terry ,经Web前端之家编辑修改或补充,转载请注明出处和本文链接:
https://jiangweishan.com/article/react20211012a1.html

网友评论文明上网理性发言 已有0人参与

发表评论: