项目包括一个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
版本无法通过触摸手势触发滑动操作,调试发现直接调用slideNext
和slidePre
方法是可以切换下一项和上一项的。由于手动监听DOM上的touchstart
、和touchmove
事件是可以正常触发的,因此解决方法是通过监听touchstart
、和touchmove
事件根据手指移动情况判断上滑以及下滑操作,并分别调用slideNext
和slidePre
方法实现滑动切换。
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;
}
通过直接设置animation
为none
达到暂停动画的目的,不足之处是再次播放动画时,不会保留暂停时的状态。
资源缓存策略
项目中有大量的图片资源和一个音乐资源,为了不让用户在资源加载完成之前就进入系统中,因此在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上的表现是会溢出可视区域的,因为IOS上100%
高度会把工具栏的高度也算上了。另外使用vh
也存在同样的问题。在查资料过程中发现了一个叫做DVH(Dynamic Viewport Unit)
动态适口单位的属性,可以完美解决百分数以及vh
的缺点,但是dvh
是新单位,兼容性不好。最终采用了js
获取可视区域高度设置为页面高度的方法:
let element = document.querySelector('#app');
// 设置元素的高度为视口的高度
if (element) {
element.style.height = window.innerHeight + 'px'
}
Comments | NOTHING