异形屏兼容框架
《异形》系列电影从1979年第一集上映至今将近40年,从最初以恐怖电影的名头上映,到现在成为科幻电影历史上的经典... ... 搞错了,接下来不是影评,是知识点!
背景
现在的手机制造者在体积不变的情况下,尽量让屏幕变大,确实在屏幕设计上煞费了苦心,为了增大液晶屏的占比,「异形」出现了!水滴屏、刘海屏等各种「异型屏」的独特界面设计替代了原本规规矩矩的布局,用户开心了,前端同学可头疼了,不仅要保证页面正常展示,还要考虑到用户的视觉体验。这些「异型屏」的出现意味着我们需要花费大量的精力在适配上,或者常常忘记了适配,造成功能遮挡或缺失,影响用户体验。

为了解决这一通用需求,我们决定做一个支持通用吸顶吸底,兼容各种异型屏的框架组件,用了这个组件你就不用为了这些兼容头疼,组件都会帮大家实现。本文就从这里展开 ~
浏览器介绍
目前 html5 规范中并没有异型屏相关 API,因此针对 app、移动浏览器、PC浏览器等终端的处理会有差异。
APP 端内
android、iOS 上的异型屏设备都是最近2年出现并增多的,说明后还有存量的完整屏幕的设备。此外,android、iOS 系统底层对于异型屏顶栏支持度也不一致,通常只有较新的系统才能支持特效较炫酷的能力。所以,单靠 FE 无法解决大量不同设备(主要是 android)和不同系统(iOS 系统版本不一)的兼容性问题。
基于 APP 和 M 页通常都有成熟的通信框架,为了保证兼容性需要 APP 开发人员协助:
通过在 url 中增加参数向 app 声明要实现顶栏穿透效果;(如果APP默认都穿透,可以忽略);
APP 提供顶栏穿透是否成功的判断方法;
如果顶栏穿透成功,则 APP 返回顶栏的高度。FE 利用顶栏高度做顶部偏移;
APP 浏览器
依赖各浏览器APP的能力,得每一个浏览器应用挨个适配,无法毕其功于一役。
PC 浏览器
PC 端基本不存在异型屏的场景,暂不考虑。
框架的出现:
在没有异型屏框架出现之前,前端同学在开发页面需要实现一系列相关逻辑:顶部是否支持穿透、实现穿透能力、获取顶栏高度等,而且实现方法各异,不可复用,可维护性不高。现在使用异型屏框架一步就可实现顶部穿透能力, 只需要把你页面的代码包裹在这个框架内,如下代码所示:
import { PageBaseBox } from '@zz-common/zz-ui';
Vue.use(PageBaseBox);
<!-- 默认带有标题任务栏,底部可吸底 -->
<z-page-base-box>
<div>自定义模块部分1</div>
<div>自定义模块部分2</div>
<template v-slot:fixedFooter>
<div class="footer">底部模块-可编辑样式</div>
</template>
</z-page-base-box>
除了顶部穿透,还可扩展以下功能:
Ta 支持通用吸顶,吸底;兼容水滴屏、刘海屏等各种异型屏。
Ta 支持连续累加吸顶(目前支持2级)或连续覆盖吸顶。
Ta 可支持serviceWork或异常监控等。
Ta 可支持各页面统一添加特定模块或功能(如页面统一导航条)
一段简短的视频演示:
接下来简单来看下源码,提炼出这个框架中几处实现逻辑:
基本功能 - 吸顶&吸底:
吸顶用到了 position:sticky
,但是兼容性不是很好,这里做了兼容处理:
// 是否支持某属性
cssSupport (attr?: any, value?: any) {
if (fixedMainDom && fixedMainDom.style && attr in fixedMainDom.style) {
fixedMainDom.style[attr] = value
fixedSubDom.style[attr] = value
return fixedMainDom.style[attr] === value
} else {
return false
}
}
initDome () {
...
// 使用判断
if (this.cssSupport('position', 'sticky')) {
return
} else {
// do SomeThingA
}
}
其中 do SomeThingA
做了什么?
监听页面滚动事件(为了提高性能,增加了滚动节流),识别元素吸顶时机;计算需要吸顶元素距离顶部的高,来获取top值。
这里使用position:fixed
,而将吸顶设置top: 0
就可以了。这个时候问题出现了,页面元素与iphoneX的刘海屏中隐藏不掉的时间和信号任务栏,重!叠!了!

