背景
直播产品的直播页面分为两种布局,当手机设备横过来的时候需要切换为一种类似全屏的布局,所以需要在产品的各个组件中判断是否是横屏状态
思路
尝试使用window.screen相关API来监听设备屏幕的朝向,并使用redux和自定义hooks来全局应用朝向的样式和逻辑
实现
Orientation API
目前的W3C标准下,判断方向的api是screen orientation,兼容范围如下
可见,Safari on iOS完全不兼容该api,覆盖率仅有76.7%,所以必须要同时兼容更旧的window.orientation API
两个api都兼容的话即可满足绝大部分情况下的方向监听要求
自定义Hooks
首先实现一个监听用的自定义hooks
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
|
import {useEffect} from 'react' import {useDispatch, useSelector} from 'react-redux' import useOrientation from '@/hooks/useOrientation'
const supportOrientation = () => (typeof window.orientation === 'number' && typeof window.onorientationchange === 'object')
const useListenOrientation = () => {
const [orientation] = useOrientation()
const dispatch = useDispatch()
const checkScreenOrient = (e) => {
console.log('screen orient change:', e)
const newType = window.screen.orientation.type
if (newType) { const orient = newType.toLowerCase().indexOf('landscape') > -1 ? 'landscape' : 'portrait'
if (orient !== orientation) { setTimeout(() => { dispatch.app.setOrientation(orient) }, 100) } } }
const checkWindowOrientation = (e) => {
console.log('orient change:', e)
const windowOrient = window.orientation
let newOrient
if([90, -90].includes(windowOrient)) { newOrient = 'landscape' } else { newOrient = 'portrait' }
if(newOrient !== orientation) { setTimeout(() => { dispatch.app.setOrientation(newOrient) }, 100) } }
useEffect(() => { if (window.screen.orientation) { checkScreenOrient(window.screen.orientation) } else if(supportOrientation()) { checkWindowOrientation() }
}, [])
useEffect(() => { if (window.screen.orientation) { window.screen.orientation.onchange = checkScreenOrient
return () => { window.screen.orientation.onchange = () => {} } } if(supportOrientation()) { window.addEventListener('orientationchange', checkWindowOrientation)
return () => { window.removeEventListener('orientationchange', checkWindowOrientation) } } }, [orientation])
return [orientation] }
export default useListenOrientation
|
然后编写给所有需要知道当前的方向的React组件来获取当前方向的hooks
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
import {useSelector} from 'react-redux'
const useOrientation = () => {
const [orientation] = useSelector(state => [state.app.orientation])
const isLandscape = orientation === 'landscape'
return [orientation, isLandscape] }
export default useOrientation
|
测试页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import React, {useEffect, useState} from 'react' import useListenOrientation from '@/hooks/useListenOrientation' import s from './index.module.less'
const OrientationTest = (props) => {
const [orientation] = useListenOrientation()
return <div className={s.wrapper}> <div>redux orientation: {orientation}</div> <div>window orientation: {window.orientation}</div> <div>screen orientation: {window.screen.orientation?.type}</div> <div style={{fontSize: 12}}>{event}</div> </div> }
export default OrientationTest
|
效果:
使用的浏览器是Google Chrome 100
竖屏时:
横屏时:
使用的浏览器是Safari On iOS
竖屏时:
横屏时:
参考