H5项目遇到的兼容问题汇总


项目包括一个loading组件以及Swiper控制的内容组件Swiper的每个Slide也是一个组件,通过Swiper滑动控制切换不同的显示组件,DOM结构如下所示:

<template>
  <div class="main">
    <Loading v-show="showLoading" class="loading" :loadingText="loadingText"></Loading>

    <swiper v-show="!showLoading">
      <swiper-slide v-for="(item, index) in componentQueue">
        <component
          ref="components"
          :is="item"
          direction="vertical"
          />
      </swiper-slide>
    </swiper>
</template>

Swiper不能滑动

对于IOS12及以下系统,Swiper11版本无法通过触摸手势触发滑动操作,调试发现直接调用slideNextslidePre方法是可以切换下一项和上一项的。由于手动监听DOM上的touchstart、和touchmove事件是可以正常触发的,因此解决方法是通过监听touchstart、和touchmove事件根据手指移动情况判断上滑以及下滑操作,并分别调用slideNextslidePre方法实现滑动切换。

Swiper不能正常显示

在项目开发时刚开始使用的是v-if控制loading页内容页容器的显隐,但这样内容页DOM结构并不会生成,loading过程中内容页的图片资源也不会提前加载,loading页无法完成“预加载”资源的功能。更换为v-show之后虽然没有这个问题,但由于DOM没正常显示引起Swiper没有正常初始化,从而导致轮博图出现不能正常显示的问题。Swiper提供了update方法,官方文档称“应该在手动添加/删除幻灯片、隐藏/显示幻灯片或使用Swiper进行任何自定义DOM修改后调用该方法”。因此通过在初始化后添加了一个定时器调用update方法解决。
然而在IOS12及以下版本的手机上这个问题又复现了,推测是定时器的延迟时间不合适导致的。最终解决方案是设置observer属性为true,这样Swiper将会自己启动检测,每当显示/隐藏、添加/删除子元素时都会自动进行更新。

IOS12打开页面报错Unexpected token '.'

问题发现在IOS12及以下版本。这个错误是在loading页加载时抛出的,根据log信息并不能直接定位到具体报错代码行列,只能定位至问题的组件。因此只能逐行代码进行查错,先把<template><script>注释掉,再逐步放开里面的代码,最终发现是使用可选链运算符?的两行代码导致的报错。因此确定是项目使用的vue3+vite的技术栈不兼容导致的,未对可选链?运算符进行转换,低版本浏览器不能识别而报错。
解决方法:

  • 使用其他写法替换可选链?运算符
  • 安装模块@babel/plugin-proposal-optional-chaining,并配置babel.config.ts

    module.exports = {
        presets: ['@vue/app'],
        plugins: ["@babel/plugin-proposal-optional-chaining"]
    }

html2canvas转图片触发DOM动画

项目中有一个页面进入时有约3s的入场动画,同时此页面还有保存图片的需求。保存图片使用的是html2canvas插件,这样就产生了一个问题,html2canvas在绘制DOM时会重新触发页面的入场动画,最终截取到的图片是动画过程中的一帧,并不是最终的样式,因此需要在保存图片时禁止动画播放,并且在关闭保存动画组件后还不能触发及影响页面动画。
前期采用的方案是临时加了一个css类:

animation-duration: 0.001s !important;
animation-delay: 0.001s !important;

设置动画的过渡时间和延迟时间为0.001s,目的是能够快速执行动画。
然而在IOS12及以下手机上这两个属性并不能取得预期效果。
最终方法是使用以下属性,直接取消动画:

opacity: 1 !important;  // DOM元素默认opacity是0,使delay动画执行前隐藏元素
animation: none;

图片旋转动画不显示

需求是需要在点击图片时,图片会开始旋转,再次点击,图片停止旋转。
原本的方法是通过设置animation-play-state属性来控制动画的暂停和播放,并且能够保留暂停时动画的状态。

.music-play {
    animation-play-state: running;
    -webkit-animation-play-state: running;
}
.music-pause {
     animation-play-state: paused;
     -webkit-animation-play-state: paused;
}

但是在IOS11系统中,这个属性不能正常工作。
最终解决方案是:除IOS11及以下外的其他系统仍使用animation-play-state属性,IOS11及一下系统使用如下样式控制图片旋转:

.music-play-compatible {
    animation: spin 2s linear infinite;
}

.music-pause-compatible {
    animation: none !important;
}

通过直接设置animationnone达到暂停动画的目的,不足之处是再次播放动画时,不会保留暂停时的状态。

资源缓存策略

项目中有大量的图片资源和一个音乐资源,为了不让用户在资源加载完成之前就进入系统中,因此在loading页需要对资源加载情况进行判断,在满足条件之后才允许用户进入系统。本项目对于Image对象使用complete属性判断图片资源是否加载完成,对于音乐资源则通过audio对象的readyState属性判断音乐资源的加载情况,readyState有以下几种状态:

  • 0 = HAVE_NOTHING - 音频/视频尚未初始化
  • 1 = HAVE_METADATA - 音频/视频是就绪的,但尚未加载
  • 2 = HAVE_CURRENT_DATA - 音频/视频是当前的,但未完全加载
  • 3 = HAVE_FUTURE_DATA - 音频/视频的未来帧已加载,但尚未加载完
  • 4 = HAVE_ENOUGH_DATA - 音频/视频已加载完毕

大多数情况下,当readyState大于等于3时既可以不影响音乐播放也不会造成太久的等待时间,有良好的用户体验。这种方式在Chrome浏览器以及测试范围内的安卓手机都可以正常工作,但是在IOS系统(包括最新版)中出现了readyState一直为1的情况。
这是因为Chrome浏览器以及安卓手机在loading页时,虽然音乐没有播放,但是可以正常加载资源,但IOS上的策略是必须要触发音乐播放的事件后才开始加载资源。
最终处理方法是:

  • 进入loading页后,js触发音乐静音播放,然后再暂停播放并复位播放进度,从而触发音乐资源加载且不影响正常的播放
  • 检测资源加载是否完成,做一个兜底,记录检测到图片资源加载完成但音乐资源未加载完成后的事件,若超过所设置的超时时间,便认为第一条策略未正常执行,音乐资源未触发加载,跳过音乐资源的加载状态判断

IOS端Height属性异常

此问题主要出现在IOS上,本意是设置页面高度为100%使页面高度撑满屏幕,但IOS上的表现是会溢出可视区域的,因为IOS100%高度会把工具栏的高度也算上了。另外使用vh也存在同样的问题。在查资料过程中发现了一个叫做DVH(Dynamic Viewport Unit)动态适口单位的属性,可以完美解决百分数以及vh的缺点,但是dvh是新单位,兼容性不好。最终采用了js获取可视区域高度设置为页面高度的方法:

let element = document.querySelector('#app');
// 设置元素的高度为视口的高度
if (element) {
    element.style.height = window.innerHeight + 'px'
}

声明:Hello World|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA协议进行授权

转载:转载请注明原文链接 - H5项目遇到的兼容问题汇总


Carpe Diem and Do what I like