CSS3 的 -webkit-overflow-scrolling: touch 可以讓頁面在Native端滾動時模擬原生的彈性滾動效果。
項目開發中用到該屬性,但導致Native端在彈性滾動到邊界時出現一個黑色背景的VIEW。
查了一下網上的資料,H5端代碼就不再敷述了,看一下Native處理
-webkit-overflow-scrolling: touch 的流程如下:
實際上,Safari真的用了原生控件來實現,對于有-webkit-overflow-scrolling的網頁,會創建一個UIScrollView,提供子layer給渲染模塊使用。創建時的堆棧如下:
Thread 1, Queue : com.apple.main-thread
#0??0x00086723?in?-[UIScrollView?initWithFrame:]?()
#1??0x004ec3bd?in?-[UIWebOverflowScrollView?initWithLayer:node:webDocumentView:]?()
#2??0x001f1769?in?-[UIWebDocumentView?webView:didCreateOrUpdateScrollingLayer:
?? withContentsLayer:scrollSize:forNode:allowHorizontalScrollbar:allowVerticalScrollbar:]?()
#3??0x01d571bd?in?__invoking___?()
#4??0x01d570d6?in?-[NSInvocation?invoke]?()
#5??0x01d5724a?in?-[NSInvocation?invokeWithTarget:]?()
#6??0x027fb6a1?in?-[_WebSafeForwarder?forwardInvocation:]?()
#7??0x027fb8ab?in?__44-[_WebSafeAsyncForwarder?forwardInvocation:]_block_invoke_0?()
#8??0x04ac753f?in?_dispatch_call_block_and_release?()
#9??0x04ad9014?in?_dispatch_client_callout?()
#10?0x04ac97d5?in?_dispatch_main_queue_callback_4CF?()
#11?0x01d09af5?in?__CFRunLoopRun?()
#12?0x01d08f44?in?CFRunLoopRunSpecific?()
#13?0x01d08e1b?in?CFRunLoopRunInMode?()
#14?0x01cbd7e3?in?GSEventRunModal?()
#15?0x01cbd668?in?GSEventRun?()
#16?0x00032ffc?in?UIApplicationMain?()
#17?0x00002ae2?in?main?at?/Users/liuhx/Desktop/UIWebView_Research/WebViewResearch/main.mm:16
實際創建的是UIWebOverflowScrollView,它繼承自UIScrollView,聲明為:
@class DOMNode, UIWebDocumentView, UIWebOverflowContentView, UIWebOverflowScrollListener;
@interface UIWebOverflowScrollView : UIScrollView
{
??????? UIWebDocumentView *_webDocumentView;
??????? UIWebOverflowScrollListener *_scrollListener;
??????? UIWebOverflowContentView *_overflowContentView;
??????? DOMNode *_node;
??????? BOOL _beingRemoved;
}
@property(nonatomic, getter=isBeingRemoved) BOOL beingRemoved; // @synthesize beingRemoved=_beingRemoved;
@property(retain, nonatomic) DOMNode *node; // @synthesize node=_node;
@property(retain, nonatomic) UIWebOverflowContentView *overflowContentView; // @synthesize overflowContentView=_overflowContentView;
@property(retain, nonatomic) UIWebOverflowScrollListener *scrollListener; // @synthesize scrollListener=_scrollListener;
@property(nonatomic) UIWebDocumentView *webDocumentView; // @synthesize webDocumentView=_webDocumentView;
- (void)setContentOffset:(struct CGPoint)arg1;
- (void)_replaceLayer:(id)arg1;
- (void)prepareForRemoval;
- (void)fixUpViewAfterInsertion;
- (id)superview;
- (void)dealloc;
- (id)initWithLayer:(id)arg1 node:(id)arg2 webDocumentView:(id)arg3;
@end
其還有一個子View作為ContentView,是給WebCore真正用作渲染overflow型內容的layer的容器。
UIWebOverflowContentView的聲明為:
@interface UIWebOverflowContentView : UIView{
}
-?(void)_setCachedSubviews:(id)arg1;
-?(void)_replaceLayer:(id)arg1;
-?(void)fixUpViewAfterInsertion;
-?(id)superview;
-?(id)initWithLayer:(id)arg1;
@end
再往底層跟,都是CALayer的操作。
以上兩個類都是UIKit層的實現,需要WebCore有硬件加速的支持才有實際意義,相關的邏輯被包含在
ACCELERATED_COMPOSITING
這個宏里。
從SVN log看,在WebKit 108400版本左右才支持,所以iOS Safari應該是需要5.0。Android只在4.0以上支持。
從前端開發的角度講,只需要知道CSS的屬性-webkit-overflow-scrolling是真的創建了帶有硬件加速的系統級控件,所以效率很高。但是這相對是耗更多內存的,最好在產生了非常大面積的overflow時才應用。
出現黑色背景View其實就是UIWebOverflowScrollView。這個UIWebOverflowScrollView只有當頁面上產生滾動條的時候才會生成,也就是后加載,所以在模塊運行之初去捕捉并設置這個VIEW是捕獲不到且不起作用的。
處理思路:
在html端的JS中對滾動條進行監聽判斷,當頁面出現滾動條時通知Native端。
本人在項目中使用 WebViewJavascriptBridge? 建立H5端與Native端的橋接。Android版本的點這里 。
Native獲取到頁面出現滾動條的通知,再去捕獲這個View并修改顏色為白色,當然顏色是按照你自己的設計來修改的,如果本身的APP為黑色,那不改也無妨。
代碼如下:
if ([_aView isKindOfClass:[UIScrollView class]])
{
??????? _aView.backgroundColor = [UIColor whiteColor];
??????? //下側的滾動條
??????? UIView *view = _aView.subviews[0];
??????? for (UIView *_inScrollview in view.subviews)
??????? {
???????????????? NSLog(@"b—%@",_inScrollview);
???????????????? _inScrollview.layer.backgroundColor = [UIColor whiteColor].CGColor;
??????? }
}
最后提一下,由于-webkit-overflow-scrolling: touch對內存消耗不小,通知Native的捕獲事件盡量只做一次,節省開銷。
資料來源:
網頁在Safari快速滾動和回彈的原理: -webkit-overflow-scrolling : touch;的實現