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 相关文章推荐
jQuery 常见操作实现方式和常用函数方法总结
May 06 Javascript
input禁止键盘及中文输入,但可以点击
Feb 13 Javascript
javascript实现时间格式输出FormatDate函数
Jan 13 Javascript
JavaScript跨平台的开源框架NativeScript
Mar 24 Javascript
jquery无限级联下拉菜单简单实例演示
Nov 23 Javascript
js获取页面及个元素高度、宽度的代码
Apr 26 Javascript
BootStrap与validator 使用笔记(JAVA SpringMVC实现)
Sep 21 Javascript
javaScript中定义类或对象的五种方式总结
Dec 04 Javascript
webpack配置文件和常用配置项介绍
Apr 28 Javascript
JavaScript中的return布尔值的用法和原理解析
Aug 14 Javascript
详解从买域名到使用pm2部署node.js项目全过程
Mar 07 Javascript
vue3.0 自适应不同分辨率电脑的操作
Feb 06 Vue.js
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 处理TXT文件(打开/关闭/检查/读取)
2013/05/13 PHP
让你的PHP7更快之Hugepage用法分析
2016/05/31 PHP
javascript中onmouse事件在div中失效问题的解决方法
2012/01/09 Javascript
用jQuery与JSONP轻松解决跨域访问的问题
2014/02/04 Javascript
js中把JSON字符串转换成JSON对象最好的方法
2014/03/21 Javascript
jQuery中focus事件用法实例
2014/12/26 Javascript
jQuery满意度星级评价插件特效代码分享
2015/08/19 Javascript
js流动式效果显示当前系统时间
2016/05/16 Javascript
清除浏览器缓存的几种方法总结(必看)
2016/12/09 Javascript
require.js中的define函数详解
2017/07/10 Javascript
深入探究angular2 UI组件之primeNG用法
2017/07/26 Javascript
解决IE7中使用jQuery动态操作name问题
2017/08/28 jQuery
在Vue组件中获取全局的点击事件方法
2018/09/06 Javascript
node.js中module模块的功能理解与用法实例分析
2020/02/14 Javascript
详解Vue.js 响应接口
2020/07/04 Javascript
.netcore+vue 实现压缩文件下载功能
2020/09/24 Javascript
详解vue-cli项目在IE浏览器打开报错解决方法
2020/12/10 Vue.js
[01:44]Ti10举办地公布
2019/08/25 DOTA
python基于隐马尔可夫模型实现中文拼音输入
2016/04/01 Python
tensorflow 变长序列存储实例
2020/01/20 Python
使用OpenCV获取图片连通域数量,并用不同颜色标记函
2020/06/04 Python
Spartoo芬兰:欧洲最大的网上鞋店
2016/08/28 全球购物
在阿尔卑斯山或希腊度过快乐假期:Alpine Elements
2019/12/28 全球购物
世界上最大的皮肤科医生拥有和经营的美容网站:LovelySkin
2021/01/03 全球购物
专业毕业生个性的自我评价
2013/10/03 职场文书
2013年军训通讯稿
2014/02/05 职场文书
医院安全生产月活动总结
2014/07/05 职场文书
村安全生产责任书
2014/08/25 职场文书
课外科技活动总结
2014/08/27 职场文书
学生实习证明模板汇总
2014/09/25 职场文书
寒山寺导游词
2015/02/03 职场文书
2015年学雷锋活动总结
2015/02/06 职场文书
nginx 反向代理之 proxy_pass的实现
2021/03/31 Servers
如何判断pytorch是否支持GPU加速
2021/06/01 Python
JavaScript实例 ODO List分析
2022/01/22 Javascript
python实现会员管理系统
2022/03/18 Python