通过前几天写的两篇博客(浅谈移动端三大viewport移动端em和rem区别),我们现在来总结一下如何实现一个最佳方案。

之前在第二篇博客中提到过我们可以使用媒体查询来针对不同设备及做适配,如下图

但是这个可能不会太精准,比如我的设备布局viewport可能是370px,这样只能使用320这一版本。而事实上,他们的viewport并不相同,所以他们的布局也得不一样。也就是说我们如果用css媒体查询只能说是可以用,但不是最佳实践。其实通过看某些牛逼的移动端网站,可以看到他们的共同点,他们都是使用js方式来实现的。

可以看到,慕课网以及我们现在做的今日十大H5页面,他们共同的地方就是在根html上设置data-dpr以及font-size。

为什么要设置这两个东西呢?

我们可以回归移动端的本质,就是根据不同的设备规划不同的布局,这里面有一点需要注意,就是任何分辨率下我们的字体要保持一致。所以data-dpr是针对字体设置的,font-size是我们不同设备上用rem的基准值。其实有一个规范的js文件可以帮我们解决以上问题,flexible.js(可以到github上copy)基本满足我们的需求。

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

简单的说一下这里面的重点吧,我们先设置页面viewport中的scale,这个用来解决border 1px问题,下面是我的一个css demo

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<title>Document</title>
<style>
ul li {
width: 100px;
padding: 8px 0 8px 15px;
color: #7c7c7c;
position: relative;
}
ul li + li:before {
position: absolute;
top: -1px;
left: 0;
content: '';
width: 100%;
height: 1px;
border-top: 1px solid #ddd;
/*transform: scaleY(0.5);*/
}
</style>
</head>
<body>
<ul>
<li></li>
<li></li>
<li></li>
</ul>
</body>
</html>

然后我们下面展示的样式第一个是注释掉 transform: scaleY(0.5)的,第二个是打开的。

为什么会这样子呢,我在浅谈移动端三大viewport说过,dpr为2的高清屏下,1个css像素=4个物理像素(1px = dpr²*dp)

所以,设计师想要的retina下border: 1px;其实就是1物理像素宽,对于css而言,可以认为是border: 0.5px;,这是高清屏下(dpr=2)下能显示的最小单位,但并不是所有的浏览器都支持border:0.5px,所以我们需要scale(0.5),也就是上面flexible.js提到的scale,通过不同的dpr(devicePixelRatio)来设置不同的scale。

然后我们的主角rem是通过refreshRem函数中的docEl.getBoundingClientRect().width来获取的,这个值跟document.body.clientWidth是相同的,也就是布局viewport,之后的width/10纯属是为了便于计算。等下,当我们在iphone5下查看布局viewport时,为什么是640,而不是320,

这是因为我们在前面设置了scale,在iphone5下,通过获取dpr(devicePixelRatio)是2,然后计算出initial-scale是0.5,因为在initial-scale为1情况下,我们知道布局viewport等于设备宽度,也就是等于理想视口(screen.width),iphone5下的理想视口是320,然后缩放比是0.5,所以最终的布局viewport是640。移动端大神PPK曾经说initial-scale是理想视口与视觉视口之比,但我感觉既然设置了initial-scale,我们的视觉视口与布局视口就相等了,所以我们在计算时用的document.body.clientWidth,用window.innerWidth(视觉视口)其结果也一样,只不过是为了给大家演示。最后根据我们计算出来的html的基值可以在css编码中还原视觉稿的真实宽高。

比如我们拿到一个iphone6的高清设计稿(750×1334),在psd中量出一个div宽为750,如何转换为rem单位呢?

公式—— rem = px / 基值

通过flexible.js计算根html的font-size为75px,所以我们可以这样写css

div {

  width: 10rem;

}

转换成html就是这样

width: 10rem; // -> 750px

最后因为dpr为2,scale了0.5,所以手机屏幕上的真实宽度为375px,就刚刚好。试想一下,假如我们不用initial-scale=0.5,那样我们得在这个设计稿上量完div后,再口算除以2,才会写width=375px,想想就累。

最后说一下字体的设置吧,这个比较特殊。以下分别是我们现在做的项目(今日十大)中不同设备的字体展示,可以清楚地看到任何手机屏幕上字体大小都要统一。

当然为了方便,我们使用sass做了一次遍历。在不同的data-dpr中遍历font-size(这里我们就看到了之前html中data-dpr的作用了)

