libflexible source code reading

Keywords: Javascript Mobile Windows Android iOS

Preface

Recently, we need a mobile product. We need to catch up with the time limit. We are referring to the flexbox layout and hand-picked of Skycat.
After rem layout, libflexe was chosen. After finishing the project, I decided to take a look at libflexe with a little spare time.
How to dynamically set the font of the root element to change the size of other elements through rem

text

First let's look at what attributes flexe needs

;(function(win, lib) {
    var doc = win.document;
    var docEl = doc.documentElement;
    var metaEl = doc.querySelector('meta[name="viewport"]');
    var flexibleEl = doc.querySelector('meta[name="flexible"]');
    var dpr = 0;
    var scale = 0;
    var tid;
    var flexible = lib.flexible || (lib.flexible = {});

When the function runs, we need to inject windows objects, and then see if lib exists under Windows (that is, whether lib already exists).
After injection, we get doc, docEl, metaEl, flexeEl, in which metaEl
To determine whether we already have a default viewport, FleEl is to determine that we have manually set dpr to avoid it.
flexible library dynamic setting dpr

if (metaEl) {
     console.warn('It will be based on what already exists. meta Label to set zoom ratio');
     var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/);
     if (match) {
         scale = parseFloat(match[1]);
         dpr = parseInt(1 / scale);
     }
 }

If metaEl exists, it means that there is a label on the page like <meta name="viewport" content="initial-scale=1". At this time, we have made it clear that we need to zoom, no longer need the intervention of flexible, the zoom value scale is direct.
Using the default initial-scale, through our default zoom, our layout viewport will be ideal viewport/scale, if our initual-scale is 1
And if the ideal is 414 px, our layout viewport will be 414 PX

else if (flexibleEl) {
       var content = flexibleEl.getAttribute('content');
       if (content) {
           var initialDpr = content.match(/initial\-dpr=([\d\.]+)/);
           var maximumDpr = content.match(/maximum\-dpr=([\d\.]+)/);
           if (initialDpr) {
               dpr = parseFloat(initialDpr[1]);
               scale = parseFloat((1 / dpr).toFixed(2));
           }
           if (maximumDpr) {
               dpr = parseFloat(maximumDpr[1]);
               scale = parseFloat((1 / dpr).toFixed(2));
           }
       }
   }

If we don't set the initial viewport but have a default of <meta name="flexible" content="initial-dpr=2"/> such flexe itself
Then we will have a default dpr, where flexe will calculate our scaling by scale=1/dpr based on our default dpr, which will affect layout viewport.
Size

if (!dpr && !scale) {
       var isAndroid = win.navigator.appVersion.match(/android/gi);
       var isIPhone = win.navigator.appVersion.match(/iphone/gi);
       var devicePixelRatio = win.devicePixelRatio;
       if (isIPhone) {
           // Under iOS, for screens 2 and 3, use twice the solution, and for the rest, use twice the solution.
           if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {
               dpr = 3;
           } else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){
               dpr = 2;
           } else {
               dpr = 1;
           }
       } else {
           // Under other equipment, it still uses twice the plan.
           dpr = 1;
       }
       scale = 1 / dpr;
   }

Next, if we do not preset DPR by <meta name="flexible" content="initial-dpr=2"/>,
Nor does it preset zooming by <meta name="viewport" content="initial-scale=1">, when flexe begins to base itself on
The dpr of the device is used to dynamically calculate scaling. For non-Apple devices, flexible sets dpr to 1. For Apple devices, the non-retina screen below the iPhone 3 is dpr to 1.
The iPhone 4-6 is a retina screen, the dpr is 2, the iPhone 6Plus is a retina HD screen, and the dpr is 3, because flexible is a mobile-focused device.
Solution, so the dpr on the tablet (including the iPad) or desktop is 1

