曾几何时, 前端还仅仅是 PC 端的随着移动时代的兴起, h5 及 CSS3 的推陈出新前端的领域慢慢的由传统的 pc 端转入了移动端, 这也导致了前端这一职业在风口的一段时间出尽了风头
从开始的惶恐和无从下手, 慢慢的到了有了统一的技术方案去落地实现
从手写不同尺寸的媒体查询 css 到以手淘的 flexible.js 来进行移动端的适配, 虽然过程曲折, 不过效果也是十分的显著, 因为有了成熟的体系以后什么东西就有据可寻, 适配也就没那么困难了
但是, 因为这次引入了高德地图, 所以在适配上出现了一点意料之外的问题
首先, 我要说下视口这个东西, 因为手淘的这个方案是严重依赖视口这个概念的
1. 视口
1.1 视口的分类
ppk 将视口分为三大类: 布局视口, 可视视口, 理想视口
那视口是什么呢? 通俗点说就是就是浏览器上 (也可能是一个 app 中的 webview) 用来显示网页的那部分区域, 但 viewport 又不局限于浏览器可视区域的大小, 它可能比浏览器的可视区域要大, 因为为了正常的显示 PC 端的网页, 浏览器会将自己的 layout viewport 设置为一个较大的值, 结果就是会出现左右的滚动条当然 viewport(visual viewport)也可能比浏览器的可视区域要小, 比如有的手机自带的浏览器会有一个自带的黑色小边距
布局视口和可视视口我们作基本了解即可在实际工作中, 我们需要接触和处理的更多是 ideal viewport
而我们前端一直孜孜以求的移动端的适配其实就是为了让用户的浏览器中呈现的是我们的理想视口
ideal viewport 并没有一个固定的尺寸, 不同的设备拥有有不同的 ideal viewport
早期移动端开发, 对于终端设备适配问题只属于 Android 系列, 有 320pt 的, 有 360pt 的, 有 384pt 的等等但随着 iPhone6,iPhone6 + 的出现, 从此终端适配问题不再是 Android 系列了, 也从这个时候让移动端适配全面进入到杂屏时代有 320pt 的, 有 375pt(iphone x)的, 有 414pt(plus)的等等
http://viewportsizes.com 里面收集了众多设备的理想宽度
1.2 如何影响视口?
既然 viewport 这么重要, 那我们怎么去控制他为我所用呢? 这个时候, 就轮到 meta 标签出场了
先来一段我们在开发的时候最常用的一句话
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
用下面的表格来解释一下 content 里面的属性
width | 设置 < em>layout viewport 的宽度,为一个正整数,或字符串 "width-device" |
height | 设置 < em>layout viewport 的高度,这个属性对我们并不重要,几乎不使用 |
initial-scale | 设置页面的初始缩放值,相对于 < em>ideal viewport 进行缩放,为一个数字,可以带小数 |
minimum-scale | 允许用户的最小缩放值,为一个数字,可以带小数 |
maximum-scale | 允许用户的最大缩放值,为一个数字,可以带小数 |
user-scalable | 是否允许用户进行缩放,值为 "no" 或 "yes", no 代表不允许,yes 代表允许 |
<meta name="viewport" content="width=device-width">
width=device-width, 通过这个特殊值我们将 layout viewport => ideal viewport
<meta name="viewport" content="initial-scale=1.0">
通过设置初始的缩放比率, 我们也可以将 layout viewport => ideal viewport
所以上面两种方式是殊途同归的那么, 为什么我们还要将两个都写上去呢?
答案: initial-scale=1.0 是为了处理在 iphoneipad 上, 无论是竖屏还是横屏, 宽度都是竖屏时 ideal viewport 的宽度,
width=device-width 是为了处理在 windows phone 上的 IE 无论是竖屏还是横屏都把宽度设为竖屏时 ideal viewport 的宽度
<meta name="viewport" content="width=400, initial-scale=1.0">
如果出现了上面的这种怎么办呢?
这个时候浏览器会取它们两个中较大的那个值例如, 当 width=400,ideal viewport 的宽度为 320 时, 取的是 400; 当 width=400, ideal viewport 的宽度为 480 时, 取的是 ideal viewport 的宽度
总结起来就是谁大谁先行
2. 引入高德后页面的表现
在 vue 的 spa 项目中引入高德以后, 我们发现在不同的 dpr 下, 地图的显示效果差距非常大
在 dpr=3 的时候, 也就是 plus 的机型上, 地图显得格外的小几乎用肉眼是无法看清上面的字体所以, 这样说来, 基于 flexible 的适配方法肯定是有问题的了
而出现这个问题的原因就是我们的 viewport 被缩放了
- 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;
- }
通过上面的代码计算出了 viewport 缩放的比率当处于 iphone 6+plus 的时候, scale = 0.333333333333....
metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
最后写到页面上面的结果就是:
<meta name="viewport" content="initial-scale=0.3333333333333333, maximum-scale=0.3333333333333333, minimum-scale=0.3333333333333333, user-scalable=no">
所以, iphone plus 是 414pt, 通过 flexible 将 viewport 缩小了 0.33333333333, 我们将 414/0.3333333333=1242.0000001242
而正好高德地图通过 canvas 绘制的画布的宽度也就是 1242
3. 如何解决这个问题
通过我的总结, 处理这个问题的方法大致有三种
3.1 通过 vue-router 的路由守卫进行处理
- beforeMount() {
- this.$nextTick(() = >{
- const dpr = document.documentElement.getAttribute('data-dpr')
- if (dpr > 1) {
- window.tempViewport = document.querySelector('meta[name="viewport"]').getAttribute('content');
- document.querySelector('meta[name="viewport"]').setAttribute('content', 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no');
- window.tempDpr = dpr;
- document.documentElement.setAttribute('data-dpr', 1);
- }
- })
- },
- beforeRouteLeave(to, from, next) {
- if (window.tempViewport) {
- document.querySelector('meta[name="viewport"]').setAttribute('content', window.tempViewport);
- delete window.tempViewport;
- }
- if (window.tempDpr) {
- document.documentElement.setAttribute('data-dpr', window.tempDpr);
- delete window.tempDpr;
- }
- next()
- },
不过这样的方式不是很好, 因为页面在过渡的时候会出现一瞬间样式的变形而且如果在当前有地图的页面有其他结构的话, 其他结构也会错乱
Tips: 如果不是 SPA 的应用, 而且整屏页面是地图占满的情况下, 这个方案还是可行的
3.2 通过 css scale 属性
这个方法在我试验了以后, 也存在问题虽然地图的大小是正常了, 但是在地图上进行点标记的时候, 会出现地图位置的偏移
Tips: 如果仅仅是展示, 而并没有任何交互的情况下, 这个方式也是可行的
3.3 通过设置 dpr = 1 (推荐)
通过设置 dpr=1, 强制 flexible 布局对 viewport 不进行缩放
<meta name="flexible" content="initial-dpr=1" />
这样, 最后写到页面上的 meta 标签就是这样的
<meta name="viewport" content="initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no">
既然 viewport 没有缩放了高德地图通过 canvas 绘制的地图也就是按照我们的 ideal viewport 来进行处理了
不过这种方式会产生另外两个副作用:
通过缩放来处理的 "1px 问题" 这里需要重新去处理了
通过 dpr 设置的不同 dpr 下的文本字号大小, 可能会出现 13px 这样很奇葩的尺寸了
第一个是适配中一个很经典的问题, 我会放到随后去讲而第二个问题只能暂时这样去处理了
4. 结尾
大漠对于这个问题的解释是: flexible 已经完成自己的使命, 该功成身退了他推荐使用 vw,vh 标准的新布局方式
而到底用不用这套方案, 作为前端的我们也是见仁见智了!
来源: https://www.cnblogs.com/ChengWuyi/p/8196647.html