项目背景
最近又做了个企业官网的项目;基本都是些静态界面,对于这种看起来没什么难度的项目,这个时候就要追求点有难度的,才能学习到东西。所以摒弃了常规的一个一个html写的方式,vue组件化+响应式布局才有点意思,正好之前也做过一个自己公司的响应式的官网,这次更加加深了理解。
架构想法
基本的想法都已经确定过了,css配合media query引入,js则根据screen.width动态处理逻辑。这个有鉴于张鑫旭的基于screen.width的伪响应式开发,这篇文章也解释了使用screen.width可以明确地区分不同的js逻辑,而不会耦合在一起。如果为了实现动态拉伸窗口切换样式的需求,可以监听window.resize事件,但是实际这没有什么必要,因为用户不会无聊到不断改变浏览器窗口大小。
对于在不同屏幕下的适配问题,首选自然是rem。以往我们公司采用的却不是这种方式。
1.设计师给750宽的图,我们也设定页面宽度为750。
2.动态调整viewport的值,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <script> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <style> html,body{margin:0;} </style> <script> var w = document.documentElement.getBoundingClientRect().width; var meta = document.createElement('meta'); meta.name="viewport"; meta.content="initial-scale=" + w/750 + ",maximum-scale=" + w/750 + ",user-scalable=no"; var head = document.querySelector('head'); head.appendChild(meta); </script> </head> </script>
|
想法很简单,取物理像素(比如iphone6设备像素750px,物理像素375px,devicePixelRatio为2;这些就不解释了)除750。得到百分比。这样在不同的屏幕上永远固定页面宽度为750像素。其实这里有几个问题:
1.使用getBoundingClientRect(),而不使用screen.width。因为在实际测试发现:有些机型screen.width取得是设备像素,有些取的又是物理像素,兼容性很差;这个在张鑫旭的设备像素比devicePixelRatio简单介绍里面也有体现。
这个时候查看淘宝聚划算(这个网站就是一个典型的rem网站)的源码发现如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| ~function(){ function e(){ r.innerText="html{font-size:"+(a.style.fontSize=a.getBoundingClientRect().width/o*d+"px")+" !important;}" } var t=navigator.userAgent, n=(t.match(/(iPhone|iPad|iPod)/),t.match(/Android/i),window), i=document, a=i.documentElement, o=(n.devicePixelRatio||1,375), d=100, r=(i.head.querySelector('[name="viewport"]'), i.createElement("style")); r.innerText="html{font-size:100px !important}", i.head.appendChild(r), e(), n.addEventListener("resize",e,!1), a.className+=t.match(/ucbrowser/i)?" app-uc ":"" }();
|
可见getBoundingClientRect()获取的基本上应该是物理像素,实际测试也证明了这个
2.必须得首先使用进行初始化,否则getBoundingClientRect()取值会不正确,所以先要初始化一次viewport,再动态计算出缩放比,添加一个新的viewport覆盖
3.这种方式直接缩放整个页面,得到的文字字体缩放效果其实是不对的。
新的实践
既然上面的策略有瑕疵,那就采用新的方式吧。看了下alloyteam的移动端适配利器-rem; 基本这可能就是最佳的方案吧。一言不合上代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| <!doctype html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <style> html,body{margin:0;} </style> <script> var w = document.documentElement.getBoundingClientRect().width, html = document.querySelector('html'); html.style.fontSize = devicePixelRatio*w/10+'px'; var meta = document.createElement('meta'); meta.name="viewport"; meta.content="initial-scale="+1/devicePixelRatio+",maximum-scale="+1/devicePixelRatio+",user-scalable=no"; var head = document.querySelector('head'); head.appendChild(meta); </script> <style> .div{ background-color:black; width:10rem; height:10rem; } .font{ font-size:1rem; } .onepxline{ width:5rem; height:1px; margin:1rem 0; background-color: red; } </style> </head> <body> <div class="div"></div> <span class="font">测试字体大小</span> <div class="onepxline"></div> <script type="text/javascript"> </script> </body> </html>
|
总结下来就是几点:
1.动态设置html的font-size:其实也就是动态设置1rem的值,当然这里也是有相关规律的,就是永远设置为设备像素/10;以iphone6为例,这里的font-size会被设置为750px/10 = 75px。这样就保证了10rem永远是整个屏幕的设备像素,刚好占满整个屏幕。
2.因为是按照设备像素设计和写的css所以,设置viewport缩放1/devicePixelRatio,刚好就是物理像素。
3.这样也就很好的解决了1px设备像素的问题和图片高清问题。
实际处理还有点小技巧,我们不可能总是去计算所有的rem,这样太累。幸好有css预处理器,比如saas,写个自定义函数自动转换px到rem。就可以完全按照设计图来写css了
1 2 3 4 5 6 7
| @function px2rem($px){ $rem: 75px; @return $px/$rem; } body{ font-size: px2rem(10px); }
|