docEl.setAttribute('data-dpr', dpr);
 if (!metaEl) {
     metaEl = doc.createElement('meta');
     metaEl.setAttribute('name', 'viewport');
     metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
     if (docEl.firstElementChild) {
         docEl.firstElementChild.appendChild(metaEl);
     } else {
         var wrap = doc.createElement('div');
         wrap.appendChild(metaEl);
         doc.write(wrap.innerHTML);
     }
 }

After calculating the zoom, set the dpr value to the root element, so that we can do the following:

.selector {
    width: 2rem;
    border: 1px solid #ddd;
}
[data-dpr="1"] .selector {
    height: 32px;
    font-size: 14px;
}
[data-dpr="2"] .selector {
    height: 64px;
    font-size: 28px;
}
[data-dpr="3"] .selector {
    height: 96px;
    font-size: 42px;
}

Setting different font sizes for different dpr screens, fonts do not need rem layout because

We don't want text to get smaller on the Retina screen. In addition, we want to see more text on the big screen mobile phone, and most font files now come with dot matrix sizes, usually 16px and 24px, so we don't want to have fantastic sizes like 13px and 15px.

At the same time, when metaEl does not exist, flexible dynamically generates a <meta name="viewport" content="initial-scale=${scale}, maximum-scale=${scale}, minimum-scale=${scale}, user-scalable=no">
If there are elements under < HTML > (such as < head > etc.), insert the meta tag, if not, wrap the meta tag in a div, and then
Write to the document through document.write

function refreshRem(){
       var width = docEl.getBoundingClientRect().width;
       if (width / dpr > 540) {
           width = 540 * dpr;
       }
       var rem = width / 10;
       docEl.style.fontSize = rem + 'px';
       flexible.rem = win.rem = rem;
   }

All right, zoom and dpr are set up. Next we need to set font-size of the root element, so that we can use rem.
Adapt to different screens. First, we need to get the width of layout viewport through docEl.getBoundingClientRect().width.
Then the width of layout viewport is divided into 10 parts and 1 part is 1 rem. As for the 540px settings, it is designed to allow you to browse the page in the case of the ipad horizontal screen, not because the experience after stretching the fit is too bad. Of course, there's still a little bit of a problem.
Because then 10rem will not be the full screen of the ipad. Of course, this is a mobile solution, not a tablet or desktop solution.

win.addEventListener('resize', function() {
      clearTimeout(tid);
      tid = setTimeout(refreshRem, 300);
  }, false);
  win.addEventListener('pageshow', function(e) {
      if (e.persisted) {
          clearTimeout(tid);
          tid = setTimeout(refreshRem, 300);
      }
  }, false);

Then when the window changes or the page is reloaded from the cache, we all need to reset the size.

if (doc.readyState === 'complete') {
       doc.body.style.fontSize = 12 * dpr + 'px';
   } else {
       doc.addEventListener('DOMContentLoaded', function(e) {
           doc.body.style.fontSize = 12 * dpr + 'px';
       }, false);
   }

The font-size value of 12 * dpr is set on body. To reset the default font value in the page, otherwise the font-size element without font-size will inherit font-size from html and become very large.

flexible.dpr = win.dpr = dpr;
        flexible.refreshRem = refreshRem;
        flexible.rem2px = function ( d ) {
            var val = parseFloat ( d ) * this.rem;
            if ( typeof d === 'string' && d.match ( /rem$/ ) ) {
                val += 'px';
            }
            return val;
        }
        flexible.px2rem = function ( d ) {
            var val = parseFloat ( d ) / this.rem;
            if ( typeof d === 'string' && d.match ( /px$/ ) ) {
                val += 'rem';
            }
            return val;
        }

Finally, flexe provides two tool functions, px2rem and rem2px, which are not explained here.

summary

After looking at flexible, we can really feel the beauty of rem layout, through the way of setting zoom coefficient dynamically.
Making layout view port correspond to design drawing greatly facilitates reconstruction and avoids the problem of 1px.
Unfortunately, Android and the ipad did not perform well.

Posted by iShaun on Wed, 10 Jul 2019 17:37:38 -0700