zoukankan      html  css  js  c++  java
  • libflexible源码阅读

    前言

    最近需要一款移动端的产品,当时需要赶工期,在参考了天猫的flexbox布局和手淘的
    rem布局方案后,决定选用libflexible。做完项目之后,稍有空闲时间,决定看看libflexible
    如何实现动态设置根元素的字体,从而通过rem的方式改变其他元素大小

    正文

    首先我们看一下flexible需要哪些属性

    ;(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 = {});

    在函数运行时,我们需要将windows对象注入,然后在看看windows下是否已经存在lib(即是否已经有
    使用过flexible)了,注入完之后,我们依次获得doc,docEl,metaEl,flexibleEl,其中metaEl
    是为了判断我们是否已经有预设好的viewport,flexibleEl则是判断我们已经手动设置好dpr来避免
    flexible库动态设置dpr

    if (metaEl) {
         console.warn('将根据已有的meta标签来设置缩放比例');
         var match = metaEl.getAttribute('content').match(/initial-scale=([d.]+)/);
         if (match) {
             scale = parseFloat(match[1]);
             dpr = parseInt(1 / scale);
         }
     }

    如果metaEl存在的话,意味着页面上存在形如<meta name="viewport" content="initial-scale=1">的标签,此时我们已经明确了我们需要的缩放,不再需要flexible的介入,缩放值scale直接
    使用预设的initial-scale,通过我们预设的的缩放,我们的layout viewport将会是 ideal viewport/scale,如果我们的initual-scale为1的话
    且ideal为414px的话,我们的layout viewport将会是414px

    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));
               }
           }
       }

    如果我们没有设置初始的viewport但是有<meta name="flexible" content="initial-dpr=2" />这样的flexible自身的预设
    那么我们将会有预设的dpr,此时flexible将根据我们预设的dpr通过scale=1/dpr的方式来计算出我们的缩放,进而影响layout viewport
    的大小

    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) {
               // iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案
               if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {
                   dpr = 3;
               } else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){
                   dpr = 2;
               } else {
                   dpr = 1;
               }
           } else {
               // 其他设备下,仍旧使用1倍的方案
               dpr = 1;
           }
           scale = 1 / dpr;
       }

    接下来,如果我们既没有通过<meta name="flexible" content="initial-dpr=2" />这种方式预设dpr,
    也没有通过<meta name="viewport" content="initial-scale=1">的方式预设缩放,此时flexible开始根据
    设备的dpr来动态计算缩放。对于非苹果设备,flexible设置dpr为1,对于苹果设备,iPhone3以下非retina屏,dpr为1
    iPhone4-iPhone6为retina屏,dpr为2,iPhone6Plus为retina HD屏,dpr为3,由于flexible是一个专注于移动端
    的解决方案,所以平板(包括iPad)或者桌面端的dpr都为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);
         }
     }

    计算完缩放后,将获取的dpr值设置到根元素上,这样我们就可以通过以下方式:

    .selector {
         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;
    }

    为不同dpr的屏幕设置不同的字体大小,字体之所以不用rem布局,是因为

    我们不希望文本在Retina屏幕下变小,另外,我们希望在大屏手机上看到更多文本,以及,现在绝大多数的字体文件都自带一些点阵尺寸,通常是16px和24px,所以我们不希望出现13px和15px这样的奇葩尺寸。

    同时当不存在metaEl时,flexible动态生成一条<meta name="viewport" content="initial-scale=${scale},maximum-scale=${scale},minimum-scale=${scale},user-scalable=no">
    的标签,如果<html>下存在元素(如<head>等元素)那么,将meta标签插入,如果没有,就将meta标签用一个div包装,然后
    通过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;
       }

    好了,缩放和dpr都设置好了,下一步我们要设置根元素的font-size了,这样我们可以通过rem的方式
    适配不同屏幕。首先我们需要通过docEl.getBoundingClientRect().width获得layout viewport的宽度,
    然后将layout viewport宽度分为10份,1份为1rem。至于设置540px,是为了让在ipad横屏这种情况下浏览页面,不至于因为拉伸适配后体验太差。当然这还是有一点点问题的
    因为这样10rem将不会是ipad的满屏了。当然这是移动端解决方案,并没有考虑平板和桌面端

    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);

    接着当窗口发生变化或者页面重新从缓存中载入时,我们都要重新设置尺寸

    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);
       }

    body上设置12 * dpr的font-size值,为了重置页面中的字体默认值,不然没有设置font-size的元素会继承html上的font-size,变得很大。

    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;
            }

    最后,flexible提供了两个工具函数px2rem和rem2px,这里就不在阐述了

    总结

    看完flexible,实实在在的感受到rem布局的妙处,通过动态设置缩放系数的方式,
    让layout viewport与设计图对应,极大地方便了重构,同时也避免了1px的问题,
    然而略有遗憾的就是在安卓和ipad上的表现不佳了。

  • 相关阅读:
    STL 里出现 warning C4018: “<”: 有符号/无符号不匹配
    (程序员面试题精选(02))-设计包含min函数的栈
    C++的内存分配问题
    C++ const解析(转)
    关于QQ一些功能的实现(转)
    C++中堆和栈的完全解析(转)
    关于Windows的TortoiseSVN 不能Check out google 代码的问题
    WPF笔记(2.6 ViewBox)——Layout
    我也设计模式——0.前言
    WPF笔记(2.8 常用的布局属性)——Layout
  • 原文地址:https://www.cnblogs.com/2019gdiceboy/p/12695246.html
Copyright © 2011-2022 走看看