CSS3中-webkit-overflow-scrolling: touch 的使用方法详解

作者:袖梨 2022-06-25


我们一起来看一篇关于 css中-webkit-overflow-scrolling: touch 手动改变 scrollTop 导致的 bug 及解决方案,希望此解决方案能够帮助到各位同学。

关于-webkit-overflow-scrolling,看看相关API,主要用来设置在IOS浏览器的容器内滚动是否开启惯性和回弹的。

所以,一般情况都是设置其值为touch的

-webkit-overflow-scrolling: touch

但是,这一次遇到一个BUG。

我的需求是需要手动设置滚动高度的,js代码是

el.scrollTop = 500;

此时,我发现在IOS环境下,每次手动改变scrollTop后整个容器变成空白,但手指触摸一下内容就出来了,scrollTop是成功改变了的,只是内容绘制出错。(这里我个人判断是浏览器底层的问题,因为安卓不存在这问题)

我通过用延迟加载,延迟设置滚动,或者是手动改变容器中的内容来强制重绘,可惜都不起作用,最终找到问题出在-webkit-overflow-scrolling: touch,因为当我把值设置为auto时BUG不存在。

思路出来了,在手动设置scrollTop前,先关闭惯性滚动,待设置完成后重新开启即可。

贴上解决代码:

el.WebKitOverflowScrolling = 'auto';
el.scrollTop = 500;
el.WebKitOverflowScrolling = 'touch';

查了一下网上的资料,H5端代码就不再敷述了,看一下Native处理

-webkit-overflow-scrolling: touch 的流程如下:

实际上,Safari真的用了原生控件来实现,对于有-webkit-overflow-scrolling的网页,会创建一个UIScrollView,提供子layer给渲染模块使用

要实现这个效果很简单,只需要加一行css代码即可:

[css] view plain copy 在CODE上查看代码片派生到我的代码片
-webkit-overflow-scrolling : touch; 
可用以下网页测试:
[html] view plain copy 在CODE上查看代码片派生到我的代码片
 
 
     
         
        scroll 
         
     
 
   

 
         
   
 
 
 
可以用手指滑动中间的蓝色区域,会发现回弹效果以及滚动得很快:

(点击图片查看大图)
如果把-webkit-overflow-scrolling那行注释掉,就会发现滚动得很慢。


实际上,Safari真的用了原生控件来实现,对于有-webkit-overflow-scrolling的网页,会创建一个UIScrollView,提供子layer给渲染模块使用。创建时的堆栈如下:

[plain] view plain copy 在CODE上查看代码片派生到我的代码片
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,声明为:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
@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的声明为:

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
@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时才应用。

相关文章

精彩推荐