大家做项目的时候有没有遇到过一些不符合常理的开发需求,例如:开发的时候PC
端和H5
是两套站点,一般H5
站点会适配平板设备,但是如果需要改成PC
端适配平板呢;前期开发PC
端没有考虑到PC
端会有需要兼容平板的一天,完犊子了,这可怎么办?PC
端的站点的长宽单位都是px
,写的固定长度;而且会有固定的版型(就是页面一般会有一个最小宽度,比如1200
px,1280
px,1380
px这些不等),如果要重写自适应,恐怕劳力费时还不讨好,当我拿到这个需求时,内心是崩溃的,后来思考了下,也有办法处理,但是会引发另外一些问题,下面我们慢慢展开。
PC
的版型1380
px,也就是小于这个宽度将以横向滚动条展示内容;首先看下面的viewport
配置:// nuxt.config.js
meta: [
{ name: "viewport", content: "width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" }
]
上面这个配置会在HTML
页面生成一个meta
标签,等同于原生下面配置:
<meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
上面配置完了之后会禁止用户缩放页面,很奈斯。凡事有意外IOS
系统高版本上不接受meta
标签限制,那么就需要通过其他方式来禁用用户缩放,看下面一段脚本代码:
// methods: {}
// 阻止IOS手动缩放
stopIOSScale() {
// 阻止双击放大
let lastTouchEnd = 0;
document.addEventListener('touchstart', function(event) {
if (event.touches.length > 1) {
event.preventDefault();
}
});
document.addEventListener('touchend', function(event) {
let now = (new Date()).getTime();
if (now - lastTouchEnd <= 300) {
event.preventDefault();
}
lastTouchEnd = now;
}, false);
// 阻止双指放大
document.addEventListener('gesturestart', function(event) {
event.preventDefault();
});
}
HTML
代码:default
模板里面处理的函数:// default.vue
<template>
<div :id="test-id" :class="{ 'test-tablet': tablet.isTablet}">
// ...to do
</div>
</template>
// ... to do
data() {
return {
tablet: {
domHeight: 0, // 容器总高度
isTablet: false, // 是否可旋转屏幕
scaleRate: 1, // 缩放比例
}
}
}
mounted() {
window.addEventListener('scroll', this.handleJsScroll); // 监听滚动事件做一些处理,如分屏加载,动态计算高度等
this.orientationChange();
}
methods: {
orientationChange() {
try {
let mql = window.matchMedia('(orientation: portrait)');
this.handleOrientationChange(mql); // 第一次加载页面触发
mql.addListener(this.handleOrientationChange); // 改变屏幕方向触发
} catch (error) {
console.log(error)
}
},
handleOrientationChange(mql) {
let screenWidth = 0, screenHeight = 0;
// 苹果平板竖屏,(刷新和监听苹果屏幕旋转文档和屏幕宽度不一致)
if (window.screen.width === document.documentElement.clientWidth) {
screenWidth = window.screen.width || document.documentElement.clientWidth || document.body.clientWidth;
} else {
screenWidth = document.documentElement.clientWidth || window.screen.width || document.body.clientWidth;
}
// 苹果平板横屏,苹果屏幕旋转文档方向改变,屏幕方向不会改变,与安卓平板不一样
if (window.screen.height === document.documentElement.clientWidth) {
screenHeight = window.screen.height || document.documentElement.clientHeight || document.body.clientHeight;
} else {
screenHeight = document.documentElement.clientWidth || window.screen.height || document.body.clientHeight;
}
// 不是平板不处理
if (!this.isTablet()) return;
const isIPads = this.isIPad();
const layoutEle = document.getElementById('__layout').style;
this.tablet.isTablet = true; // 根据是否是平板动态绑定样式处理一些特殊样式
this.stopIOSScale();
// 安卓和ios横屏和竖屏相反
if(mql.matches) {
let scaleRate = isIPads ? screenWidth : screenHeight;
this.tablet.scaleRate = scaleRate / 1380;
} else {
let scaleRate = isIPads ? screenHeight : screenWidth;
this.tablet.scaleRate = scaleRate / 1380;
}
// 缩放比例大于1说明可以完全展示不需要缩放,不处理
if (this.tablet.scaleRate >= 1 || !screenWidth) return;
layoutEle.minWidth = `${1380}px` // 给没有最小宽度的元素设置宽度,也可以通过上面设置的 .test-tablet .xxx {} 方式添加样式
layoutEle.transform = `scale(${this.tablet.scaleRate})`
layoutEle.transformOrigin = 'left top'
},
// 是否是苹果平板
isIPad() {
const ua = navigator?.userAgent;
const isSafari = ua.includes("Safari") && !ua.includes("Linux");
const isIphone = ua.includes("iPhone");
const isIPad = isSafari && !isIphone && 'ontouchend' in document;
return isIPad;
},
// 是否是安卓平板
isPad () {
const ua = navigator?.userAgent;
const isPads = ua.includes("Safari") && ua.includes("Linux");
const isPad = isPads && 'ontouchend' in document;
return isPad;
},
// 是否是平板
isTablet () {
const isIPads = this.isIPad();
const isPads = this.isPad();
return (isIPads || isPads)
},
destroyed() {
window.removeEventListener('scroll', this.handleJsScroll);
}
注意到orientationChange
函数此处监听的是媒体查询属性,另外有些其他资料写到用Window.orientation
属性监听,但是这个属性已弃用了参考链接!
CSS
缩放之后div
还是会占用原有的位置,会在页面底部展示一大片空白区域,那么需要在滚动的时候对缩放的id
为__layout
的容器外层的容器设置高度处理;如下面所示:// methods: {}
// throttle from'lodash';
handleJsScroll: throttle(function () {
// 滚动时动态修改外层容器高度
if (this.tablet.scaleRate < 1) {
let designEle = document.getElementById('__layout的外层容器').style;
this.tablet.domHeight = document.getElementById('__layout').clientHeight || 0;
designEle.height = `${this.tablet.domHeight * this.tablet.scaleRate}px`;
designEle.overflow = 'hidden';
}
}, 3000),
这样处理完成之后,兼容上到达了80% ~ 90%
,有一些弹窗会因为缩放的原因导致与W3C
的CSS
属性规范相违背,导致缩放之后id
为__layout
的容器内position: fixed;
效果变成了position: absolute;
,这对一些提示信息和确认操作的交互体验是致命的;只要将弹窗el-dialog
标签加上 append-to-body
属性配置就行了,加塞到body
下面不会受到id
为__layout
的容器缩放的影响;
这里面的一些判断平板(安卓和IOS
)的函数可能不一定十分准确,因为查了很多资料并没有一个十分准确的方法去识别平板设备,大家可以根据自己实际情况去修改判断平板等相关函数。用PC
端站点去兼容平板设备的逻辑思想大概就是这样的。