Flexible.js可伸缩布局实现方法详解


Posted in Javascript onNovember 13, 2020

阿里团队开源的一个库。flexible.js,主要是实现在各种不同的移动端界面实现一稿搞定所有的设备兼容自适应问题。

实现方法:

通过JS来调整html的字体大小,而在页面中的制作稿则统一使用rem这个单位来制作。关键代码如下:

;(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 = {});
   
  if (metaEl) {
    console.warn('将根据已有的meta标签来设置缩放比例');
    var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/);
    if (match) {
      scale = parseFloat(match[1]);
      dpr = parseInt(1 / scale);
    }
  } 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 (!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;
  }
 
  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);
    }
  }
 
  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;
  }
 
  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);
  }
   
 
  refreshRem();
 
  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;
  }
 
})(window, window['lib'] || (window['lib'] = {}));

从上面的代码,主要是改变了dpx和document的font-size大小。大小为docEl.getBoundingClientRect().width / 10 + 'px';

假设我们的设计稿宽是640的,则html的字体大小则被设为64px.则相当于1rem=64px.(1rem相对于根节点的字体大小)。

假如一个元素的宽是160px,在平时,我们可以采用百分比可以做到自适应,假如使用响应式的话,可能需要设置多个,比如在320px,输出80px,而在640px输出160px等。而采用以上的方法,则只需要输出2.5rem就能实现统一,如下表格:

设备宽度 320px 360px 414px 640px
Html字体大小 32px 36px 41.4px 64px
实际输出 1rem 1rem 1rem 1rem
设计稿缩放大小 80px 90px 103.5px 160px
实际输出 2.5rem 2.5rem 2.5rem 2.5rem

以上的2.5rem是怎么得出的呢?

160/64(1rem的基数为64px)=2.5rem;按照官方的说法(750px举例)

Flexible会将视觉稿分成100份(主要为了以后能更好的兼容vh和vw),而每一份被称为一个单位a。同时1rem单位被认定为10a。针对我们这份视觉稿可以计算出:

1a = 7.5px
1rem = 75px

那么我们这个示例的稿子就分成了10a,也就是整个宽度为10rem,<html>对应的font-size为75px:

这样一来,对于视觉稿上的元素尺寸换算,只需要原始的px值除以rem基准值即可。例如此例视觉稿中的图片,其尺寸是176px * 176px,转换成为2.346667rem * 2.346667rem。

另外在使用这个来处理自适应的另一个坑就是css sprite,作者的建议是使用svg,或者icon font.或者base64等其他方案。

另外就是在dpr=2时,小图片可能会出现模糊,建议以最大的图片来切图。

字体建议使用px

在作者的观点中,是建议描述性的字体使用px,如果有slogan之类大于48px的,可以使用rem,由于使用rem在iPhone5和iPhone6中字体不同,可能出现13px和15px,点阵字体。

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

如此一来,就决定了在制作H5的页面中,rem并不适合用到段落文本上。所以在Flexible整个适配方案中,考虑文本还是使用px作为单位。只不过使用[data-dpr]属性来区分不同dpr下的文本字号大小。

div {
  width: 1rem; 
  height: 0.4rem;
  font-size: 12px; // 默认写上dpr为1的fontSize
}
[data-dpr="2"] div {
  font-size: 24px;
}
[data-dpr="3"] div {
  font-size: 36px;
}

为了能更好的利于开发,在实际开发中,我们可以定制一个font-dpr()这样的Sass混合宏:

@mixin font-dpr($font-size){
  font-size: $font-size;

  [data-dpr="2"] & {
    font-size: $font-size * 2;
  }

  [data-dpr="3"] & {
    font-size: $font-size * 3;
  }
}

有了这样的混合宏之后,在开发中可以直接这样使用:

@include font-dpr(16px);

