关于异形屏的安全区以及苹果官方的文档,网上有很详尽的博客,这里不再累述,本文偏实践。阅读本文前,可点击了解safe-Area概念
适配iphone X方案:
1. 实现原理:
> 首先我们需要了解一个属性:constant(safe-area-inset-bottom)/env(safe-area-inset-bottom);这个的含义是,安全区底部距离屏幕底部的距离。因此,非异形屏下,这个值为0/或不解析;异形屏下,这个值为真实的距离,比如34px啥的。
接下来,我们需要做的有三点:
- 找到有底部banner的页面
- 将底部banner的父容器从bottom: 0; 升级成 bottom: 0;bottom: constant(safe-area-inset-bottom); bottom: env(safe-area-inset-bottom); // bottom: 0;是兼容非异形屏,bottom: constant(safe-area-inset-bottom);是为了让banner上移到安全区内。
- 给 banner:after添加纯色遮罩bottom: 0; height: 0; height: constant(safe-area-inset-bottom); height: env(safe-area-inset-bottom); background: #fff;
(注:iOS11支持constant语法,future版本支持env语法)
然后我们发现底部的banner确实是在安全区内了,而且底部有纯色遮罩覆盖,不会有穿透效果。
然而。。。。我们是否忘记了,底部banner上移了,那页面里原有的内容区是不是被盖住了一部分?
所以,我们需要再给body:after加上height: 0; height: $safeArea(bottom);把body也撑起一个高度,使得内容区不会被上移了的banner遮住。
2. 实现方式:
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
| $safeAreaHeight = ($height, $isImportant){ @if $isImportant { height: $height !important; height: t('calc( constant(safe-area-inset-bottom) + ' + $height + ')') !important; height: t('calc( env(safe-area-inset-bottom) + ' + $height + ')') !important; } @else { height: $height; height: t('calc( constant(safe-area-inset-bottom) + ' + $height + ')'); height: t('calc( env(safe-area-inset-bottom) + ' + $height + ')'); } }
$safeAreaTop = ($height, $isImportant){ @if $isImportant { top: $height !important; top: t('calc( constant(safe-area-inset-top) + ' + $height + ')') !important; top: t('calc( env(safe-area-inset-top) + ' + $height + ')') !important; } @else { top: $height; top: t('calc( constant(safe-area-inset-top) + ' + $height + ')'); top: t('calc( env(safe-area-inset-top) + ' + $height + ')'); } }
$safeAreaLeft = ($left, $isImportant){ @if $isImportant { left: $left !important; left: t('calc( constant(safe-area-inset-left) + ' + $left + ')') !important; left: t('calc( env(safe-area-inset-left) + ' + $left + ')') !important; } @else { left: $left; left: t('calc( constant(safe-area-inset-left) + ' + $left + ')'); left: t('calc( env(safe-area-inset-left) + ' + $left + ')'); } }
$safeAreaRight = ($right, $isImportant){ @if $isImportant { right: $right !important; right: t('calc( constant(safe-area-inset-right) + ' + $right + ')') !important; right: t('calc( env(safe-area-inset-right) + ' + $right + ')') !important; } @else { right: $right; right: t('calc( constant(safe-area-inset-right) + ' + $right + ')'); right: t('calc( env(safe-area-inset-right) + ' + $right + ')'); } }
$safeAreaBottom = ($height, $isImportant){ @if $isImportant { bottom: $height !important; bottom: t('calc( constant(safe-area-inset-bottom) + ' + $height + ')') !important; bottom: t('calc( env(safe-area-inset-bottom) + ' + $height + ')') !important; } @else { bottom: $height; bottom: t('calc( constant(safe-area-inset-bottom) + ' + $height + ')'); bottom: t('calc( env(safe-area-inset-bottom) + ' + $height + ')'); } }
|
1 2 3 4 5 6 7
| body:after { $safeAreaHeight(0px, true); display: block; content: ''; width: 100%; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
.f-safeArea { $safeAreaBottom(0px, true); } .f-safeArea:after { $safeAreaHeight(0px, true); display: block !important; content: '' !important; position: fixed !important; left: 0 !important; bottom: 0 !important; width: 100% !important; background: #fff !important; }
|
1 2
| <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
3. 实现效果:
APP内:
Safari非全屏:
Safari全屏:
总结:
- wap中的底部banner没有一个通用组件的形式,因此没有通用方法去适配,只能将涉及到底部banner的页面进行针对性的修改。
- 因为涉及到的页面较多,本方案采用在不侵入原有的代码的情况下,通过添加class的方案去兼容覆盖原有样式,达到兼容iPhone X的目的。因为不破坏原有的DOM结构,修改的成本较小。
- 可以考虑以后在wap中写一个通用banner组件容器,baner中和业务逻辑相关的代码作为组件的$body,以后做兼容只需要修改通用组件,节省成本。