移动端H5开发-兼容性问题

1,567 阅读5分钟

引言

笔者是一名前端开发工程师,大部分开发工作都是在 H5 的世界里遨游。多年的 H5 开发经验总结了一些常见的兼容性问题,在这里分享给大家,希望在遇到同类问题时能让你少走弯路,留住日益蓬松的头发。

注:这里不会涉及 css 厂商前缀的兼容问题,此内容会在前端工程化篇中去讲解。

Android 相关

使用 line-height 实现文字垂直居中,发现文字偏上

解决方案:采用 flex 布局,align-items: center 来替代,兼容性更高

.elem {
  display: flex;
  justify-content: center;
  align-items: center;
}
border-radius 画出的圆在移动端有毛边

解决方案:给元素添加 overflow: hidden 属性

.elem {
  overflow: hidden;
}
安卓上去掉语音输入按钮
input::-webkit-input-speech-button {
  display: none;
}

iOS 相关

Vue 单页应用在 iOS 上微信分享失效,图片,标题和描述均未正常显示,安卓上分享正常

iOS 分享 H5 的效果(失效)

安卓分享 H5 的效果(正常)

原因分析:我们一般在 APP.vue 的 mounted 生命周期中初始化微信 SDK,此时页面的地址 hash 是#/,而首页的 hash 是#/home,导致初始化微信 SDK 时传入的分享 url 和用户实际触发分享操作时页面的 url 不一致,致使在 iOS 上分享失败。

解决方案:初始化微信分享 SDK 时传入的地址,和实际触发分享时页面的地址保持一致

iOS safari 被点击元素会出现半透明灰色遮罩

解决方法:给 html 或者 body 加如下 css 代码

body {
  -webkit-tap-highlight-color: rgba(0,0,0,0);
  -webkit-user-modify: read-write-plaintext-only;
}
iOS 禁止保存或拷贝图像

长按图片保存场景下,禁止 IOS 默认识别图像行为

img {
  -webkit-touch-callout: none;
}
iOS 端微信 H5 页面上下滑动时卡顿

解决方法:给滚动元素加上-webkit-overflow-scrolling 属性

-webkit-overflow-scrolling 属性是来控制元素在移动设备上是否有回弹的效果

  • auto:使用普通滚动,当手指在屏幕上离开时,滚动立即停止
  • touch:使用具有回弹效果的滚动, 当手指从触摸屏上移开,内容会继续保持一段时间的滚动效果。继续滚动的速度和持续的时间和滚动手势的强烈程度成正比。同时也会创建一个新的堆栈上下文。
body {
  -webkit-overflow-scrolling:touch;
}
iOS 默认输入框内阴影重置

阻止 iOS 默认的美化页面的策略-webkit-appearance:none;

input {
  border: 0;
  -webkit-appearance:none;
}
对非可点击元素(div,span 等)监听 click 事件,部分 ios 版本不会触发事件

解决方案 1:添加 css 属性 cursor: pointer; 解决方案 2:换成 button 元素

cursor: pointer;

<button></button>
一进页面就调用 focus()方法或者设置 autofocus 属性,输入框没有自动聚焦

无解,在 iOS 系统中自动聚焦不被允许

将日期(yyyy-mm-dd HH:mm:ss)转换成时间戳报错
const date = '2022-09-22 12:00:00';
// 下面写法iOS会报错无法解析
const timeStamp = new Date(date).getTime();
// 兼容写法如下,将横杠转换成斜杠
const timeStamp = new Date(date.replace(/\-/g, '/')).getTime();

webview 相关

手机底部刘海存在背景,和页面背景色不一致

通过指定 body 的背景色来解决。

body {
  background-color: #fff;
  // or 暗色模式
  // background-color: #000;
}
对于带有 hash 的 H5 链接,部分手机厂商的 webview 打开 H5 页面会加载两次

