preface

The rise of mobile terminal gives us a bigger stage in the front end, but at the same time, it also brings us a series of headaches, mobile terminal adaptation is one of them, the most commonly used scheme on the market is REM adaptation.

Why is she a gnawing goblin? Because she is really love and hate, flexible adaptive layout with CSS unit conversion tools, let a person love; On the other hand, because of the variety of devices and performance on mobile devices, achieving perfect compatibility can be a headache.

Even so, still can’t stop the author for her obsession. This article will discuss the topic of REM adaptation, as well as share my own experience and a set of code I am currently working with. In addition, the increasing compatibility of mobile devices has resulted in other adaptations that are beyond the scope of this article.

Instance analysis

The global variable

const docEl = document.documentElement
const metaEl = document.querySelector('meta[name="viewport"]')

const maxWidth = window.__MAX_WIDTH__ || 750
const divPart = window.__DIV_PART__ || 15
const bodySize = window.__BODY_SIZE__ || 12

let scale = 1
let dpr = 1
let timer = null
Copy the code
  • MetaEl: Grab the existing viewport to support the user to customize the actual page zoom ratio, through setting the viewport can achieve the visual actual physical pixels. For example,Initial - scale = 0.5, i.e. double screen, assuming font size=100px at the root node, then 0.01rem is physical pixel 1px; whileInitial - scale = 1.0While in CSS units 0.01rem=1px, we know that in a double screen, 1px actually has 4 physical pixels.
  • MaxWidth: UI draft width, usually based on iphone6, that is 750.
  • DivPart: Divide the width of the device into several parts. In the above code, 750/15=50, meaning that the screen is 750 width, 1rem=50px.
  • BodySize: At initialization, set the body font size.
  • Scale and DPR are page scaling and device pixel ratio respectively.

Initialization Settings

if (metaEl) {
  console.warn('Set scaling based on existing META tags')

  const match = metaEl.getAttribute('content').match(/initial-scale=([\d.]+)/)

  if (match) {
    scale = parseFloat(match[1])
    dpr = parseInt(1 / scale)
  }
} else {
  if (window.navigator.appVersion.match(/iphone/gi)) {
    dpr = parseInt(window.devicePixelRatio) || 1
    scale = 1 / dpr
  }

  const newMetaEl = document.createElement('meta')
  newMetaEl.setAttribute('name'.'viewport')
  newMetaEl.setAttribute('content'.`width=device-width, initial-scale=${scale}, maximum-scale=${scale}, minimum-scale=${scale}, user-scalable=no`)
  docEl.firstElementChild.appendChild(newMetaEl)
}

// Set the root node DPR
docEl.setAttribute('data-dpr', dpr)
Copy the code

Here’s why you should distinguish between Android and IOS devices. Many people would say IOS has multiple screens. In fact, Android has multiples, so why don’t we consider that?

  • Some Android devices have strange pixel ratios, such as 2.5, 3.8 and other strange numbers.
  • Some Androids are weird, like pages that are slightly wider than the screen, horizontal scrollbars (for unspecified reasons, CSS has been ruled out), and expensive to integrate.

The core code

function bodyLoaded (cb) {
  if (document.body) {
    cb && cb()
  } else {
    document.addEventListener('DOMContentLoaded'.function () {
      cb && cb()
    }, false)}}// Refresh rem when the window width changes
function refreshRem () {
  let width = docEl.clientWidth

  if (width / dpr > maxWidth) {
    width = maxWidth * dpr
  }

  // Set the root node to font-size
  window.remUnit = width / divPart
  docEl.style.fontSize = window.remUnit + 'px'

  bodyLoaded((a)= > {
    // Test rem accuracy, zoom if not as expected
    let noEl = document.createElement('div')
    noEl.style.width = '1rem'
    noEl.style.height = '0'
    document.body.appendChild(noEl)

    let rate = noEl.clientWidth / window.remUnit

    if (Math.abs(rate - 1) > =0.01) {
      docEl.style.fontSize = (window.remUnit / rate) + 'px'
    }

    document.body.removeChild(noEl)
  })
}

/ / initialization
refreshRem()

bodyLoaded((a)= > {
  document.body.style.fontSize = bodySize * dpr + 'px'
  document.body.style.maxWidth = maxWidth * dpr + 'px'
})
Copy the code

RefreshRem function is the core of the whole REM adaptation, which will be called every time it needs to be updated. We also set the maximum width of the page, which can ensure a good visual effect even when opened on PC.

On some Android phones, however, 1rem does not equal the font size of the root node. For example: Font size=20px for HTML. Normally 1rem should be 20px, but on some models it can be 22px, 18px, etc. (I suspect this is also the case with page width overflow mentioned above). Therefore, the author added the code bodyLoaded, after the REM setting is completed, and then compared with the actual visual 1REM, if the deviation is more than 1%, it is considered that REM needs to be redefined, so that we can 100% guarantee that 1REM is the size we expect.

Page width monitor

window.addEventListener('resize'.function () {
  clearTimeout(timer)
  timer = setTimeout(refreshRem, 200)},false)

// window.addEventListener('pageshow', function (e) {
// if (e.persisted) {
// refreshRem()
/ /}
// }, false)
Copy the code

This code is used to listen for the resize event to recalculate the font size of the root node, and the timer is used to prevent frequent computations (in fact, in mobile phones, there is no chance of resize being triggered frequently, so the timer can be left alone). Some readers may ask why they don’t listen for onorientationchange. In fact, it is not necessary. In essence, the screen switch is also a kind of REsize.

So what does this code comment out mean? It is used to monitor the browser returns, but this code in iPhone8, there will be problems on iPhoneX, in return, we get the document. The documentElement. ClientWidth is its actual size (no pixel than on equipment), so that the whole page layout is wrong. After careful consideration, I decided to delete this code, because when I returned, I left it exactly as I left it, and there was no need to re-evaluate it.

Tool function

window.px2rem = function (d) {
  let val = parseFloat(d) / window.remUnit

  if (typeof d === 'string' && d.match(/px$/)) {
    val += 'rem'
  }

  return val
}

window.rem2px = function (d) {
  let val = parseFloat(d) * window.remUnit

  if (typeof d === 'string' && d.match(/rem$/)) {
    val += 'px'
  }

  return val
}
Copy the code

Expose global functions to facilitate the use of JS to control the size.

CSS Restyling

I won’t post the style code here for space, but you can read it here: reset.css

conclusion

This REM adaptation code is summed up in the daily development of the author extracted, can not be said to be 100% perfect, but also enough to adapt to the mainstream models on the market. Then with the construction tool, automatic conversion to REM units, save worry and effort.

Finally, a great global build tool, fle- CLI, is recommended to help you avoid the complexity of building configuration.

This article source address: github.com/ansenhuang/…