CSS реализует многострочный текст «развернуть и свернуть».

CSS
CSS реализует многострочный текст «развернуть и свернуть».

Автор: Ян Вэньбинь

Исходное заявление: Эта статья подготовлена ​​членами команды интерфейса чтения YFE, пожалуйста, уважайте оригинальность, пожалуйста, свяжитесь с общедоступной учетной записью (id: yuewen_YFE) для авторизации и укажите автора, источник и ссылку.

Многострочный текст Развернуть Свернуть — очень распространенное взаимодействие, как показано на следующем рисунке.

展开3 (1).gifОсновные трудности в реализации такого типа компоновки и взаимодействия заключаются в следующем:

  • Кнопка «Развернуть и свернуть» в правом нижнем углу многострочного текста
  • Переключение между состояниями «развернуть» и «свернуть»
  • Когда текст не превышает заданного количества строк, кнопка "Развернуть свернуть" не отображается

Честно говоря, в одиночку смотреть на этот макет раньше было непросто, даже с JavaScript (нужно вычислять ширину текста, чтобы динамически перехватывать текст,vue-clampВот как это делается), не говоря уже о следующей логике взаимодействия и суждения, но после некоторых размышлений, на самом деле, чистый CSS также может быть отлично реализован, давайте шаг за шагом, чтобы увидеть, как этого добиться~

1. Кнопка "Развернуть свернуть" в правом нижнем углу

Многим студентам-дизайнерам нравится этот дизайн, они помещают кнопку в нижний правый угол, а текстсмешиваниеВместе, а не в одну линию, это может быть более визуально и эстетически приятно. Давайте сначала рассмотрим усечение многострочного текста, это относительно просто

1. Обрезка многострочного текста

Допустим есть такая html структура

<div class="text">
  浮动元素是如何定位的
正如我们前面提到的那样,当一个元素浮动之后,它会被移出正常的文档流,然后向左或者向右平移,一直平移直到碰到了所处的容器的边框,或者碰到另外一个浮动的元素。
</div>

Многострочный текст нельзя пропустить, с этим должен быть знаком каждый, в основном он используется дляline-clamp, ключевые стили следующие

