Vue/Nuxt框架开发的PC端网站兼容平板设备的
大家做项目的时候有没有遇到过一些不符合常理的开发需求,例如:开发的时候PC端和H5是两套站点,一般H5站点会适配平板设备,但是如果需要改成PC端适配平板呢;前期开发PC端没有考虑到PC端会有需要兼容平板的一天,完犊子了,这可怎么办?PC端的站点的长宽单位都是px,写的固定长度;而且会有固定的版型(就是页面一般会有一个最小宽度,比如1200px,1280px,1380px这些不等),如果要重写自适应,恐怕劳力费时还不讨好,当我拿到这个需求时,内心是崩溃的,后来思考了下,也有办法处理,但是会引发另外一些问题,下面我们慢慢展开。
- 这个项目
PC的版型1380px,也就是小于这个宽度将以横向滚动条展示内容;首先看下面的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代码:

下面是进行处理PC端站点缩放的核心程序代码,在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端站点去兼容平板设备的逻辑思想大概就是这样的。