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实现)MapBar中坐标的加密和解密的脚本
May 16 Javascript
js中document.getElementByid、document.all和document.layers区分介绍
Dec 08 Javascript
JavaScript之Getters和Setters 平台支持等详细介绍
Dec 07 Javascript
JS打开层/关闭层/移动层动画效果的实例代码
May 11 Javascript
原生js实现淘宝首页点击按钮缓慢回到顶部效果
Apr 06 Javascript
JQuery弹出层示例可自定义
May 19 Javascript
小议JavaScript中Generator和Iterator的使用
Jul 29 Javascript
JS组件系列之Bootstrap table表格组件神器【二、父子表和行列调序】
May 10 Javascript
JS实现登录页面记住密码和enter键登录方法推荐
May 10 Javascript
学习掌握JavaScript中this的使用技巧
Aug 29 Javascript
JavaScript实现两个select下拉框选项左移右移
Mar 09 Javascript
浅谈Node.js爬虫之网页请求模块
Jan 11 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 addslashes和mysql_real_escape_string
2010/01/24 PHP
PHP 设置MySQL连接字符集的方法
2011/01/02 PHP
PHP 函数call_user_func和call_user_func_array用法详解
2014/03/02 PHP
php+ajax无刷新分页实例详解
2015/12/07 PHP
PHP针对伪静态的注入总结【附asp与Python相关代码】
2017/08/01 PHP
Laravel手动返回错误码示例
2019/10/22 PHP
javascript各浏览器中option元素的表现差异
2011/04/07 Javascript
jQuery fadeTo方法调整图片的透明度使用介绍
2013/05/06 Javascript
JavaScript图片放大技术(放大镜)实现代码分享
2013/11/14 Javascript
ExtJs纵坐标值重复问题的解决方法
2014/02/27 Javascript
详解Angular调试技巧之报错404(not found)
2018/01/31 Javascript
浅析node应用的timing-attack安全漏洞
2018/02/28 Javascript
vue中子组件向父组件传递数据的实例代码(实现加减功能)
2018/04/20 Javascript
AngularJS 前台分页实现的示例代码
2018/06/07 Javascript
原生JS实现轮播图效果
2018/10/12 Javascript
vue 2.5.1 源码学习 之Vue.extend 和 data的合并策略
2019/06/04 Javascript
解决layui的radio属性或别的属性没显示出来的问题
2019/09/26 Javascript
python操作MySQL数据库的方法分享
2012/05/29 Python
使用Python的Flask框架实现视频的流媒体传输
2015/03/31 Python
基于Python的文件类型和字符串详解
2017/12/21 Python
详解python while 函数及while和for的区别
2018/09/07 Python
Python2和Python3之间的str处理方式导致乱码的讲解
2019/01/03 Python
DataFrame:通过SparkSql将scala类转为DataFrame的方法
2019/01/29 Python
Flask框架重定向,错误显示,Responses响应及Sessions会话操作示例
2019/08/01 Python
python能做什么 python的含义
2019/10/12 Python
可视化pytorch 模型中不同BN层的running mean曲线实例
2020/06/24 Python
Python爬虫实例——scrapy框架爬取拉勾网招聘信息
2020/07/14 Python
python向企业微信发送文字和图片消息的示例
2020/09/28 Python
python+flask编写一个简单的登录接口
2020/11/13 Python
Black Halo官方网站:购买连衣裙、礼服和连体裤
2018/06/13 全球购物
英国顶尖手表珠宝品牌独家授权经销商:HS Johnson
2020/10/28 全球购物
TCP/IP的分层模型
2013/10/27 面试题
年终总结会议主持词
2014/03/17 职场文书
专题组织生活会发言材料
2014/10/17 职场文书
感恩教师主题班会
2015/08/12 职场文书
导游词之香港-太平山顶
2019/10/18 职场文书