.text {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

image.png

2. Эффект объемного звучания в правом нижнем углу

упомянулэффект переноса текста, о чем вообще можно думатьпоплавок поплавок, Да, не думайте, что флоатинг ушел в прошлое, а конкретные сценарии все еще очень полезны. Например, поставьте внизу кнопку, а затем установите плавающую

<div class="text">
  <button class="btn">展开</button>
  浮动元素是如何定位的
正如我们前面提到的那样,当一个元素浮动之后,它会被移出正常的文档流,然后向左或者向右平移,一直平移直到碰到了所处的容器的边框,或者碰到另外一个浮动的元素。
</div>
.btn {
  float: left;
  /*其他装饰样式*/
}

image.pngЕсли установлено правое плавание

.btn {
  float: right;
  /*其他装饰样式*/
}

image.pngСуществует ужеокружатьЭффект только в правом верхнем углу, как переместить кнопку в правый нижний угол? попробуй сначалаmargin

.btn {
  float: right;
  margin-top: 50px;
  /*其他装饰样式*/
}

image.png

Видно, что хотя кнопка находится в правом нижнем углу, текст не имеет пробела над кнопкой.

несмотря на то чтоmarginНе решает проблему, но плавающая кнопка по-прежнему влияет на весь текст, что, если плавающих элементов несколько? Здесь используются псевдоэлементы::beforeзаменять

.text::before{
  content: '';
  float: right;
  width: 10px;
  height: 50px;/*先随便设置一个高度*/
  background: red
}

image.png

Теперь, когда кнопка находится слева от псевдоэлемента, как мне переместить ее вниз? Очень просто, очистить поплавокclear: both;просто хорошо

.btn {
  float: right;
  clear: both;
  /*其他装饰样式*/
}

image.png

Видно, что текст теперь полностью обернут вокруг двух плавающих элементов справа, пока ширина псевдоэлемента с красным фоном установлена ​​на 0 (или ширина не установлена, по умолчанию 0) , достигается эффект заворачивания в правом нижнем углу.

.text::before{
  content: '';
  float: right;
  width: 0; /*设置为0,或者不设置宽度*/
  height: 50px;/*先随便设置一个高度*/
}

image.png

3. Динамическая высота

Хотя нижний правый и объемный вверху доделаны, высота фиксированная.Как выставить ее динамически? можно использовать здесьcalcДля расчета вычтите высоту кнопки из высоты всего контейнера, как показано ниже.

.text::before{
  content: '';
  float: right;
  width: 0;
  height: calc(100% - 24px);
}

image.png

Жалко, похоже, это не имеет никакого эффекта, открыл консоль, чтобы посмотреть, и обнаружил, чтоcalc(100% - 24px)Вычислить высоту как 0

image.png

Причину легко представить, т.Высота 100% отказВ сети есть много разборов подобного рода задач.Обычное решение - указать высоту для родителя, но здесь высота меняется динамически, и есть развернутое состояние. Высота еще более непредсказуема, поэтому установка высоты невозможна Выбрать.

Кроме того, на самом деле есть еще один способ, который заключается в использованиигибкий макет. Наверное, такгибкий макетВ подпунктах высота изменения может быть рассчитана в процентах Подробнее см. в информации на w3.orgcss-flexboxописание

If the flex item hasalign-self: stretch, redo layout for its contents, treating this used size as its definite cross size so that percentage-sized children can be resolved.

Поэтому необходимо дать.textоберните один слой, затем установитеdisplay: flex

<div class="wrap">
  <div class="text">
    <button class="btn">展开</button>
    浮动元素是如何定位的
  正如我们前面提到的那样,当一个元素浮动之后,它会被移出正常的文档流,然后向左或者向右平移,一直平移直到碰到了所处的容器的边框,或者碰到另外一个浮动的元素。
  </div>
</div>
.wrap{
  display: flex;
}

На практике display:grid и display:-webkit-box одинаково эффективны, принцип аналогичен

Таким образом, расчетная высота только сейчас вступает в силу, и изменяется количество строк текста, которое также находится в правом нижнем углу~

image.png

Кроме того, динамическая высота также может быть реализована с отрицательным запасом (производительность будет немного лучше, чем у calc).

.text::before{
  content: '';
  float: right;
  width: 0;
  /*height: calc(100% - 24px);*/
  height: 100%;
  margin-bottom: -24px;
}

На этом эффект обтекания правого нижнего угла в основном завершен, и многоточие тоже перед кнопкой развернуть.Полный код можно посмотретьЭффект переноса многострочного расширения в правом нижнем углу codepen

4. Совместимость с другими браузерами

Приведенная выше реализация является наиболее совершенным способом справиться с этим. Я изначально думал, что совместимость не большая проблема, ведь использовались только усечение текста и плавание.-webkit-line-clampХотя это является-webkit-префикс, ноfirefoxОн тоже поддерживает.Когда я открыл его, я был ошарашен.safariа такжеfirefoxВсе запутано!

image.png

Это немного неудобно, неужели все предыдущие усилия были напрасными? Эти два нельзя игнорировать, иначе это будет только демо и не может быть использовано в производственной среде.

Быстро откройте консоль, чтобы увидеть, в чем причина. После недолгих поисков оказалось, чтоdisplay: -webkit-box! После установки этого свойства исходный текст как бы становится целым блоком, а плавающий элемент не может производить эффект обтекания, после его удаления плавание нормальное.

image.png

Итак, возникает вопрос:Как добиться многострочного усечения без отображения: -webkit-box?

На самом деле вышеперечисленными усилиями достигнут эффект обтекания в правом нижнем углу.Если установить максимальную высоту, когда известно количество строк, будет ли выполняться еще и многострочное усечение? Для облегчения установки высоты можно добавитьвысота линии, если его нужно установить в 3 строки, то высота устанавливается вline-height * 3

.text {
  /*
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  */
  line-height: 1.5;
  max-height: 4.5em;
  overflow: hidden;
}

Чтобы облегчить лучший контроль над количеством строк, обычно используемое количество строк может быть разделено с помощью селектора атрибутов (обычно не слишком много), как показано ниже.

[line-clamp="1"] {
  max-height: 1.5em;
}
[line-clamp="2"] {
  max-height: 3em;
}
[line-clamp="3"] {
  max-height: 4.5em;
}
...
<!--3行-->
<div class="text" line-clamp="3">
...
</div>
<!--5行-->
<div class="text" line-clamp="5">
 ...
</div>

image.png

Видно, что это в принципе нормально, за исключением того, что многоточия нет, теперь добавьте многоточие, прямо перед кнопкой расширения, это можно реализовать с помощью псевдоэлементов

.btn::before{
  content: '...';
  position: absolute;
  left: -10px;
  color: #333;
  transform: translateX(-100%)
}

image.png

так,Safariа такжеFirefoxСовместимый макет в основном завершен, и полный код можно просмотретьЭффект переноса многострочного расширения в правом нижнем углу codepen (полная совместимость)

Два, "расширенные" и "свернутые" два состояния

Когда дело доходит до переключения состояния CSS, об этом может подумать каждый.input type="checkbox"Бар. Здесь нам также нужно использовать эту функцию, сначала добавьтеinput, затем поместите предыдущийbuttonзаменитьlabel, и черезforассоциация атрибутов

<div class="wrap">
  <input type="checkbox" id="exp">
  <div class="text">
    <label class="btn" for="exp">展开</label>
    浮动元素是如何定位的
  正如我们前面提到的那样,当一个元素浮动之后,它会被移出正常的文档流,然后向左或者向右平移,一直平移直到碰到了所处的容器的边框,或者碰到另外一个浮动的元素。
  </div>
</div>

Таким образом, при нажатииlabel, на самом деле щелкнулinputЭлемент, теперь давайте добавим два состояния, которые должны отображать только 3 строки и не ограничивать количество строк

.exp:checked+.text{
  -webkit-line-clamp: 999; /*设置一个足够大的行数就可以了*/
}

Совместимые версии могут напрямую устанавливать максимальную высотуmax-heightявляется большим значением или напрямую устанавливается наnone

.exp:checked+.text{
  max-height: none;
}

展开2.gif

Здесь тоже есть небольшая проблема.Кнопка "Развернуть" после нажатия должна становиться "Свернуть".Как это изменить?

Есть хитрость: вы можете использовать псевдоклассы всякий раз, когда вам нужно динамически изменять содержимое.contentТехнология генерации, конкретный метод заключается в удалении или скрытии текста в кнопке и использовании псевдоэлементов для генерации

<label class="btn" for="exp"></label><!--去除按钮文字-->
.btn::after{
  content:'展开' /*采用content生成*/
}

Добавить к:checkedусловие

.exp:checked+.text .btn::after{
  content:'收起'
}

Совместимые версии Из-за предыдущего пропущенного номера он не может быть автоматически скрыт, поэтому необходимо выполнить дополнительную обработку.

.exp:checked+.text .btn::before {
    visibility: hidden; /*在展开状态下隐藏省略号*/
}

展开3.gif

Эффект в принципе такой же, как и в начале этой статьи, полный код можно посмотретьcodepen, многострочное расширение, сворачивающееся взаимодействие, совместимую версию можно посмотретьCodepen многострочное развертывание и свертывание взаимодействия (полная совместимость)

Еще одна вещь, если даноmax-heightУстановите соответствующее значение, обратите внимание, чтоявляется подходящим значением, конкретный принцип может относиться кCSS-трюки: динамическая анимация перехода высоты, а также добавить анимацию перехода

.text{
  transition: .3s max-height;
}
.exp:checked+.text{
  max-height: 200px; /*超出最大行高度就可以了*/
}

Kapture 2021-05-11 at 14.21.55.gif

В-третьих, оценка количества строк текста

Вышеупомянутое взаимодействие в основном удовлетворило требованиям, но проблемы все равно будут. НапримерКогда текст маленький, усечения в это время нет, то есть многоточия нет, но кнопка "Развернуть" все равно в правом нижнем углу, как ее скрыть?

image.png

Обычно решение js простое, сравните элементscrollHeightа такжеclientHeightВы можете, а затем добавить соответствующее имя класса. Ниже псевдокод

if (el.scrollHeight > el.clientHeight) {
  // 文本超出了
  el.classList.add('trunk')
} 

Итак, как CSS реализует такого рода суждения?

Безусловно, в CSS нет такого логического суждения, и большинству из нас нужно использовать его с других точек зрения.«Завязка»реализовать. Например, в этом сценарии, когда усечение не происходит, это означает, что текст полностью виден.конец текстаДобавьте элемент (маленький красный квадрат), чтобы не влиять на исходный макет, здесь задается абсолютное позиционирование

.text::after {
    content: '';
    width: 10px;
    height: 10px;
    position: absolute;
    background: red;
}

展开4.gifКак видите, за красным квадратом здесь полностью следует многоточие. Когда появится многоточие, маленький красный квадрат должен исчезнуть,Потому что он был сжатздесь поставь родителейoverflow: hiddenТо, что вы можете видеть, временно скрывает принцип

展开5.gif

Затем вы можете установить маленький красный квадрат прямо сейчас надостаточно большой размер,Например100% * 100%

.text::after {
    content: '';
    width: 100%;
    height: 100%;
    position: absolute;
    background: red;
}

9202761e575d8627136547a61fc60edc.gif

Как видите, красные блоки закрывают нижний правый угол, а теперь меняют фон на белый (тот же цвет фона, что и у родителя), родительoverflow: hiddenдобавить обратно

.text::after {
    content: '';
    width: 100%;
    height: 100%;
    position: absolute;
    background: #fff;
}

展开7.gifТеперь взгляните на эффект нажатия, чтобы развернуть

展开8.gif

Теперь, после его расширения, обнаруживается, что кнопка отсутствует (сейчас покрыта псевдоэлементом и не может быть нажата), что, если вы хотите, чтобы она все еще была видна после нажатия? добавить это:checkedсостояние, скрывать оверлей при раскрытии

.exp:checked+.text::after{
    visibility: hidden;
}

Таким образом реализована функция скрытия кнопки расширения, когда текста меньше

展开9.gif

Окончательный полный код можно посмотретьcodepen многострочное расширение свернуть автоматически скрыть, совместимую версию можно посмотретьcodepen многострочное развертывание и свертывание автоматически скрывают (полная совместимость)

Следует отметить, что совместимые версии могут поддерживатьсяIE 10+(Это уж слишком, он по-прежнему поддерживает IE), но поскольку IE не поддерживает codepen, тестовый IE можно скопировать и протестировать локально.

cc602bca-3538-466e-8dce-ee630ddd2635.gif

4. Резюме и объяснение

В общем, основное внимание уделяетсямакет, взаимодействие на самом деле относительно простое, общая стоимость реализации на самом деле очень низкая, и нет относительно необычных атрибутов, за исключением макета.-webkit-boxКажется, немного глючит (в конце концов-Webkit-kernelFirefox просто позаимствовал у него, неизбежны некоторые проблемы), к счастью, можно добиться эффекта обрезания многострочного текста, совместимость неплохая, в принципе полностью совместима (IE10+), вот ключевые моменты реализации

  • Эффекты переноса текста рассматриваются в первую очередьпоплавок поплавок
  • гибкий макетДочерние элементы могут вычислять высоту в процентах
  • Многострочное усечение текста также можно использовать в сочетании с эффектами переноса текста.max-heightРеализация моделирования
  • Переключение состояний может быть выполнено с помощьюcheckbox
  • CSS для изменения текста можно использоватьпсевдоэлементгенерировать
  • Больше использования окклюзии CSS«Завязывание глаз»

Можно сказать, что эффект расширения и свертывания многострочного текста является первым в отрасли.Большая проблемаЕсть много js-решений, но ни одно из них не кажется идеальным. Я надеюсь, что эта новая идея решения CSS может вдохновить вас по-разному. Спасибо за чтение, добро пожаловать в избранное и вперед~