前言:18年12月24日项目成功上线了,在经历了两周的线上bug、UI以及代码优化后,解决了很多问题,因而再完善与优化一下这个 项目。
布局优化在这篇文章完成了 → [移动端优雅布局实践](),最后我使用的方案是——absolute脱离文档流(好处是设置容器的height: 100%
,能够直接继承html窗口高度)。javascript
开始项目以前没有对dpr(device pixel radio)与缩放作过多的了解,在项目开发的时候就将它们都直接写死为1了。到后来UI验收的时候发现并无实现UI设计师预期的细线效果。我在解决这个问题的时候才去认真看了一下dpr的介绍。这篇详解dpr的文章写得还不错。css
从概念来讲,dpr就是设备的物理像素与设备独立像素(也就是css逻辑像素,如下就称为css逻辑像素)的比率。html
好比:iPhone 6的分辨率是750*1334,window.screen.width
(css逻辑像素)为375,所以java
dpr = 750 /375 = 2
再好比:iPhone X的分辨率是1125*2436,window.screen.width
(css逻辑像素)也是375,所以node
dpr = 1125 /375 = 3
那么dpr有什么用呢?react
在这以前先提一下咱们移动端必备的一个meta
标签:webpack
<meta name="viewport" content="width=device-width,maximum-scale=1,minimum-scale=1,user-scalable=no" />
device-width在html中也一样被解读为理想(基准)视口的宽度,即320px,375px,414px,这里的px就是指css像素,一般也被称为逻辑像素;那咱们能够认为html中的css像素的显示尺寸应该和NA中的pt、dp的显示尺寸相等。
经过这个meta标签,咱们能够实现initial-scale=1
初始缩放100%,就能够达到1px的css逻辑像素 = 眼睛在设备上看起来的1px
,换句话说body { width: 375px; }
能够在iPhone 6上充满竖屏的整个宽度。git
那么问题就来了,若是咱们要给一个盒子加上一个1px的细线:border-bottom: 1px solid red;
。那么在iPhone 6上真的是1px吗?github
iPhone 6真机截图(宽度为702px):web
能够看出高度明显不止1像素。这就是因为dpr形成的,由于iPhone 6的dpr为2,且缩放比例为100%,1px的css渲染出来就是2px物理像素。
这就是咱们UI粑粑和产品们不满意的地方。
那接下来如何去解决这个问题呢?
根据设备的dpr来动态计算缩放比例,以及根节点的font-size
。
// rem.js (function(doc, win) { var docEl = doc.documentElement, dpr = Math.min(win.devicePixelRatio, 3); dpr = window.top === window.self ? dpr : 1; //被iframe引用时,禁止缩放 var scale = 1 / dpr, resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize'; docEl.dataset.dpr = dpr; var metaEl = doc.createElement('meta'); metaEl.name = 'viewport'; metaEl.content = 'initial-scale=' + scale + ',maximum-scale=' + scale + ', minimum-scale=' + scale + ',user-scalable=no,viewport-fit=cover'; docEl.firstElementChild.appendChild(metaEl); var recalc = function() { var width = docEl.clientWidth; // 大于1280按1280来算 if (width / dpr > 1280) { width = 1280 * dpr; } // px : rem = 100 : 1 docEl.style.fontSize = 100 * (width / 375) + 'px'; }; recalc(); if (!doc.addEventListener) return; win.addEventListener(resizeEvt, recalc, false); })(document, window);
若是有显示富文本元素,则须要处理富文本元素的样式
移动端为了适配不一样的机型,使用rem
为单位是一个不错的选择,并且咱们也一直在用它。
这里除了根据dpr来计算initial-scale
,还调整了根节点的font-size
,以致于在缩放的时候可以还原到视窗大小(由于要缩放,因此要相应的增长rem
的基数)。
这样咱们上面写的border-bottom: 1px solid red;
在这个方案显示出来就是这样的:
哇咔咔,能够看出明显变细了,这才是咱们UI粑粑们想要的O(∩_∩)O~~
可是这样的设置在结合ant-design-mobile
的时候,发现ant-design-mobile
的组件都被缩小了。原来是,它的元素都是以px为单位,而咱们缩放前没有对它的最小单位乘以相应的基数。那么,咱们须要给它配置一个基数!
查文档发现ant-design-mobile
提供了主题配置,并且它提供了一个@hd
的变量作为长度基本单位,它的默认值是1px
。
@hd
设置为0.01rem
就能够解决问题。这个主题配置的文档是以webpack项目来作的例子。那如何在next.js
项目中完成自定义配置呢?
我在next.js的examples中没有找到我所须要的example,不过找到了两个相关的例子:一个是with-antd-mobile,一个是with-ant-design-less。第二个是ant-design
的自定义主题配置,那应该就能够仿照这个example去增长with-antd-mobile的自定义主题配置。
这里提一下,这个
with-antd-mobile在我写
Next框架与主流工具的整合以后更新了
next.config.js
的配置,这里也改为了最新的配置。
npm i @zeit/next-less @zeit/next-css less less-vars-to-js -S
.babelrc
配置{ "presets": ["next/babel"], "plugins": [ [ "import", { "libraryName": "antd-mobile", "style": true } ] ] }
next.config.js
配置/* eslint-disable */ const withCSS = require('@zeit/next-css'); const withSass = require('@zeit/next-sass'); const withLess = require('@zeit/next-less'); const lessToJS = require('less-vars-to-js'); const fs = require('fs'); const path = require('path'); // Where your antd-custom.less file lives const themeVariables = lessToJS(fs.readFileSync(path.resolve(__dirname, './antd-custom.less'), 'utf8')); // fix: prevents error when .less files are required by node if (typeof require !== 'undefined') { require.extensions['.less'] = file => {}; require.extensions['.css'] = file => {}; } module.exports = withCSS( withLess( withSass({ lessLoaderOptions: { javascriptEnabled: true, modifyVars: themeVariables } }) ) );
antd-custom.less
@hd: 0.01rem;
重启项目,就大功告成了。
antd-mobile
的Toast.info()
组件在显示的时候不能点击背景就消失,与原生的Toast有些差别,为了体验,这里再作了一层封装,在点击背景的时候隐藏Toast。
// utils/toast.js static info = (content, duration, onClose, mask) => { Toast.info(content, duration, onClose, mask); const toastElement = document.getElementsByClassName('am-toast-mask')[0]; toastElement && toastElement.addEventListener('click', () => { Toast.hide(); onClose && onClose(); }); };
antd-mobile
的loading图在Android上有些怪异,这里也自定义了Loading:
static loading = (content, duration, onClose, mask) => { Toast.info( <div> <svg className="rotate360-800" width="0.26rem" height="0.26rem" viewBox="0 0 26 26"> <title>加载</title> <desc>Created with Sketch.</desc> <g id="首页" stroke="none" strokeWidth="1" fill="none" fillRule="evenodd"> <g transform="translate(-185.000000, -519.000000)" fill="#FFFFFF" id="加载"> <g transform="translate(185.000000, 519.000000)"> <g id="分组" transform="translate(0.625011, 0.625011)"> <path d="M12.3750144,0.0259531552 C5.53983848,0.0259531552 0,5.56600648 0,12.4009676 C0,19.2359286 5.53983848,24.775982 12.3750144,24.775982 C19.2099755,24.775982 24.7500288,19.2359286 24.7500288,12.4009676 C24.7500288,5.56579163 19.2099755,0.0259531552 12.3750144,0.0259531552 Z M12.3750144,22.028385 C7.05736759,22.028385 2.74781179,17.7185714 2.74781179,12.4009676 C2.74781179,7.08332074 7.05741056,2.7735501 12.3750144,2.7735501 C17.6926612,2.7735501 22.0026467,7.08336371 22.0026467,12.4009676 C22.0026467,17.7186144 17.6926182,22.028385 12.3750144,22.028385 Z" id="形状" fillOpacity="0.2" fillRule="nonzero" /> <path d="M12.3749972,0.0259402646 L12.3750144,2.77353721 C17.6926612,2.77353721 22.0026467,7.08335082 22.0026467,12.4009547 L24.7500116,12.4009547 C24.7500116,5.56577874 19.2099583,0.0259402646 12.3749972,0.0259402646 Z" id="路径" /> </g> </g> </g> </g> </svg> <div style={{ fontSize: '0.12rem' }}>{content}</div> </div>, duration, onClose, mask ); };
一个项目须要在不断的优化与完善中才能变得更好。搭建项目是对一个项目负责人很大的考验,若是在项目设计的初期有许多的问题没有考虑到,就颇有可能致使优化的时候须要耗费很大的精力。
总之,不要逃避困难与问题,这些都是成长路上不可或缺的。