vue项目中有一个全屏功能,我该怎么办?本文结合实际的vue项目介绍了浏览器原生的全屏API、 screenfull、useFullscreen的用法并结合源码分析了screenfull和useFullscreen的原理,欢迎阅读学习。
1.原生requestFullScreen等
原生的全屏API有如下几个:
(1)Element.requestFullscreen():用于发出异步请求使元素进入全屏模式;
(2)Document.exitFullscreen() :用于让当前文档退出全屏模式;
(3)fullscreen: 只读属性,报告文档当前是否以全屏模式显示内容;
(4)fullscreenElement: 当前文档中正在以全屏模式显示的节点,如果没有使用全屏模式,则返回null;
(5)allowfullscreen:是否允许激活全屏模式。
更多详细内容可以参照MDN。如下代码是笔者项目中用到的一段代码:
fullScreen() {
if (this.$refs['cells'].requestFullScreen) {
this.$refs['cells'].requestFullScreen()
} else if (this.$refs['cells'].mozRequestFullScreen) {
this.$refs['cells'].mozRequestFullScreen()
} else if (this.$refs['cells'].webkitRequestFullScreen) {
this.$refs['cells'].webkitRequestFullScreen()
}
},
复制代码
2.screenfull的使用
2.1 简介
screenfull对各种浏览器全屏的API进行封装,以便于我们可以方便的使页面或者元素切换到全屏状态。例如:
使页面切换到全屏:
import screenfull from 'screenfull';
document.getElementById('button').addEventListener('click', () => {
if (screenfull.isEnabled) {
screenfull.request();
} else {
// Ignore or do something else
}
});
复制代码
使元素切换到全屏:
import screenfull from 'screenfull';
const element = document.getElementById('target');
document.getElementById('button').addEventListener('click', () => {
if (screenfull.isEnabled) {
screenfull.request(element);
}
});
复制代码
screenfull有如下常用的API:
.request(element, options?) 使元素或者页面切换到全屏;
.exit()退出全屏;
.toggle(element, options?)在全屏和非全屏之间切换;
.on(event, function)监听全屏切换或者错误事件;
.off(event, function)移除之前注册的事件监听;
.isFullscreen 现在是否为全屏;
.isEnabled 浏览器是否允许全屏。
2.2 使用
element-plus-admin中使用了screenfull, 并封装为单独的组件:
<template>
<div class='hidden-xs-only px-2'>
<svg-icon v-if='!isFullscreen' class-name='cursor-pointer' icon-class='svg-fullscreen' @click='changeScreenfull' />
<svg-icon v-else class-name='cursor-pointer' icon-class='svg-exit-fullscreen' @click='changeScreenfull' />
<!-- 切换失效 -->
<!-- <svg-icon class-name='cursor-pointer' :icon-class='isFullscreen ? "svg-exit-fullscreen" : "svg-fullscreen"' @click='changeScreenfull' /> -->
</div>
</template>
<script lang='ts'>
import { defineComponent, ref, onMounted, onUnmounted } from 'vue'
import screenfull from 'screenfull'
import { ElNotification } from 'element-plus'
export default defineComponent({
name: 'Screenfull',
setup() {
const isFullscreen = ref(false)
const changeScreenfull = () => {
if (!screenfull.isEnabled) {
ElNotification({
message: '浏览器不支持全屏',
type: 'warning'
})
}else{
screenfull.toggle()
}
}
const change = () => {
if(screenfull.isEnabled) isFullscreen.value = screenfull.isFullscreen
}
onMounted(() => screenfull.isEnabled && screenfull.on('change', change))
onUnmounted(() => screenfull.isEnabled && screenfull.off('change', change))
return {
isFullscreen,
changeScreenfull
}
}
})
</script>
复制代码
2.3 screenfull原理
下面我们通过screenful的源码了解一下其原理。
2.3.1 methodMap
首先定义了一个由不同浏览器全屏相关的方法组成的数组:
const methodMap = [
[
'requestFullscreen',
'exitFullscreen',
'fullscreenElement',
'fullscreenEnabled',
'fullscreenchange',
'fullscreenerror',
],
// New WebKit
[
'webkitRequestFullscreen',
'webkitExitFullscreen',
'webkitFullscreenElement',
'webkitFullscreenEnabled',
'webkitfullscreenchange',
'webkitfullscreenerror',
],
// Old WebKit
[
'webkitRequestFullScreen',
'webkitCancelFullScreen',
'webkitCurrentFullScreenElement',
'webkitCancelFullScreen',
'webkitfullscreenchange',
'webkitfullscreenerror',
],
[
'mozRequestFullScreen',
'mozCancelFullScreen',
'mozFullScreenElement',
'mozFullScreenEnabled',
'mozfullscreenchange',
'mozfullscreenerror',
],
[
'msRequestFullscreen',
'msExitFullscreen',
'msFullscreenElement',
'msFullscreenEnabled',
'MSFullscreenChange',
'MSFullscreenError',
],
];
复制代码
2.3.2 nativeAPI
nativeAPI方法用于检查浏览器支持的全屏相关方法,代码详细分析见注释:
const nativeAPI = (() => {
// 无浏览器前缀的方法名数组
const unprefixedMethods = methodMap[0];
const returnValue = {};
// 遍历methodMap的每一个元素
for (const methodList of methodMap) {
// 是否可以退出全屏
const exitFullscreenMethod = methodList?.[1];
// 检查document上是否存在可以退出全屏的方法
if (exitFullscreenMethod in document) {
// 如果支持全屏,则把相关方法都注册到returnValue上
for (const [index, method] of methodList.entries()) {
returnValue[unprefixedMethods[index]] = method;
}
return returnValue;
}
}
return false;
})();
复制代码
2.3.3 eventNameMap
eventNameMap定义全屏状态切换和全屏发生错误的事件映射:
const eventNameMap = {
change: nativeAPI.fullscreenchange,
error: nativeAPI.fullscreenerror,
};
复制代码
2.3.4 request
screenfull对象上定义了一些全屏方法, 首先看一下request方法:
request(element = document.documentElement, options) {
return new Promise((resolve, reject) => {
// 全屏触发的方法
const onFullScreenEntered = () => {
// 取消注册进入全屏的事件
screenfull.off('change', onFullScreenEntered);
resolve();
};
// 注册进入全屏的事件
screenfull.on('change', onFullScreenEntered);
// 执行原生的进入全屏的方法,返回值为promise
const returnPromise = element[nativeAPI.requestFullscreen](options);
if (returnPromise instanceof Promise) {
returnPromise.then(onFullScreenEntered).catch(reject);
}
});
},
复制代码
request用于发起全屏请求,返回值为一个promise。
2.3.5 exit
exit方法用于退出全屏,具体逻辑和请求全屏类似:
exit() {
return new Promise((resolve, reject) => {
if (!screenfull.isFullscreen) {
resolve();
return;
}
// 退出全屏触发的方法
const onFullScreenExit = () => {
screenfull.off('change', onFullScreenExit);
resolve();
};
// 退出全屏触发的事件
screenfull.on('change', onFullScreenExit);
// 执行退出全屏的方法
const returnPromise = document[nativeAPI.exitFullscreen]();
if (returnPromise instanceof Promise) {
returnPromise.then(onFullScreenExit).catch(reject);
}
});
},
复制代码
2.3.6 事件相关的方法
onchange(callback) {
screenfull.on('change', callback);
},
onerror(callback) {
screenfull.on('error', callback);
},
on(event, callback) {
const eventName = eventNameMap[event];
if (eventName) {
document.addEventListener(eventName, callback, false);
}
},
off(event, callback) {
const eventName = eventNameMap[event];
if (eventName) {
document.removeEventListener(eventName, callback, false);
}
}
复制代码
on方法用于注册事件,off用于取消事件,onchange和onerror分别用于注册全
3.useFullscreen的使用
3.1 简介
useFullscreen是vueuse核心提供的全屏功能。官方文档给出了如下图所示的例子:
点击fullscreen按钮可以进入全屏模式,实现代码如下图所示:
<script setup lang="ts">
import { ref } from 'vue'
import { useFullscreen } from '@vueuse/core'
const el = ref(null)
const { toggle, isFullscreen } = useFullscreen(el)
</script>
<template>
<div class="text-center">
<div class="flex" p="y-4">
<video
ref="el"
class="m-auto rounded"
src="https://vjs.zencdn.net/v/oceans.mp4"
width="600"
controls
/>
</div>
<button @click="toggle">
Go Fullscreen
</button>
</div>
</template>
复制代码
引入了useFullscreen来控制el(即video元素)的全屏,使用到了toggle方法和isFullscreen这个响应式的变量。
3.2 使用
vue-element-plus-admin 中使用了useFullscreen:
<script setup lang="ts">
import { Icon } from '@/components/Icon'
import { useFullscreen } from '@vueuse/core'
import { propTypes } from '@/utils/propTypes'
import { useDesign } from '@/hooks/web/useDesign'
const { getPrefixCls } = useDesign()
const prefixCls = getPrefixCls('screenfull')
defineProps({
color: propTypes.string.def('')
})
const { toggle, isFullscreen } = useFullscreen()
const toggleFullscreen = () => {
toggle()
}
</script>
<template>
<div :class="prefixCls" @click="toggleFullscreen">
<Icon
:size="18"
:icon="isFullscreen ? 'zmdi:fullscreen-exit' : 'zmdi:fullscreen'"
:color="color"
/>
</div>
</template>
复制代码
引入了useFullscreen来控制页面的全屏,使用到了toggle方法和isFullscreen这个响应式的变量。
3.3 useFullscreen原理
和screenfull类似,首先定义了一个由不同浏览器全屏相关的方法组成的数组functionsMap:
type FunctionMap = [
'requestFullscreen',
'exitFullscreen',
'fullscreenElement',
'fullscreenEnabled',
'fullscreenchange',
'fullscreenerror',
]
const functionsMap: FunctionMap[] = [
[
'requestFullscreen',
'exitFullscreen',
'fullscreenElement',
'fullscreenEnabled',
'fullscreenchange',
'fullscreenerror',
],
// New WebKit
[
'webkitRequestFullscreen',
'webkitExitFullscreen',
'webkitFullscreenElement',
'webkitFullscreenEnabled',
'webkitfullscreenchange',
'webkitfullscreenerror',
],
// Old WebKit
[
'webkitRequestFullScreen',
'webkitCancelFullScreen',
'webkitCurrentFullScreenElement',
'webkitCancelFullScreen',
'webkitfullscreenchange',
'webkitfullscreenerror',
]
// 省略一大堆
] as any
复制代码
再看useFullscreen方法:
export function useFullscreen(
target?: MaybeElementRef,
options: UseFullscreenOptions = {},
) {
const { document = defaultDocument, autoExit = false } = options
// 获取目标元素
const targetRef = target || document?.querySelector('html')
// isFullscreen根据目标是否全屏是响应式的
const isFullscreen = ref(false)
// 是否支持不用响应式的,能支持就是能,不能就是不能
let isSupported = false
// 取第一组全屏相关的方法
let map: FunctionMap = functionsMap[0]
// document不存在肯定不支持
if (!document) {
isSupported = false
}
else {
// 检测functionsMap的所有元素(每一个元素都是一个数组,包含浏览器全屏相关的方法)
for (const m of functionsMap) {
// document 上存在退出全屏的方法
if (m[1] in document) {
// 哪一种浏览器支持则使用对应那组方法
map = m
// 支持后即可退出循环检查
isSupported = true
break
}
}
}
// 解构出要使用的方法(REQUEST:请求全屏方法 EXIT:退出全屏 ELEMENT:当前全屏元素 EVENT:全屏change事件)
const [REQUEST, EXIT, ELEMENT,, EVENT] = map
// 退出全屏方法
async function exit() {
if (!isSupported)
return
if (document?.[ELEMENT])
await document[EXIT]()
isFullscreen.value = false
}
// 进入全屏方法
async function enter() {
if (!isSupported)
return
await exit()
// 目标元素
const target = unrefElement(targetRef)
if (target) {
await target[REQUEST]()
isFullscreen.value = true
}
}
// 切换
async function toggle() {
if (isFullscreen.value)
await exit()
else
await enter()
}
if (document) {
// 事件监听
useEventListener(document, EVENT, () => {
isFullscreen.value = !!document?.[ELEMENT]
}, false)
}
if (autoExit) // 自动退出
tryOnScopeDispose(exit)
return {
isSupported,
isFullscreen,
enter,
exit,
toggle,
}
}
复制代码
useFullscreen是使用hook的形式对全屏API进行了封装,useFullscreen方法返回了isSupported和isFullscreen两个变量用于判断是否支持全屏和是否在全屏状态;enter、exit 和toggle方法用于进入、退出和切换全屏。
4.总结
本文介绍了浏览器原生的全屏API, 然后介绍了使用较多的screenfull, 最后介绍了vueuse 提供的useFullscreen。screenfull和useFullscreen在本质上都是对浏览器原生API的封装。在实际项目中该如何选择呢? 欢迎您留言讨论~