Как динамически изменять ширину заголовка, чтобы обеспечить равномерную длину строки - PullRequest
2 голосов
/ 23 января 2020

Я пытаюсь выяснить, как веб-сайт Washington Post заставляет заголовок заголовка так красиво разрываться независимо от того, какой текст или ширина экрана. Из того, что я могу определить, если h1 имеет класс headline, его ширина рассчитывается динамически для достижения эффекта. Я знаю, что это делается с помощью JavaScript, и представьте, что это не жестко запрограммировано, поэтому как эта ширина на самом деле вычисляется, чтобы каким-то образом знать, как заголовок будет ломаться (предотвращая сильно изменяющиеся длины строк, сироты и т. Д. c.). Я просто не могу отследить JS, который управляет этой функцией.

Вот пример обычной загрузки страницы:

enter image description here

И пример с удаленным классом headline:

enter image description here

(Вы также можете заметить разницу прямо при загрузке страницы, предположительно, просто до того, как в игру вступит JavaScript)

Спасибо!

1 Ответ

0 голосов
/ 23 января 2020

Это то, что вы хотите. Очевидно, что поскольку некоторые слова длиннее других, не все строки имеют идеально одинаковую длину. Приведенный ниже код сохраняет количество разрывов строк, созданных браузером. Затем он корректирует положение разрывов строк, чтобы обеспечить более или менее четных длин строк.

PS: попробуйте выполнить приведенный ниже код без JS, и вы увидите разница

let h1 = document.querySelector('h1')
let text = h1.textContent
let splitText = text.split(' ')
let getContentBoxHeight = elem => {
  let elemStyle = window.getComputedStyle(elem)
  let elemHeightWithPadding = parseInt(elemStyle.getPropertyValue('height'))
  let elemPadding = parseInt(elemStyle.getPropertyValue('padding-top')) + parseInt(elemStyle.getPropertyValue('padding-bottom'))
  return elemHeightWithPadding - elemPadding
}
let getContentBoxWidth = elem => {
  let elemStyle = window.getComputedStyle(elem)
  let elemWidthWithPadding = parseInt(elemStyle.getPropertyValue('width'))
  let elemPadding = parseInt(elemStyle.getPropertyValue('padding-left')) + parseInt(elemStyle.getPropertyValue('padding-right'))
  return elemWidthWithPadding - elemPadding
}

// return the number of line breaks created by the browser
let breakPointAmount = (() => {
  let body = document.querySelector('body')
  let dummyH1 = document.createElement('h1')
  let oneLineHeight
  let totalLineHeight = getContentBoxHeight(h1)

  dummyH1.appendChild(document.createTextNode('M'))
  body.appendChild(dummyH1)
  oneLineHeight = getContentBoxHeight(dummyH1)
  dummyH1.remove()
  return Math.round(totalLineHeight / oneLineHeight) - 1
})()

// refine the number of line breaks created by the browser
let breakPoints = (() => {
  let denominator = breakPointAmount + 1
  let points = []
  let h1Length
  h1.style.width = 'max-content'
  h1Length = getContentBoxWidth(h1)
  h1.style.width = ''
  for (let i = 0; i < breakPointAmount; i++) {
    points.push(Math.round(h1Length * (i + 1) / denominator))
  }
  return points
})()

// determine where that same number of break points should go in text
let indexesToBreak = Array(breakPointAmount).fill(1)
let cumulativeLength = 0
let cumulativeText = ''
for (let i = 0; i < splitText.length; i++) {
  let word = splitText[i]
  let calculateLength = word => {
    let body = document.querySelector('body')
    let dummyH1 = document.createElement('h1')
    let length
    dummyH1.appendChild(document.createTextNode(word))
    dummyH1.style.width = 'max-content'
    body.appendChild(dummyH1)
    length = getContentBoxWidth(dummyH1)
    dummyH1.remove()
    return length
  }

  cumulativeText += word + ' '
  cumulativeLength = calculateLength(cumulativeText)

  for (let j = 0; j < indexesToBreak.length; j++) {
    if (indexesToBreak[j] === 1) {
      indexesToBreak[j] = {
        index: i,
        currentMin: Math.abs(cumulativeLength - breakPoints[j])
      }
    } else {
      if (cumulativeLength - breakPoints[j] < indexesToBreak[j].currentMin) {
        indexesToBreak[j] = {
          index: i,
          currentMin: Math.abs(cumulativeLength - breakPoints[j])
        }
      }
    }
  }
}

// insert break points at updated locations into text
let newText = (function() {
  let final = ''
  let itbIndex = 0
  for (let i = 0; i < splitText.length; i++) {
    final += `${splitText[i]} `
    if (indexesToBreak[itbIndex] && i === indexesToBreak[itbIndex].index) {
      final += '<br>'
      itbIndex += 1
    }
  }
  return final.trim()
})()

// add text with new break points to page
h1.innerHTML = newText
h1 {
  text-align: center;
}
<h1>Some Long Title that Requires Line Breaking To Ensure Even Line Lengths</h1>
...