当然这只是针对于描述性的文本,比如说段落文本。但有的时候文本的字号也需要分场景的,比如在项目中有一个slogan,业务方希望这个slogan能根据不同的终端适配。针对这样的场景,完全可以使用rem给slogan做计量单位。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JS重要知识点小结
Nov 06 Javascript
JavaScript中的console.log()函数详细介绍
Dec 29 Javascript
Javascript中的高阶函数介绍
Mar 15 Javascript
JS实现网页标题栏显示当前时间和日期的完整代码
Nov 02 Javascript
jQuery实现查找最近父节点的方法
Jun 23 Javascript
Bootstrap学习系列之使用 Bootstrap Typeahead 组件实现百度下拉效果
Jul 07 Javascript
JavaScript判断日期时间差的实例代码
Mar 01 Javascript
在Vue中使用Compass的方法
Mar 02 Javascript
如何为vue的项目添加单元测试
Dec 19 Javascript
vue实现倒计时获取验证码效果
Apr 17 Javascript
vue Element左侧无限级菜单实现
Jun 10 Javascript
vscode 调试 node.js的方法步骤
Sep 15 Javascript
vue切换菜单取消未完成接口请求的案例
Nov 13 #Javascript
在vue中嵌入外部网站的实现
Nov 13 #Javascript
如何基于viewport vm适配移动端页面
Nov 13 #Javascript
VueCli生产环境打包部署跨域失败的解决
Nov 13 #Javascript
Vue使用Proxy代理后仍无法生效的解决
Nov 13 #Javascript
vue iview 隐藏Table组件里的某一列操作
Nov 13 #Javascript
详解Java中String JSONObject JSONArray List转换
Nov 13 #Javascript
You might like
PHP中用hash实现的数组
2011/07/17 PHP
PHP清除字符串中所有无用标签的方法
2014/12/01 PHP
Laravel 5框架学习之Blade 简介
2015/04/08 PHP
PHP单链表的实现代码
2016/07/05 PHP
thinkphp关于简单的权限判定方法
2017/04/03 PHP
php递归函数怎么用才有效
2018/02/24 PHP
豆瓣网的jquery代码实例
2008/06/15 Javascript
基于jQuery制作迷你背词汇工具
2010/07/27 Javascript
jQuery表单验证插件formValidator(改进版)
2012/02/03 Javascript
JSON语法五大要素图文介绍
2012/12/04 Javascript
js iframe跨域访问(同主域/非同主域)分别深入介绍
2013/01/24 Javascript
jQuery实现点击小图片淡入淡出显示大图片特效
2015/09/09 Javascript
Bootstrap每天必学之栅格系统(布局)
2015/11/25 Javascript
微信小程序 图片上传实例详解
2017/05/05 Javascript
基于JS脚本语言的基础语法详解
2017/07/22 Javascript
webpack2.0配置postcss-loader的方法
2017/08/17 Javascript
vue element-ui之怎么封装一个自己的组件的详解
2019/05/20 Javascript
Node.js API详解之 console模块用法详解
2020/05/12 Javascript
Python对象体系深入分析
2014/10/28 Python
Python3搜索及替换文件中文本的方法
2015/05/22 Python
Python基于动态规划算法解决01背包问题实例
2017/12/06 Python
pytorch + visdom CNN处理自建图片数据集的方法
2018/06/04 Python
influx+grafana自定义python采集数据和一些坑的总结
2018/09/17 Python
PyQt打开保存对话框的方法和使用详解
2019/02/27 Python
python求加权平均值的实例(附纯python写法)
2019/08/22 Python
使用python爬取抖音app视频的实例代码
2020/12/01 Python
CSS3 毛玻璃效果
2019/08/14 HTML / CSS
美国五金商店:Ace Hardware
2018/03/27 全球购物
美国沙龙美发产品购物网站:Hair.com by L’Oreal
2020/11/09 全球购物
大三自我鉴定范文
2013/10/05 职场文书
工程业务员工作职责
2013/12/07 职场文书
英语自荐信范文
2013/12/11 职场文书
财务部出纳岗位职责
2013/12/22 职场文书
机关门卫制度
2014/02/01 职场文书
React中的Context应用场景分析
2021/06/11 Javascript
SpringBoot工程下使用OpenFeign的坑及解决
2021/07/02 Java/Android