useFullscreen
作用
管理 DOM 全屏的 Hook。
原理
通过
screenfull
这个包来实现的。
源码
ts
import { useEffect, useState, useRef } from 'react'
import screenfull from 'screenfull'
import useLatest from '../useLatest'
import useMemoizedFn from '../useMemoizedFn'
import type { BasicTarget } from '../utils/domTarget'
import { getTargetElement } from '../utils/domTarget'
import { isBoolean } from '../utils'
export interface PageFullscreenOptions {
className?: string
zIndex?: number
}
export interface Options {
onExit?: () => void
onEnter?: () => void
pageFullscreen?: boolean | PageFullscreenOptions
}
const useFullscreen = (target: BasicTarget, options?: Options) => {
const { onExit, onEnter, pageFullscreen = false } = options || {}
// pageFullscreen 为 true 时,className 默认为 'ahooks-page-fullscreen',zIndex 默认为 999999
const { className = 'ahooks-page-fullscreen', zIndex = 999999 } =
isBoolean(pageFullscreen) || !pageFullscreen ? {} : pageFullscreen
// 保存 onExit 和 onEnter 的最新值
const onExitRef = useLatest(onExit)
const onEnterRef = useLatest(onEnter)
// The state of full screen may be changed by other scripts/components,
// so the initial value needs to be computed dynamically.
const [state, setState] = useState(getIsFullscreen)
// ref 保存是否全屏的状态值
const stateRef = useRef(getIsFullscreen())
// 判断是否全屏
function getIsFullscreen() {
return (
screenfull.isEnabled &&
!!screenfull.element &&
screenfull.element === getTargetElement(target)
)
}
// 根据是否全屏,调用 onExit 或 onEnter
const invokeCallback = (fullscreen: boolean) => {
if (fullscreen) {
onEnterRef.current?.()
} else {
onExitRef.current?.()
}
}
// 更新是否全屏的状态值
const updateFullscreenState = (fullscreen: boolean) => {
// Prevent repeated calls when the state is not changed.
if (stateRef.current !== fullscreen) {
invokeCallback(fullscreen)
setState(fullscreen)
stateRef.current = fullscreen
}
}
// 监听全屏状态的变化,更新是否全屏的状态值
const onScreenfullChange = () => {
const fullscreen = getIsFullscreen()
updateFullscreenState(fullscreen)
}
// 切换页面全屏
const togglePageFullscreen = (fullscreen: boolean) => {
const el = getTargetElement(target)
if (!el) {
return
}
let styleElem = document.getElementById(className)
if (fullscreen) {
el.classList.add(className)
if (!styleElem) {
styleElem = document.createElement('style')
styleElem.setAttribute('id', className)
styleElem.textContent = `
.${className} {
position: fixed; left: 0; top: 0; right: 0; bottom: 0;
width: 100% !important; height: 100% !important;
z-index: ${zIndex};
}`
el.appendChild(styleElem)
}
} else {
el.classList.remove(className)
if (styleElem) {
styleElem.remove()
}
}
updateFullscreenState(fullscreen)
}
// 进入全屏
const enterFullscreen = () => {
const el = getTargetElement(target)
if (!el) {
return
}
if (pageFullscreen) {
togglePageFullscreen(true)
return
}
if (screenfull.isEnabled) {
try {
screenfull.request(el)
} catch (error) {
console.error(error)
}
}
}
// 退出全屏
const exitFullscreen = () => {
const el = getTargetElement(target)
if (!el) {
return
}
if (pageFullscreen) {
togglePageFullscreen(false)
return
}
if (screenfull.isEnabled && screenfull.element === el) {
screenfull.exit()
}
}
// 切换全屏
const toggleFullscreen = () => {
if (state) {
exitFullscreen()
} else {
enterFullscreen()
}
}
useEffect(() => {
// 如果不支持全屏,或者 pageFullscreen 为 true,直接返回
if (!screenfull.isEnabled || pageFullscreen) {
return
}
// 监听全屏状态的变化,更新是否全屏的状态值
screenfull.on('change', onScreenfullChange)
// 组件卸载时,移除监听
return () => {
screenfull.off('change', onScreenfullChange)
}
}, [])
return [
state,
{
enterFullscreen: useMemoizedFn(enterFullscreen),
exitFullscreen: useMemoizedFn(exitFullscreen),
toggleFullscreen: useMemoizedFn(toggleFullscreen),
isEnabled: screenfull.isEnabled,
},
] as const
}
export default useFullscreen