这是部分 webview 对于特殊 url 有独特的解析和加载逻辑,去掉 hash 即可

// 比如下面这个单页应用的链接,完全可以去掉尾部的 hash

https://vivo.diyring.cc/friend/5b818e2a0b8b6113#/

音视频问题

audio 和 video 自动播放问题

由于自动播放网页中的音频或视频,会给用户带来一些困扰或者不必要的流量消耗,所以苹果系统和安卓系统通常都会禁止自动播放和使用 JS 的触发播放,必须由用户来触发才可以播放。

软键盘问题

输入框聚焦,页面底部内容被动往上推

原因分析:这种情况出现在 Android 手机,输入框聚焦弹出输入法时,会触发页面的 resize 事件。页面的可视区域被缩小,导致页面内容被动往上移动。 解决方案:将页面最外层设置为绝对定位,宽高均为 100%,将滚动容器的父节点设一个最小高度为页面的初始化高度。

<template>
    <html>
        <head></head>
        <body>
            <div id="app">
                </div class="view">
                    <div class="scroll">
                        <input type="tel" placeholder="请输入手机号码" :maxlength="11"  />
                    </div>
                </div>
            </div>
        </body>
    </html>
</template>

<script lang="ts">

{
    ...

    // 页面高度信息
    @Getter('innerHeight') innerHeight: string;

    // 更新页面高度信息
    @Mutation('UPDATE_INNER_HEIGHT') updateInnerHeight: (height: number) => void;

    mounted() {
        // 进入页面就获取页面高度快照,给页面设置一个最小高度,防止输入框聚焦后顶起页面底部内容
        this.updateInnerHeight(window.innerHeight);
        window.addEventListener('resize', this.adjustInnerHeight);
    }

    // iPhone进入二级页底部会出现导航条影响innerHeight的取值
    adjustInnerHeight() {
        // 差值>200,我们认为是输入框聚焦弹起输入框导致的resize,不更新innerheight
        const isEffective = Math.abs(window.innerHeight - this.innerHeight) < 200;
        isEffective && this.updateInnerHeight(window.innerHeight);
    }

    ...
}

</script>

<style lang="less">
#app {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

.view {
  overflow: hidden;
  height: 100%;
}

.scroll {
  overflow-y: scroll;
  height: 100%;
}
</style>

其它问题

body 存在默认背景色

body 标签在大部分浏览器中的默认背景色是白色,但在极少数浏览器中的背景颜色是淡绿色或者其他颜色。

解决方案:通过指定 body 背景色为#fff,来兼容更多设备。

body {
  background-color: #fff;
}
旋转屏幕的时候,字体大小调整的问题
body {
  -webkit-text-size-adjust: 100%;
}
页面回退

安卓回退到上一页,上一页会触发 reload 事件,重新加载页面;

iOS 回退到上一页,会触发 visibilitychange 事件,不会重载页面。

document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'visible') {
    backgroundMusic.play();
  } else {
    backgroundMusic.pause();
  }
});

我们可以在 iOS 上监听该事件,处理页面回退逻辑中的系统差异问题。

html2canvas 插件生成截图有黑边

解决方法:降低html2canvas版本至1.0.0-rc.3

// 想要保存的图片节点
const dom = canvasRef.value as HTMLElement;
document.getElementById('share');
html2canvas(dom, {
  allowTaint: true,
  useCORS: true,
  logging: true,
  windowWidth: window.innerWidth,
  windowHeight: window.innerHeight,
  ignoreElements: elem => {
  return elem && elem.className.includes('desc');
  },
}).then((canvas: HTMLCanvasElement) => {
  // canvas 转化为图片
  const img = canvas.toDataURL('image/jpeg');
  const image = new Image();
  image.src = img;
  image.classList.add('img');
  const container = document.getElementById('picture');
  const child = document.querySelector('#picture img');
  child && container?.removeChild(child);
  document.getElementById('picture')?.appendChild(image);
});