Flexible.js可伸缩布局实现原理与注意事项
本文详解Flexible.js实现移动端自适应布局的核心原理,通过动态调整html字体大小结合rem单位实现一稿多端。文章深入解析了1rem与视觉稿宽度的换算关系,提供了Sass混合宏处理不同DPR下字体的方案,并指出了CSS Sprite模糊、字体单位选择等实际开发中的关键注意事项。
Flexible.js简介与核心原理
Flexible.js是由阿里团队开源的一个JavaScript库,旨在解决移动端界面在不同设备上的兼容与自适应问题,实现“一稿搞定”所有设备的目标。
其核心实现机制如下:
该方案通过JavaScript动态调整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'] = {}));
rem布局计算与换算方法
上述代码的核心逻辑在于修改dpx及document的font-size属性,其计算公式为:docEl.getBoundingClientRect().width / 10 + 'px'。
以宽度为640px的设计稿为例,HTML的字体大小会被设置为64px。这意味着1rem等于64px(即相对于根节点字体大小的倍数)。
若一个元素在设计稿中的宽度为160px,传统做法可能需要通过百分比或响应式媒体查询在不同分辨率(如320px输出80px,640px输出160px)下分别设置。而采用Flexible.js方案,只需统一输出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。针对上述750px视觉稿,可计算出:
1a = 7.5px 1rem = 75px
因此,该示例的设计稿总宽度被划分为10a,即整个页面宽度为10rem,对应的<html>元素font-size设置为75px。
由此,视觉稿上元素尺寸的换算变得简单:只需将原始px值除以rem基准值即可。例如,视觉稿中一张176px * 176px的图片,转换为rem单位即为2.346667rem * 2.346667rem。
开发注意事项与字体处理方案
此外,使用Flexible处理自适应时需注意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,业务方希望其能根据不同终端进行适配。针对此类场景,完全可以使用rem作为Slogan的计量单位。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
WebStorm内置JS代码格式化功能怎么用
2026-11-05 10:10:47
在线JS代码格式化与混淆压缩工具推荐及使用指南
2026-11-03 20:31:53
CodePen、JSFiddle与RunJS对比:哪款在线JavaScript编辑器更好用
2026-11-03 18:52:45
无需安装环境,5款主流JavaScript在线运行工具推荐
2026-11-03 18:23:26
JS怎么将地址栏查询字符串转换为JSON对象
2026-11-03 18:10:29