移动端布局最佳实践(viewport+rem)的更多相关文章

  1. 移动端布局:视口viewport的理解

    移动端开发中,有一些基本概念需要理解清楚,才能更好的组织编程逻辑.在刚接触时,移动端视口的缩放和rem单位的缩放搞混淆了,弄得自己很蒙圈.所以仔细总结下自己的理解. 移动端的适配,我理解为两点: 第一 ...

  2. JSR-303 Bean Validation 介绍及 Spring MVC 服务端验证最佳实践

    任何时候,当要处理一个应用程序的业务逻辑,数据校验是你必须要考虑和面对的事情. 应用程序必须通过某种手段来确保输入参数在上下文来说是正确的. 分层的应用在很多时候,同样的数据验证逻辑会出现在不同的层, ...

  3. 移动端布局的思考和rem的设置

    如下方法如有不正确的地方,欢迎指正 前提: 设置稿750px 目标:40px = 1rem js设置方法:(小于等于750屏幕等比缩放) ;(function (doc, win, undefined ...

  4. Html5移动端页面自适应布局详解(阿里rem布局)

    在移动设备上进行网页的重构或开发,首先得搞明白的就是移动设备上的viewport,通读网上的各种对于viewport的解释之后 大概viewport可以理解为三种 1.layout viewport  ...

  5. 前端笔记之移动端&响应式(中)视口&百分比布局&弹性盒模型&rem&fillpage

    一.viewport视口 1.1什么是屏幕尺寸.屏幕分辨率.屏幕像素密度? 屏幕尺寸:指屏幕的对角线的长度,单位是英寸,常见的屏幕尺寸有3.5.3.7.4.2.4.7.5.0.5.5.6.0等. 屏幕 ...

  6. 移动端布局 - REM方式

    默认以宽度为640px的设计稿为基准页面,然后通过JS获取当前显示设备的尺寸,对应的调整 html 标签的font-size大小,从而实现通过以rem为单位的移动端布局适配. 具体代码 (functi ...

  7. 移动端布局的一些设置(在viewport里设置使页面显示相同宽度,显示相同像素大小)

    viewport(视口) 具体数值(不设置时默认为980 ,部分安卓手机不支持设置成具体数值) width=device-width 和设备宽度保持一致 user-scalable=no 是否允许用户 ...

  8. 2018亚太CDN峰会开幕, 阿里云王海华解读云+端+AI的短视频最佳实践

    4月11-12日,2018亚太CDN峰会在北京隆重召开,在11日下午的短视频论坛中,阿里云高级技术专家王海华进行了<短视频最佳实践:云+端+AI>的主题演讲,分享了短视频的生命周期关键点和 ...

  9. CSS 布局:40个教程、技巧、例子和最佳实践

    前言: 布局是WEB开发一个重要的课题,进入XHTML/CSS后,使用TABLE布局的方式逐渐淡出,CSS布局以众多优点成为主流,本文将介绍40个基于CSS的web布局的资源和教程.文章的出处在htt ...

随机推荐

  1. mongo查询系统

    首先,我们先向集合(collections)中添加测试文档(documents).如下: > for(i=1;i<=5;i++) db.test.insert({x:i,y:i*i,z:6 ...

  2. 是时候开始用C#快速开发移动应用了

    从2015年接触Xamarin到至今已经2个年头,我对Xamarin的技能没有长进多少,但它却已经足够成熟到在跨平台移动开发工具中占有一席之地.在扫了一些资料之后,突然发现国外有很多移动端的应用已经是 ...

  3. 从deque到std::stack,std::queue,再到iOS 中NSArray(CFArray)

    从deque到std::stack,std::queue,再到iOS 中NSArray(CFArray) deque deque双端队列,分段连续空间数据结构,由中控的map(与其说map,不如说是数 ...

  4. 移动平台Unity3D 应用性能优化

    WeTest 导读 做了大概半年多VR应用了,VR由于双眼double渲染的原因,对性能的优化要求比较高,在项目的进展过程中,总结了一些关于移动平台上Unity3D的性能优化经验,供分享. 一.移动平 ...

  5. sql 注入命令大全

    1.判断有无注入点 ; and 1=1 and 1=2 2.猜表一般的表的名称无非是admin adminuser user pass password 等.. and 0<>(selec ...

  6. java 多线程访问同一个对象数据保护的问题

    java 多线程同时访问统一个数据的时候,会引起一些错误,后面的线程会修改数据,而前面的线程还在使用修改前的内容, 使用 synchronized 关键字,保证代码块只能有一个线程来访问 public ...

  7. windows安装nvm管理node版本

    1.卸载原本系统里面的nodejs 2.下载nvm安装包:https://github.com/coreybutler/nvm-windows/releases ---->nvm-setup.z ...

  8. 9天C#转Java学习过程,自己记录一下

    其实没有完整的9天,就是连续每天花点时间,过程so frustrated,踩坑无数...下面是学习过程的记录 第1天 开始正式学习JavaEE,已完成: 1. Tomcat安装: 2. Tomcat配 ...

  9. 【LeetCode】119. Pascal's Triangle II

    题目: Given an index k, return the kth row of the Pascal's triangle. For example, given k = 3,Return [ ...

  10. Linux 多用户系统

    Linux OS是基于Unix系统开发而来,我们知道计算机是昂贵与稀缺的资源,所以一台计算机就要满足多个用户同时使用,即多用户的系统的思想. 实现方式:通过分时共享的策略.即让多个用户可以同时使用一台 ...