• Vue/Nuxt框架开发的PC端网站兼容平板设备的


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

    1. 这个项目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" }
    ]
    
    • 1
    • 2
    • 3
    • 4

    上面这个配置会在HTML页面生成一个meta标签,等同于原生下面配置:

    <meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
    
    • 1

    上面配置完了之后会禁止用户缩放页面,很奈斯。凡事有意外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();
          });
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    1. 前面第一步做完了,那么接下来就开始做页面的缩放程序处理了,比如有下一段渲染后的HTML代码:
      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);
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89

    注意到orientationChange函数此处监听的是媒体查询属性,另外有些其他资料写到用Window.orientation属性监听,但是这个属性已弃用了参考链接

    1. 处理完上面这些基本上就差不多了,但是通过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),
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这样处理完成之后,兼容上到达了80% ~ 90%,有一些弹窗会因为缩放的原因导致与W3CCSS属性规范相违背,导致缩放之后id__layout的容器内position: fixed;效果变成了position: absolute;,这对一些提示信息和确认操作的交互体验是致命的;只要将弹窗el-dialog标签加上 append-to-body 属性配置就行了,加塞到body下面不会受到id__layout的容器缩放的影响;

    后续:

    这里面的一些判断平板(安卓和IOS)的函数可能不一定十分准确,因为查了很多资料并没有一个十分准确的方法去识别平板设备,大家可以根据自己实际情况去修改判断平板等相关函数。用PC端站点去兼容平板设备的逻辑思想大概就是这样的。

  • 相关阅读:
    机器学习笔记之EM算法(五)广义EM的总结与其他变种形式
    全局多项式(趋势面)与IDW逆距离加权插值:MATLAB代码
    Codeforces Round 954 (Div. 3)
    利用PPT导出一张高清图的方法,office与WPS只需要使用一个即可,我使用的是office。
    随机森林实战案例——对天气最高温度进行预测
    2022双十一当贝投影战报:劈风斩浪,六项第一
    【报名指南】2024年第九届数维杯数学建模挑战赛报名全流程图解
    Vue3 - Pinia 状态管理(环境搭建安装及各属性使用教程)详细使用教程
    【OpenCV 例程200篇】210. 绘制直线也会有这么多坑?
    Markdown 画图学习
  • 原文地址:https://blog.csdn.net/qq_45272329/article/details/126888725