微信小程序中WebView中原生组件限制问题解析

背景

在微信的文档中有一个章节说明了『 原生组件的使用限制 』有这么一段话

『由于原生组件脱离在 WebView 渲染流程外,因此在使用时有以下限制:

原生组件的层级是最高的,所以页面中的其他组件无论设置 z-index 为多少,都无法盖在原生组件上。
后插入的原生组件可以覆盖之前的原生组件。
原生组件还无法在 scroll-view、swiper、picker-view、movable-view 中使用。
部分CSS样式无法应用于原生组件,例如:
无法对原生组件设置 CSS 动画
无法定义原生组件为 position: fixed
不能在父级节点使用 overflow: hidden 来裁剪原生组件的显示区域
原生组件的事件监听不能使用 bind:eventname 的写法,只支持 bindeventname。原生组件也不支持 catch 和 capture 的事件绑定方式
在iOS下,原生组件暂时不支持触摸相关事件。
在工具上,原生组件是用web组件模拟的,因此很多情况并不能很好的还原真机的表现,建议开发者在使用到原生组件时尽量在真机上进行调试。』

解析

所谓的原生组件,即非Web组件系统扩展Native组件。因为小程序在视图渲染层面使用了WebView,而在Video,Map这类组件,使用WebView的WebCore渲染之后体验不佳的诟病一直存在,而且标准不一。小程序上因使用原生的WebView进行渲染,而不是用修改的WebView内核(至少在iOS上没有这么干),而无法对web原生标签扩展。基于用户体验,和坑爹的技术限制,小程序提出了原生组件的概念,也就是在WebView上面使用原生组件填充占位元素的方式修补这类组件用户体验问题。因为WebView和原生组件在应用层本身就不是一个渲染层级,于是出现Web上面的标签无法浮于Video之上(直播应用的恶梦),在不修改技术思路的前提下,position: fixed, overflow: hidden这样的属性是不可能用于原生组件的样式的。不过伪同层渲染也不是说不可能,即在渲染原生组件时候根据层级镂空面积。

特别在Map上使用WebView作为渲染之后体验不佳的诟病一直存在,特别是地图上marker标记过多的重度场景下,笔者所在的公司的在使用高德地图Web端提供出来的C端具备反人类的体验,地图拖拉龟速,点击响应缓慢,加载loading地图区域等待时间过长。而Video则支持的格式有限,列出部分浏览器的支持的如下:

Firefox:支持 Ogg Vorbis和WAV 
Opera :支持Ogg Vorbis和WAV 
Safari :支持MP3,AAC格式 ,和MP4 
Chrome :支持Ogg Vorbis,MP3,WAV,AAC和MP4 
Internet Explorer 9+ :支持MP3,AAC格式 ,和MP4 
IOS :支持MP3,AAC格式 ,和MP4 
Android :支持AAC和MP3 

上述,可以知道视频支持有限(限于版权)。而就我们关注的移动端iOS和Andoroid,实现一个视频播放,我们可能都会有以下几点的需求:
1、全屏处理;
2、覆盖层效果;
3、自动播放;
4、播放控制;
5、隐藏播放控件;
在iOS上如果使用WebView,你无法修改全屏下的工具这一点体验已经足够让所有的产品经理抓狂,更不用说Android的这么多的机型。覆盖层效果在微信上不得不使用微信提供原生组件cover-view实现,而限于原生实现限制,cover-view的支持有限。

设计方案

1、组件层于WebView层之上

这也应该是微信小程序团队现阶段使用的方案,通过特殊的占位标签,使用getBoundingClientRect获取组件位置,而原生组件跟随Webview滚动。
Talking is cheap. Show me your code,那么用代码实现的效果的如下。

从图中可见,覆盖层确实位于原生组件之下。

2、组件层于WebView层之下

此方式略微复杂。需要通过与Webview scroll联动的置于Webview之下的Component Layer实现,而Webview背景设置为透明。至于事件,通过Webview的事件透传,传递到Component Layer,需要通过缓存webview中元素再计算是否被点中通过重写hitTest方法实现。通过此技术方案实现的好处也是明显的,因为原生组件层很多时候都是置于最底层,而Web上的组件可以轻松覆盖于Native之上,无需使用cover-view之类的hack方法。
效果如下所示

Tips

在iOS上还特别需要注意一点UIWebview的坑。在使用-webkit-overflow-scrolling 使用,你会发现momentum scroll阶段并不会触发scroll事件,而且 scrollTop 属性不会变化,当然getBoundingClientRect也同样失效。如果考虑使用touchmove 这样事件你也仅仅在手指还在屏幕上的时候触发,检测滚动区域内部元素的getBoundingClientRect 同样无效。

当然幸运的是,这么大一个坑只是发生在UIWebview,对WKWebview并没有影响。

你可能感兴趣的:(微信,微信小程序,微信开发,微信开放平台,微信公众平台)