zoukankan      html  css  js  c++  java
  • iOS Safari/WebKit对DeviceOrientationEvent的实现

    背景知识:

    Apple官方只发现一个文档:

    https://developer.apple.com/library/safari/#documentation/SafariDOMAdditions/Reference/DeviceOrientationEventClassRef/DeviceOrientationEvent/DeviceOrientationEvent.html

    连个例子都没有,还是自己实践吧。https://code.csdn.net/hursing/pagetest/blob/master/deviceorientationevent.html

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. <html>  
    2.     <head>  
    3.        <title>DeviceOrientationEvent</title>  
    4.        <meta charset="UTF-8" />  
    5.     </head>  
    6.     <body>  
    7.         <p>左右:<span id="alpha">0</span></p>  
    8.         <p>前后:<span id="beta">0</span></p>  
    9.         <p>扭转:<span id="gamma">0</span></p>  
    10.         <p>指北针指向:<span id="heading">0</span>度</p>  
    11.         <p>指北针精度:<span id="accuracy">0</span>度</p>  
    12.         <script type="text/javascript">  
    13.         function orientationHandler(event) {  
    14.             document.getElementById("alpha").innerHTML = event.alpha;  
    15.             document.getElementById("beta").innerHTML = event.beta;  
    16.             document.getElementById("gamma").innerHTML = event.gamma;  
    17.             document.getElementById("heading").innerHTML = event.webkitCompassHeading;  
    18.             document.getElementById("accuracy").innerHTML = event.webkitCompassAccuracy;  
    19.         }  
    20.           
    21.         if (window.DeviceOrientationEvent) {  
    22.           window.addEventListener("deviceorientation", orientationHandler, false);  
    23.         } else {  
    24.           document.body.innerHTML = "What user agent u r using???";  
    25.         }  
    26.         </script>  
    27.     </body>  
    28. </html>  

    用MobileSafari或UIWebView打开以上网页,可以看到5个数值的实时变化。

    (点击看大图)

    在handler的event参数中可以获得5个属性,其中两个:

    webkitCompassHeading:与正北方向的角度差值。正北为0度,正东为90度,正南为180度,正西为270度。因为0度是正北,所以叫指北针,不是指南针。

    webkitCompassAccuracy:指北针的精确度,表示偏差为正负多少度。一般是10。

    下面对另外三个属性的介绍引用一个转自http://www.tfan.org/wp-content/slides/deviceaccess.html的slides:

    [plain] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. deviceorientation 事件对象  
    2. 事件对象包含着每个轴相对于设备静止状态下发生变化的信息。  
    3.   
    4. 设备坐标系的概念:x 轴方向是从左往右,y 轴方向是从下往上,z 轴方向是从后往前。当设备静止放在水平桌面时,这三个值都是 0。  
    5.   
    6. 其中三个主要的属性:  
    7.   
    8. alpha: 在围绕 z 轴旋转时(即左右旋转时),y 轴的度数差。  
    9. beta: 在围绕 x 轴旋转时(即前后旋转时),z 轴的度数差。  
    10. gamma: 在围绕 y 轴旋转时(即扭转设备时),z 轴的度数差。  
    [plain] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. 围绕 z 轴旋转会引起 alpha 旋转角度发生变化。  
    2.   
    3. 当设备顶部指向地球正北方向时,alpha 角是 0 度,设备向左边旋转时增大,介于 0 - 360 度之间。  
    [plain] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. 围绕 x 轴旋转,也就是前后翻转(朝着用户或者远离用户)时,会引起 beta 旋转角度变化。  
    2.   
    3. 设备水平放置时,beta 角度是 0 度;向上翻逐步增加到 180 度;向下翻减少到 -180 度。  

    注:我试了一下ipad safari,beta取值是-90到90,但用Android chrome是-180到180。苹果的文档也表示beta是-90到90,参考https://developer.apple.com/library/safari/documentation/SafariDOMAdditions/Reference/DeviceOrientationEventClassRef/DeviceOrientationEvent/DeviceOrientationEvent.html#//apple_ref/javascript/instp/DeviceOrientationEvent/beta,和我引用的文档说的不同。估计这是苹果自己的实现,觉得这样用户体验好,因为翻转后苹果自动旋转屏幕,故不存在倒转的操作。不能用beta值来判断屏幕是绝对地向天空还是向地面,因为那是基于初始位置的差值,即只要一刷新就归0。

    [plain] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. 围绕 y 轴旋转,会引起 gamma 角度值的变化。  
    2.   
    3. 水平放置角度是 0,向右拧增加到 90 度;向左拧减少到 -90 度。  




    在《Mac Safari VS Mobile Safari开启的宏》中已确认,DeviceOrientationEvent是iOS Safari特有的feature。

    iOS Safari开源码的Document.h中,与DeviceOrientationEvent有关的是比Mac开源码多了这10行:

    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. #if ENABLE(DEVICE_ORIENTATION)  
    2.     DeviceMotionController* deviceMotionController() const;  
    3.     DeviceOrientationController* deviceOrientationController() const;  
    4. #endif  
    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. #if ENABLE(DEVICE_ORIENTATION)  
    2.     OwnPtr<DeviceMotionClient> m_deviceMotionClient;  
    3.     OwnPtr<DeviceMotionController> m_deviceMotionController;  
    4.     OwnPtr<DeviceOrientationClient> m_deviceOrientationClient;  
    5.     OwnPtr<DeviceOrientationController> m_deviceOrientationController;  
    6. #endif  

    其中DeviceMotionXXX是重力感应相关的,既有联系又有区别,请查看《iOS Safari/WebKit对DeviceMotionEvent的实现》。

    还有一个类叫DeviceOrientationClientMock,Mock是仿制品的意思。这个类只用在自动化测试,模拟设备的转动。

    下图是相关类之间的关系概貌:

    其中CMMotionManager是系统SDK,功能和使用方法请参见:

    http://developer.apple.com/library/ios/#documentation/CoreMotion/Reference/CMMotionManager_Class/Reference/Reference.html

    DeviceOrientationClient是个抽象类,在iOS由子类DeviceOrientationClientIOS(这是6.0的名字,在5.0叫做DeviceOrientationClientIPhone)来实现,直接与MotionManager交互。

    与CMMotionManager的名字类似的CoreMotionManager是个C++类,在关系链中主要充当编程语言adapter的角色,即对外是C++接口,内部实现会使用Objective-C

    xcode文档里没看到必须在主线程操作CMMotionManager的字眼,但CoreMotionManager都是在主线程操作(包括创建、销毁、调用函数)CMMotionmanager的,收到回调后,通过GCD或performSelector在MainThread和WebThread间传递消息。

    当执行JavaScript遇到addEventListener时,浏览器堆栈为:

    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. Thread 5 WebThread, Queue : (null)  
    2. #0  0x389740f8 in -[CoreMotionManager checkClientStatus] ()  
    3. #1  0x38973fa0 in -[CoreMotionManager addOrientationClient:] ()  
    4. #2  0x389a9b04 in WebCore::DeviceOrientationClientIOS::startUpdating() ()  
    5. #3  0x389aa000 in WebCore::DeviceOrientationController::addListener(WebCore::DOMWindow*) ()  
    6. #4  0x3873df36 in WebCore::DOMWindow::addEventListener(WTF::AtomicString const&, WTF::PassRefPtr<WebCore::EventListener>, bool) ()  
    7. #5  0x3873dc26 in WebCore::JSDOMWindow::addEventListener(JSC::ExecState*) ()  
    8. #6  0x3873da78 in WebCore::jsDOMWindowPrototypeFunctionAddEventListener(JSC::ExecState*) ()  

    第0层函数如果检测到自身不在主线程执行,会使用performSelectorOnMainThread本Selector到主线程再继续,主要操作是创建一个repeat的NSTimer不断查询CMMotionManager的相关属性,然后通过

    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. void orientationChanged(double, double, double, double, double);  

    函数,把文章开头提到的5个参数值封装成类WebCore::DeviceOrientation,

    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. class DeviceOrientation : public RefCounted<DeviceOrientation> {  
    2. public:  
    3.     static PassRefPtr<DeviceOrientation> create();  
    4.     static PassRefPtr<DeviceOrientation> create(bool canProvideAlpha, double alpha, bool canProvideBeta, double beta, bool canProvideGamma, double gamma, bool canProvideCompassHeading, double compassHeading, bool canProvideCompassAccuracy, double compassAccuracy);  
    5.   
    6.     double alpha() const;  
    7.     double beta() const;  
    8.     double gamma() const;  
    9.     bool absolute() const;  
    10.     bool canProvideAlpha() const;  
    11.     bool canProvideBeta() const;  
    12.     bool canProvideGamma() const;  
    13.     bool canProvideAbsolute() const;  
    14.   
    15.     double compassHeading() const;  
    16.     double compassAccuracy() const;  
    17.     bool canProvideCompassHeading() const;  
    18.     bool canProvideCompassAccuracy() const;  
    19.   
    20. private:  
    21.     DeviceOrientation();  
    22.     DeviceOrientation(bool canProvideAlpha, double alpha, bool canProvideBeta, double beta, bool canProvideGamma, double gamma, bool canProvideCompassHeading, double compassHeading, bool canProvideCompassAccuracy, double compassAccuracy);  
    23.   
    24.     bool m_canProvideAlpha;  
    25.     bool m_canProvideBeta;  
    26.     bool m_canProvideGamma;  
    27.     double m_alpha;  
    28.     double m_beta;  
    29.     double m_gamma;  
    30.   
    31.     bool m_canProvideCompassHeading;  
    32.     bool m_canProvideCompassAccuracy;  
    33.     double m_compassHeading;  
    34.     double m_compassAccuracy;  
    35. };  

    最后通过WebCore::EventTarget的标准流程传递到JavaScript的函数。

    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. Thread 5 WebThread, Queue : (null)  
    2. #0  0x387bec96 in WebCore::EventTarget::dispatchEvent(WTF::PassRefPtr<WebCore::Event>) ()  
    3. #1  0x389aa1d4 in WebCore::DeviceOrientationController::didChangeDeviceOrientation(WebCore::DeviceOrientation*) ()  
    4. #2  0x389a9b4c in WebCore::DeviceOrientationClientIOS::orientationChanged(double, double, double, double, double) ()  
    5. #3  0x38974b0a in __48-[CoreMotionManager sendMotionData:withHeading:]_block_invoke_0 ()  


    另外两个值得留意的地方是:

    当Document需要suspend或resume时,会同时挂起或恢复 orientation或motion的监听。

    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. void Document::suspendActiveDOMObjects(ActiveDOMObject::ReasonForSuspension why)  
    2. {  
    3.     ScriptExecutionContext::suspendActiveDOMObjects(why);  
    4.   
    5. #if ENABLE(DEVICE_ORIENTATION)  
    6.     if (m_deviceMotionController)  
    7.         m_deviceMotionController->suspendUpdates();  
    8.     if (m_deviceOrientationController)  
    9.         m_deviceOrientationController->suspendUpdates();  
    10. #endif  
    11.   
    12.     ...  
    13. }  
    14.   
    15. void Document::resumeActiveDOMObjects()  
    16. {  
    17.     ScriptExecutionContext::resumeActiveDOMObjects();  
    18.       
    19. #if ENABLE(DEVICE_ORIENTATION)  
    20.     if (m_deviceMotionController)  
    21.         m_deviceMotionController->resumeUpdates();  
    22.     if (m_deviceOrientationController)  
    23.         m_deviceOrientationController->resumeUpdates();  
    24. #endif  
    25.   
    26.     ...  
    27. }  

    当网页正在监听orientation或motion时,这个Page不能Cache

    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. bool PageCache::canCache(Page* page)  
    2. {  
    3.     ...  
    4.       
    5.     return canCachePageContainingThisFrame(page->mainFrame())  
    6.         && page->backForward()->isActive()  
    7.         && page->settings()->usesPageCache()  
    8. #if ENABLE(DEVICE_ORIENTATION)  
    9.         && !(page->mainFrame()->document()->deviceMotionController() && page->mainFrame()->document()->deviceMotionController()->isActive())  
    10.         && !(page->mainFrame()->document()->deviceOrientationController() && page->mainFrame()->document()->deviceOrientationController()->isActive())  
    11. #endif  
    12.         && loadType != FrameLoadTypeReload  
    13.         && loadType != FrameLoadTypeReloadFromOrigin  
    14.         && loadType != FrameLoadTypeSame;  
    15. }  

    iOS Safari/WebKit对DeviceMotionEvent的实现》还会对本文有补充,请点击继续查看。

  • 相关阅读:
    创建可按比例调整的布局的 Windows 窗体
    Visual C# 2010 实现资源管理器
    Visual C# 2010 实现菜单项和状态栏
    使用异步事件在后台进行计算并报告进度
    A Byte of Python(简明Python教程) for Python 3.0 下载
    面向对象思想
    封装变化(二)
    好玩,看你的博客价值几何?
    基于消息与.Net Remoting的分布式处理架构
    设计之道
  • 原文地址:https://www.cnblogs.com/guandekuan/p/6382566.html
Copyright © 2011-2022 走看看