收藏
评论

基于加速度计判断横竖屏

也许有人会问,小程序中都是竖直app形态,要横竖屏判断有什么用?即使判断出了横屏状态,你能把小程序横过来?答案是不能的,但是判断当前设备处于横屏或者竖屏状态来实现一些友好的用户体验交互方式的需求确实存在。例如手机横屏,让视频播放自动全屏,手机竖屏,让视频切换回来小屏。


然而,截止至目前,小程序官方的API中并没有提供这样的横竖屏判断的方法。那么我们只能自己想办法实现这样的判断。小程序的设备API中提供了加速度计的监听方法,使用方法如下:

wx.onAccelerometerChange(function(res) {

  console.log(res.x)

  console.log(res.y)

  console.log(res.z)

})


加速度计的三轴

以下是一般移动设备的加速度计三轴坐标系示例图:



以手机竖直面向用户为例,加速计的三轴坐标系统的X、Y、Z轴定义如下:

  • 沿着手机屏幕顶部向上是Y轴正方向,向下是Y轴负方向;

  • 当手机顶部朝上时,沿着手机屏幕向右是X轴正方向,向左是X轴负方向;

  • 正对手机时,垂直屏幕向外是Z轴正方向,垂直屏幕向里是Z轴负方向;

当手机处于静止状态时,手机此时只受一个重力加速度(1g=9.8m/s²)的作用,加速度计返回的res.x、res.y、res.z的值就是设备的三轴受到的加速度的值,取值范围从[-1g,1g]。设备以不同方式放置时,x/y/z的值如下:



计算姿态角

在stackoverflow上找到了根据加速度计三轴的值计算姿态角公式(https://stackoverflow.com/questions/3755059/3d-accelerometer-calculate-the-orientation),经过结合设备的三轴坐标方向对公式进行调整,最终得出了公式:

Pitch = atan2(Y, Z) * 180/M_PI;Roll = atan2(-X, sqrt(Y*Y + Z*Z)) * 180/M_PI;

Roll = atan2(-X, sqrt(Y*Y + Z*Z)) * 180/M_PI;


  • Roll(绕Y轴旋转的角度)

当设备绕着自身Y轴旋转时(表示手机左侧或右侧翘起的角度),该角度值将会发生变化,取值范围是-90到90度。

  • Pitch(绕X轴旋转的角度)

当手机绕着自身的Y轴旋转(表示手机顶部或尾部翘起的角度),该角度会发生变化,值的范围是-180到180度。




接下来就是根据自己对横竖屏角度的观测,再结合微信小程序中,视频全屏只能以手机向左旋转方式全屏的特性,只对用户左侧横屏判断为横屏状态,实现代码片段如下:

  // 0为竖屏,1为横屏
  let lastState = 0;
  let lastTime = Date.now();
 
  wx.startAccelerometer();
 
  wx.onAccelerometerChange((res) => {
    const now = Date.now();
     
    // 500ms检测一次
    if (now - lastTime < 500) {
      return;
    }
    lastTime = now;
 
    let nowState;
 
    // 57.3 = 180 / Math.PI
    const Roll = Math.atan2(-res.x, Math.sqrt(res.y * res.y + res.z * res.z)) * 57.3;
    const Pitch = Math.atan2(res.y, res.z) * 57.3;
 
    // console.log('Roll: ' + Roll, 'Pitch: ' + Pitch)
 
    // 横屏状态
    if (Roll > 50) {
      if ((Pitch > -180 && Pitch < -60) || (Pitch > 130)) {
        nowState = 1;
      } else {
        nowState = lastState;
      }
 
    } else if ((Roll > 0 && Roll < 30) || (Roll < 0 && Roll > -30)) {
      let absPitch = Math.abs(Pitch);
 
      // 如果手机平躺,保持原状态不变,40容错率
      if ((absPitch > 140 || absPitch < 40)) {
        nowState = lastState;
      } else if (Pitch < 0) { /*收集竖向正立的情况*/
        nowState = 0;
      } else {
        nowState = lastState;
      }
    }
    else {
      nowState = lastState;
    }
 
    // 状态变化时,触发
    if (nowState !== lastState) {
      lastState = nowState;
      if (nowState === 1) {
        console.log('change:横屏');
      } else {
        console.log('change:竖屏');
      }
    }
  });


然后就可以在横竖屏切换的状态下,去切换视频的横竖屏了

if (state === 1) {
  video.requestFullScreen();
} else {
  video.exitFullScreen();
}


其他

另外,在这里发现小程序的一个小bug,就是当进入一个页面,马上就调用requestFullScreen()方法去拉起视频全屏时,会破坏整个页面的布局,并且再调用全屏方法时,视频就无法再全屏了,像这样:



所以为了防止用户直接以横屏的状态进入一个视频播放页,而我们的横屏判断检测生效立即触发全屏引发bug,我将监听横竖屏的事件通过setTimeout(listener, 3000)延迟3s监听,这样横屏才不会触发bug。


最后

文中的很多知识点很多都是从网络文章学来,可能存在错误的理解,如有错误,欢迎各位指正。

最后再打个广告,欢迎喜欢看游戏直播的小伙伴来试用我们的《TG电竞》直播小程序,这里聚合各大平台的知名主播,总有一款适合你哦。






收藏

10 个评论

  • Mn
    Mn
    2019-05-17

    非常感谢!!!昨晚领导刚提的需求,这篇文章太有帮助了!!

    2019-05-17
    赞同
    回复
  • 2019-03-12

    先占个位置

    2019-03-12
    赞同
    回复
  • 少波
    少波
    2018-11-29
    有些时候检查不出来呢,已经横屏了就是不打印log信息。。
    2018-11-29
    赞同
    回复 1
    • 少波
      少波
      2018-11-29

      手机几乎垂直的时候可以响应,但是如果倾斜接近放到桌子上就没发响应了

      2018-11-29
      回复
  • 拓荒
    拓荒
    2018-10-10

    第二次进入这个页面onAccelerometerChange  里面的setData不执行了,其他的都执行


    2018-10-10
    赞同
    回复 1
    • 志安_Chen
      志安_Chen
      2018-10-10

      离开页面的时候尝试用一下wx.stopAccelerometer()

      2018-10-10
      回复
  • 2018-08-13

    赞一个、

    2018-08-13
    赞同
    回复
  • 凶得批爆
    凶得批爆
    2018-08-08

    6666

    2018-08-08
    赞同
    回复
  • 我是你的石头城
    我是你的石头城
    2018-05-25

    谢谢

    2018-05-25
    赞同
    回复
  • 我的目标:星辰大海!
    我的目标:星辰大海!
    2018-03-08

    video怎么做的直播,我用的是live-player横屏做不到全屏,能指点一下吗,谢谢

    2018-03-08
    赞同
    回复 1
  • 牲产队-朝酒晚舞
    牲产队-朝酒晚舞
    2018-03-01

    66666666666,收藏下,万一以后有这样的需求 应该有用。

    2018-03-01
    赞同
    回复
  • 杨盛盛
    杨盛盛
    2018-01-30

    我也遇到了进入页面直接横屏就会导致页面布局紊乱的问题,如果设置延时的话,用户体验会不太好。

    这个BUG微信官方会处理吗

    2018-01-30
    赞同
    回复
登录 后发表内容