12

前言

随着小程序api开放的功能日渐增多,小程序可以做到的功能和展现形式也越来越多,其中横屏的展现形式就是其中的一种,而实现横屏的方案也有多种,但是每种方案都有一定的缺陷,恰巧最近也在横屏方案上踩了不少坑,接下来就来和大家分享一下小程序的不同横屏方案的优劣(踩坑心得)

组件自带横屏方法

小程序中的媒体组件一般都会提供全屏的方法,而且全屏方法中会提供一个direction的全屏参数,可以通过这全屏参数将小程序旋转90度横屏展示,这是小程序中最简单的横屏方法。

这个方法优点在于调用的组件全屏方法做的横屏,不需要考虑头部导航栏的胶囊按钮的显示问题,媒体组件在做全屏显示时,胶囊按钮会自动隐藏不见,同时大多数媒体组件都已经支持了同层渲染,可以通过z-index调整组件层级,因此用此法在横屏布局时可以在媒体组件上覆盖其他组件布局

缺点:这个方法的缺点也很明显,就是只能使用媒体相关组件才能有横屏的全屏,场景很单一,如果不是媒体组件或者不想全屏,那么对不起,此法不可用

计算加速度判断横屏

这个方法是之前有人发在微信社区里的一个方法,通过小程序提供的wx.onAccelerometerChange方法监听设备在x、y、z轴上的加速度,通过返回的重力加速度计算设备的旋转角度,下面贴出代码给大家参考:

// 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:竖屏');
      }
    }
  });

更多的实现细节可以前往社区帖子查看,这个方法个人实际尝试过,确实可以判断设备处在横屏还是竖屏

优点:这个方法的优点在于可以监听到设备的横竖屏情况,然后根据项目需要展示不同横竖屏布局,自由度很高

缺点:这个方法的缺点在于在页面内容横屏时无法使头部导航栏的胶囊按钮横屏展示,因为头部导航栏的胶囊按钮始终是固定屏幕右上角不动的,这样会使得横屏效果有点奇怪,横屏是导航栏固定在左边是竖屏时的布局,除非横屏的布局是媒体组件的全屏,这样就能遮住胶囊按钮,自己自定义一个头部导航,这样就不会显得布局奇怪

pageOrientation

pageOrientation是小程序提供的配置属性,可以设置当前页面是横屏展示、竖屏展示、或者自动旋转,同时提供一个onResize的监听方法,当屏幕发生旋转时会触发onResize的回调,并且会在回调中返回当前是横屏还是竖屏以及相应区域的大小。

优点:配置即可,手机旋转触发监听事件,返回屏幕旋转后的宽高和当前横竖屏情况,横屏时胶囊按钮会自动旋转,这样页面布局就不会有上一个方法所提及的怪异布局

缺点:

  • 由于是配置形式,没办法主动调用,只能被动监听
  • 当用户将手机允许旋转关闭后,onResize的监听方法就不会触发
  • 由于小程序大多数时候使用的是rpx的单位,是一个基于响应区域大小的动态计算的单位,当屏幕发生旋转后,假设竖屏旋转到横屏,竖屏时响应区域为375 667,到了横屏时响应区域为667 375,此时onResize回调还没触发,此时的页面布局是竖屏的布局,但是由于响应区域发生变化375 667到667 375,rpx就会重新计算,此时页面的布局会突然变大,然后onResize回调触发后才会变为横屏布局,会有布局闪现错乱的情况
  • width:100%或者width:100vw这类的占满全部的样式也会出现问题,同样假设从竖屏切换到横屏,当竖屏切换的横屏时,此时width:100%的样式,计算100%的宽度还是竖屏时的375,不会立即切换到横屏时的667,因此切换到横屏时的一瞬间,能看到原来100%的样式,此时并没有占满100%,闪了一下375之后,然后才会占满,假设我们自定义的顶部标题栏,用了width:100%,那么我横屏时就会发现,标题栏宽度只有一半,然后才会占满

横屏时响应区域变化产生布局变化的解决办法

这个解决办法只能解决横屏时由于响应区域变化导致rpx重新的计算渲染的问题,既然rpx会在响应区域变化时重新计算渲染,那我们最直接粗暴简单的方法就是不使用动态计算的单位px,这样就不会在横屏时重新渲染计算,但是这样也有一个问题,就是在不同屏幕的大小的手机下也没法动态计算了

那有没有其他的更好办法呢?vmax、vmin这两个单位应该平时用的不多,个人也很少用,如果不是这次写小程序横屏,也不会关注到它

vmin:取值是当前vw和vh中较小的值,vmax:取值是当前vw和vh中较大的值

在竖屏时100vmin=375px,到了横屏时100vmin=375px,这样就能保证在横竖屏切换的时候内容大小不变,在小程序中使用calc(vmin/7.5),假设大小为10px,那么css为calc(10vmin/7.5),如果你嫌这么写麻烦,同时使用的是vs code,那么可以创建一个全局User Snippets:

"rpx to vmin" : {
  "prefix": "tovmin",
  "body": ["calc($0vmin / 7.5)"],
  "description": "rpx to vmin"
 }

这样在写css时直接写tomin就会将calc($0vmin / 7.5)输出

官方snippets创建步骤

总结

小程序中的横屏方案总是有这样或者那样的问题,并没有完美的解决方案,有时还是小程序自身原因导致的,我们能做的只是寻找解决办法或者换个交互设计,这篇文章的目的就是希望对大家在做横屏方案时提供一点帮助,少踩坑,这是我的踩坑经历,提供给大家借鉴,同时也有我的一些解决办法,如果大家有更好的方案,可以一起学习。

如果有错误或不严谨的地方,欢迎批评指正,如果喜欢,欢迎点赞


william
2.7k 声望826 粉丝

love and share