您的位置:首页 > 其它

Viewport那些事(四)

2013-05-05 11:44 330 查看
meta viewport

     
     为了更好地方便网页作者在移动浏览器上设置合适自己网页的viewport,Apple在meta标签中引入了viewport属性,相关的介绍可以看这里:《Using the viewport》
     http://developer.apple.com/library/ios/#documentation/AppleApplications/Reference/SafariWebContent/UsingtheViewport/UsingtheViewport.html
     
scale
     这里缩放比例指的是设备像素和CSS像素之间的比例,即可以认为是device-width/height和visual viewport(可以通过window.innerWidth/Height获取)之间的比例。
旋转屏幕
     Safari旋转屏幕是保持visual viewport的宽度不变,除非超出最大/最小缩放比例,所以旋转屏幕后缩放比例会发生变化。

Viewport的默认值(IOS)
     首先来看看width的默认值,前面和Apple的文档中都有提到viewport的默认值中width为980px,但是当initial-scale被设置为1.0的时候,Safari会认为width的值为device-width。所以如你想设置initial-scale为1.0并且viewport的宽度为980px时,你需要同时设置这两个属性,详见《Using the viewport》。
     其他属性的默认值文档中虽然没有提到,但是很容易试验出来。
     minimum-scale,它的值是根据排版出来的document大小来计算,Safari会取参考值0.25,device-width/document.width和device-height/document.heght的最大值,可以看出Safari的设计是想保证visual viewport刚好包含文档的整个宽度或者刚好包含文档的整个高度。
     maxmum-scale,如果没有指定,Safari会取maxmum-scale为5。
     initial-scale,如果没有指定,Safari会取initial-scale = minimum-scale。

     基于以上这些结论,可以比较容易地知道不同网页在不同meta viewport设置下的layout viewport和初始的visual viewport,我们还是以(三)中的那个页面为例来说明,没有设置viewport属性并且文档高度足够大,如果页面中存在比较大的overflow元素,例如width:2000px,那么文档的宽度document.width就是2000,Safari Mobile根据以上规则计算出的minimum-scale和initial-scale就是document.width/device-width,所以初始打开页面会是这样:


     
     如果没有overflow元素,document.width就是viewport的大小980,初始打开页面就会是这样:



    如果 网页通过javascript动态调整页面的宽度或高度,那么就有可能导致Safari Mobile打开页面后计算出的minimum-scale和initial-scale不断变化而发生抖动,在用户进行了一次缩放或者旋屏之后抖动停止。

initial-scale对width的影响
     如果没有设置width而设置了initial-scale,那么width = device-width / initial-scale
     如果设置了width并且设置了initial-scale,那么width为device-width / initial-scale和设置的width间的最大值 

document大小对minimum-scale的影响
     即使网页作者指定了minimum-scale的值,它也会根据排版出来的document大小来调整,最终的值为minimum-scale,document.width/device-width和document.heght/device-height三者之间的最大值。

Safari中Viewport的处理流程

viewport相关的数据结构
     viewport参数在Document中保存的结构如下所示:

struct ViewportArguments {
enum {
ValueAuto = -1,
ValueDeviceWidth = -2,
ValueDeviceHeight = -3,
ValuePortrait = -4,
ValueLandscape = -5
};

float width;
float minWidth;
float maxWidth;
float height;
float minHeight;
float maxHeight;
float zoom;
float minZoom;
float maxZoom;
float userZoom;
float orientation;
};


   结构中每一个值都对应这meta标签中viewport的属性值,默认值都是ValueAuto(-1)。

触发Viewport更新的时机
     在WebKit源码中,触发viewport更新的时机只有两处,分别是在加载第一次接收到数据并且Document创建之后和Meta标签处理时解析到viewport属性时,相应的函数调用关系图如下:

     


     在Document创建之后更新viewport时ViewportArguments为默认值ValueAuto。

viewportArguments的处理
     线程分工
     在对Viewport的处理中,内核线程仅完成viewport的解析步骤就将viewport参数抛给主线程来处理。
     主线程会完成viewport的计算和处理工作。除了内核线程抛转来的viewport消息,一些其它情况也会触发主线程中viewport的计算步骤。

     在WebKit源码中,WebCore只解析viewport的属性并不处理ViewportArguments(通过Document::processArguments函数解析Viewport参数),而是将其交给ChromeClient来处理,对应的函数调用关系图如下:
     
     


     在Safari的ChromeClientIOS::dispatchViewportPropertiesDidChange函数中会调用Frame::dictionaryForViewportArguments函数将Viewport参数封装成NSDictionary的形式传递给主线程来处理。

NSDictionary* Frame::dictionaryForViewportArguments(const ViewportArguments& arguments) const
{
return [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:NSFloatValue(arguments.initialScale), NSFloatValue(arguments.minimumScale),
NSFloatValue(arguments.maximumScale), NSFloatValue(arguments.userScalable),
NSFloatValue(arguments.width), NSFloatValue(arguments.height), nil]
forKeys:[NSArray arrayWithObjects:@"initial-scale", @"minimum-scale", @"maximum-scale", @"user-scalable", @"width", @"height", nil]];
}


     主线程在收到消息后会调用UIWebDocumentView的viewportConfigurationsDidChange函数来处理viewport参数。

     


     根据前面的讨论,viewport中最小缩放比例会根据document的大小进行调整,而document的大小在动态网页中经常会发生变化,这是viewport就需要重新计算。所以主线程处理Viewport参数的函数viewportConfigurationsDidChange除了被内核线程Document::updateViewportArguments抛转的消息触发外,还有可能在文档大小发生变化时被调用。

     


     另外,观察Safari中Viewport计算函数的调用栈,还有如下一些情况也可能会触发viewport的重新计算。

    
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息