Write By CS 逍遥剑仙 http://home.ustc.edu.cn/~CSSjf/
我的主页: https://csxiaoyao.com/
- GitHub: https://github.com/csxiaoyaojianxian
- Email: sunjianfeng@csxiaoyao.com
- QQ:
1. CSS 中的单位
1.1 基于像素 px
最常用的 绝对单位, 按精确像素计算
1.2 基于字号 em / rem
em 是 相对单位, 基准为父节点字体大小, 若自身定义了 font-size 则按自身计算, 不推荐使用
rem 是 相对单位, css3 新加, 按照根节点 <html> 的字号作为基准, 下方提供的设置根节点 62.5% 的方案并不推荐, 具体实践见第二节的介绍
- /* 根节点字体大小设置为 62.5%, 即 10px 可以方便计算, 否则将以浏览器默认字号 16px 为基准 */
- /* 10 ÷ 16 * 100% = 62.5% */
- /* 但 Chrome 最小的字体大小为 12px, 因此浏览器中的字体均>= 1.2rem */
- HTML {
- font-size: 62.5%;
- }
- body {
- font-size: 1.4rem;
- } /* 1.4 * 10px = 14px */
1.3 基于视窗 vw / vh / vmin / vmax
vw / vh 即 viewpoint width / height, 按照 视窗 的宽高的百分比进行计算, 和 CSS 中的 % 按照父元素的宽高作为计算基准的方式不同
vmin / vmax 取视窗宽高二者中较小 / 大值的百分比进行计算
- /* 元素始终在屏幕上可见 */
- .box {
- width: 100vmin;
- height: 100vmin;
- }
- /* 元素始终铺满整屏 */
- .box {
- width: 100vmax;
- height: 100vmax;
- }
1.4 其他 % / vm / pc / pt / ch / ex ...
% 百分比, 一般相对于父元素, 但对于 position: absolute; 的元素是相对于已定位的父元素, 对于 position: fixed; 的元素是相对于可视窗口, 并且当父元素没有指定高度时, 子元素设置百分比没有效果, 高度直接为子元素的实际高度
vm css3 新单位, 相对于视窗宽高较小的那个的百分比, 兼容性较差
下面的单位几乎用不到:
in 寸
cm 厘米
mm 毫米
pt point, 约 1/72 寸
pc pica, 大约 6pt,1/6 寸
ch ...
ex ...
...
2. 使用 rem 进行移动端页面适配
移动端适配最简单的是通过 JS 动态计算 viewport 的缩放值, 但过于粗暴, 会导致页面图片文字失真模糊. 目前, 移动端页面一般使用 rem 或 vw / vh 开发会较为方便, 下面以 rem 为例:
为了方便计算, 约定: 100px = 1rem, 若设计师给到一张宽度为 750px 的设计稿, 那么可以设置 HTML 的字体大小为 设备宽度 / 设计稿宽度 * 100 个 px 像素, 以 iPhone 6/7/8 的宽度 375px 为例, 则 HTML 字体的大小为 50px, 即在宽度为 375 px 的设备上, 1rem = 50px. 对于开发人员, 一个宽度为 50px 的 div, 就可以很轻松的通过除以 100, 计算出对应的 rem 为 0.5rem, 不需要再根据各种机型进行适配, 0.5rem 换算到 iPhone 6/7/8 为 25px, 而放到 750px 宽的设备上对应的是 50px.
总体来说, 使用 rem 进行适配需要以下四步:
先根据设计稿尺寸使用 px 完成页面
设置 meta, 控制视口宽度, 按 1:1 比例渲染页面
<meta name="viewport" content="initial-scale=1,maximum-scale=1,minimum-scale=1">
动态设置 HTML 的 font-size
- var clientWidth = document.documentElement.clientWidth
- document.documentElement.style.fontSize = 100 * clientWidth / 750 + 'px'
建议设置监听, 在页面尺寸变化时仍能正常展示
- if (!document.addEventListener || !Windows.addEventListener) return
- document.addEventListener('DOMContentLoaded', setScale, false)
- Windows.addEventListener('orientationchange' in win ? 'orientationchange' : 'resize', setScale, false)
- Windows.addEventListener('load', setScale, false)
将页面各元素的 px 值除以 100 转换为 rem
3. 横版页面的 rem 适配
上面第二部分通过动态设置 HTML 的 font-size 已经实现了页面随设计稿比例缩放, 这种方式是页面宽度 100% 撑满设备宽度的, 但是很多情况下, 我们更希望部分横版页面能够高度撑满设备高度, 而左右部分留白, 此时有两种方式可以实现:
HTML 的 font-size 依据页面高度进行设置, 假设横版设计稿高度为 375px
- var clientHeight = document.documentElement.clientHeight
- document.documentElement.style.fontSize = 100 * clientHeight / 375 + 'px'
限制横版页面的最大宽度, 假设最大宽度为 670px 时, 左右留白适配合适
- var maxWidth = 670
- var calWidth = Math.min(document.documentElement.clientWidth, maxWidth)
- document.documentElement.style.fontSize = 100 * calWidth / 750 + 'px'
4. 解决微信等第三方 App 的默认字号问题
微信等 App 内可以设置默认字号, 若用户修改了默认文字大小, 会给上述的适配造成困扰, 解决方法是先获取 App 的原始缩放比例, 再在此基础上计算 font-size, 实现如下:
- // 创建 1rem 宽度的不可见元素, 用于计算原始缩放比例
- var scaleDom = (function () {
- var scaleDom = document.createElement('div')
- scaleDom.style.cssText = 'width:1rem;height:0;overflow:hidden;position:absolute;z-index:-2;visibility:hidden;'
- document.body.appendChild(scaleDom)
- return scaleDom
- })()
- // 计算使用过 fontSize 缩放 (如微信) 下的原始缩放比例
- function getOriginScale () {
- var htmlFontSize = Number(String(document.querySelector('html').style.fontSize || 16).replace('px', ''))
- var instanceWidth = Number(String(Windows.getComputedStyle ? Windows.getComputedStyle(scaleDom).width : scaleDom.offsetWidth).replace('px', ''))
- var scale = (htmlFontSize && instanceWidth) ? htmlFontSize / instanceWidth : 1
- return scale
- }
- // 设置 HTML 用于处理 rem 的 font-size
- function setScale () {
- var scale = getOriginScale()
- var clientWidth = document.documentElement.clientWidth
- if (!clientWidth) return
- Windows.uimakerScale = clientWidth / designWidth * scale
- document.documentElement.style.fontSize = 100 * Windows.uimakerScale + 'px'
- }
5. 最佳实践
针对上述的适配方案, 本文提供一套已在 HTML 自助化系统的生产环境中使用的适配代码作为最佳实践.
首先是 HTML 代码中需要配置视窗参数:
- <!DOCTYPE HTML>
- <HTML lang="en">
- <head>
- <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
- ...
- </head>
- <body data-page-type="{{jsonData.pageType}}" data-design-width="{{jsonData.width}}"
- data-max-width="{{jsonData.maxWidth}}">
- ...
- </body>
- </HTML>
在初始化代码中执行下面的自执行函数, 即可完成适配.
- ;(function (doc, win) {
- // 页面数据
- var pageType = parseInt(doc.body.getAttribute('data-page-type')) || 0 // 页面方向 0 竖 1 横
- var designWidth = parseInt(doc.body.getAttribute('data-design-width')) || 750 // 设计宽度
- var maxWidth = parseInt(doc.body.getAttribute('data-max-width')) || designWidth // 最大页面适配宽度
- // 创建 1rem 宽度的不可见元素, 用于计算原始缩放比例
- var scaleDom = (function () {
- var scaleDom = doc.createElement('div')
- scaleDom.style.cssText = 'width:1rem;height:0;overflow:hidden;position:absolute;z-index:-2;visibility:hidden;'
- doc.body.appendChild(scaleDom)
- return scaleDom
- })()
- // 计算使用过 fontSize 缩放 (如微信) 下的原始缩放比例
- function getOriginScale () {
- var htmlFontSize = Number(String(doc.querySelector('html').style.fontSize || 16).replace('px', ''))
- var instanceWidth = Number(String(win.getComputedStyle ? win.getComputedStyle(scaleDom).width : scaleDom.offsetWidth).replace('px', ''))
- var scale = (htmlFontSize && instanceWidth) ? htmlFontSize / instanceWidth : 1
- return scale
- }
- // 设置 HTML 用于处理 rem 的 font-size 和 页面二次缩放
- function setScale () {
- var scale = getOriginScale()
- var clientWidth = doc.documentElement.clientWidth
- var clientHeight = doc.documentElement.clientHeight
- if (!clientWidth || !clientHeight) return
- var calWidth = maxWidth> 0 ? Math.min(clientWidth, maxWidth) : clientWidth
- win.uimakerScale = calWidth / designWidth * scale
- doc.documentElement.style.fontSize = 100 * win.uimakerScale + 'px'
- }
- setScale()
- setTimeout(() => { setScale() }, 300)
- if (!doc.addEventListener || !win.addEventListener) return
- doc.addEventListener('DOMContentLoaded', setScale, false)
- win.addEventListener('orientationchange' in win ? 'orientationchange' : 'resize', setScale, false)
- win.addEventListener('load', setScale, false)
- })(document, Windows)
6. 总结
移动端的适配已是老生常谈, 市面上也有不少成熟的第三方库, 但或多或少需要进行额外的配置, 本文的方案是结合本人在一个自助化生成网页的项目的生产环境的实践中总结得出, 若有更好的方案和建议欢迎和我交流.
sign
来源: https://www.qcloud.com/developer/article/1809067