吸顶的界面兼容,不同的 「异形」,预留不同的顶部高度,也就是top值。判断如果是在客户端内,可以让客户端同学配合,获取任务栏的高度TaskHeight
并提供给前端(这里不做赘述),然后拿到这个值,统一在框架外增加paddingTop
,任务栏部分舍弃不用,避免h5内容被覆盖。若在端外,浏览器已经统一处理,不需要添加paddingTop
。
同理,吸底也是直接使用position:fixed
;bottom:0

这里需要注意的是,iphoneX的底部操作条覆盖了底部内容,导致按钮失效了,这里还需要做一个兼容。
// 如果是iphoneX,添加特有class
<div :class="['page_base', {'is_iphonex': isIphoneX}]">
<div class="fixed_footer"></div>
</div>
// this.isIphoneX = navigator.userAgent.toLocaleUpperCase().indexOf('IPHONE') >= 0 && window.screen.height >= 812
// 样式
<style scoped lang="scss">
$fixedBottomIPhoneX: 50px;
.page_base.is_iphonex{
margin-bottom: $fixedBottomIPhoneX;
.fixed_footer{
bottom: $fixedBottomIPhoneX
}
}
</style>
基本布局 - Slot实现:
作为一个框架,需要控制它包含的吸顶模块,吸底模块,连续吸顶等。开发一个页面,内容布局多种多样,怎么识别模块呢?
为此,在框架中添加了一些特殊身份slot,还支持传入自定义slot。针对特殊身份的solt,做了一些处理,如:吸顶/吸底计算等
<!-- z-page-base-box 默认带有标题任务栏,底部可吸底 -->
<z-page-base-box>
<template v-slot:fixedHeaderSlot>
<div class="header">自定义任务栏信息</div>
</template>
<div>自定义模块部分1</div>
<div>自定义模块部分2</div>
<template v-slot:fixedFooter>
<div class="footer">底部模块-可编辑样式</div>
</template>
</z-page-base-box>

连续吸顶,覆盖吸顶 - Dom跟随:
首先先来说一下覆盖吸顶,一层盖住一层,直接外部传参给组件,告诉组件要不要进行覆盖,那么层级的所有
top
都为任务栏的高度taskHeigth
,这个不难理解。(见本文开头,一段简短的视频演示)接下来连续吸顶:头部吸顶,一级吸顶,二级吸顶
这里可以看到一级吸顶高度变化了,二级吸顶dom会跟随,这是怎么做到的呢?
这里使用MutationObserver
创建一个观察者。
MutationObserver
接口提供了监视对DOM树所做更改的能力,译名dom变动观察器
// api定义
var observe = new MutationObserver(function(mutations,observer){
})
MutationObserver有三个方法,分别如下:
1、observe:设置观察目标,接受两个参数,target:观察目标,options:通过对象成员来设置观察选项
2、disconnect:阻止观察者观察任何改变
3、takeRecords:清空记录队列并返回里面的内容
// 添加观察者
addAbserve (target) {
observe = new MutationObserver((mutations,observe) => {
this.mainHeight = this.getTargetRect(abserveTarget).height
})
// childList:设置true,表示观察目标子节点的变化,比如添加或者删除目标子节点,不包括修改子节点以及子节点后代的变化;
//subtree:设置为true,目标以及目标的后代改变都会观察
observe.observe(target, {childList: true, subtree: true})
}
// 移除观察者
removeAbserve () {
observe && observe.disconnect()
}
功能增强
由于是一个框架,包裹页面内容,因此可以增加一些通用UI, 例如:增加底部导航栏、增加侧边功能栏,增加广告位等 也可以统一加一些通用功能,例如:serviceWork
、异常监控等。
5. Webpack4 入门(上)|| Webpack4 入门(下)
6. MobX 入门(上) || MobX 入门(下)
7. 80+篇原创系列汇总
回复“加群”与大佬们一起交流学习~
点击“阅读原文”查看 80+ 篇原创文章